Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/sqlalchemy/sql/compiler.py: 29%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

2967 statements  

1# sql/compiler.py 

2# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors 

3# <see AUTHORS file> 

4# 

5# This module is part of SQLAlchemy and is released under 

6# the MIT License: https://www.opensource.org/licenses/mit-license.php 

7# mypy: allow-untyped-defs, allow-untyped-calls 

8 

9"""Base SQL and DDL compiler implementations. 

10 

11Classes provided include: 

12 

13:class:`.compiler.SQLCompiler` - renders SQL 

14strings 

15 

16:class:`.compiler.DDLCompiler` - renders DDL 

17(data definition language) strings 

18 

19:class:`.compiler.GenericTypeCompiler` - renders 

20type specification strings. 

21 

22To generate user-defined SQL strings, see 

23:doc:`/ext/compiler`. 

24 

25""" 

26from __future__ import annotations 

27 

28import collections 

29import collections.abc as collections_abc 

30import contextlib 

31from enum import IntEnum 

32import functools 

33import itertools 

34import operator 

35import re 

36from time import perf_counter 

37import typing 

38from typing import Any 

39from typing import Callable 

40from typing import cast 

41from typing import ClassVar 

42from typing import Dict 

43from typing import FrozenSet 

44from typing import Iterable 

45from typing import Iterator 

46from typing import List 

47from typing import Mapping 

48from typing import MutableMapping 

49from typing import NamedTuple 

50from typing import NoReturn 

51from typing import Optional 

52from typing import Pattern 

53from typing import Protocol 

54from typing import Sequence 

55from typing import Set 

56from typing import Tuple 

57from typing import Type 

58from typing import TYPE_CHECKING 

59from typing import TypedDict 

60from typing import Union 

61 

62from . import base 

63from . import coercions 

64from . import crud 

65from . import elements 

66from . import functions 

67from . import operators 

68from . import roles 

69from . import schema 

70from . import selectable 

71from . import sqltypes 

72from . import util as sql_util 

73from ._typing import is_column_element 

74from ._typing import is_dml 

75from .base import _de_clone 

76from .base import _from_objects 

77from .base import _NONE_NAME 

78from .base import _SentinelDefaultCharacterization 

79from .base import Executable 

80from .base import NO_ARG 

81from .elements import ClauseElement 

82from .elements import quoted_name 

83from .schema import Column 

84from .sqltypes import TupleType 

85from .type_api import TypeEngine 

86from .visitors import prefix_anon_map 

87from .visitors import Visitable 

88from .. import exc 

89from .. import util 

90from ..util import FastIntFlag 

91from ..util.typing import Literal 

92from ..util.typing import TupleAny 

93from ..util.typing import Unpack 

94 

95if typing.TYPE_CHECKING: 

96 from .annotation import _AnnotationDict 

97 from .base import _AmbiguousTableNameMap 

98 from .base import CompileState 

99 from .cache_key import CacheKey 

100 from .ddl import ExecutableDDLElement 

101 from .dml import Insert 

102 from .dml import UpdateBase 

103 from .dml import ValuesBase 

104 from .elements import _truncated_label 

105 from .elements import BindParameter 

106 from .elements import ColumnClause 

107 from .elements import ColumnElement 

108 from .elements import Label 

109 from .functions import Function 

110 from .schema import Table 

111 from .selectable import AliasedReturnsRows 

112 from .selectable import CompoundSelectState 

113 from .selectable import CTE 

114 from .selectable import FromClause 

115 from .selectable import NamedFromClause 

116 from .selectable import ReturnsRows 

117 from .selectable import Select 

118 from .selectable import SelectState 

119 from .type_api import _BindProcessorType 

120 from ..engine.cursor import CursorResultMetaData 

121 from ..engine.interfaces import _CoreSingleExecuteParams 

122 from ..engine.interfaces import _DBAPIAnyExecuteParams 

123 from ..engine.interfaces import _DBAPIMultiExecuteParams 

124 from ..engine.interfaces import _DBAPISingleExecuteParams 

125 from ..engine.interfaces import _ExecuteOptions 

126 from ..engine.interfaces import _GenericSetInputSizesType 

127 from ..engine.interfaces import _MutableCoreSingleExecuteParams 

128 from ..engine.interfaces import Dialect 

129 from ..engine.interfaces import SchemaTranslateMapType 

130 

131_FromHintsType = Dict["FromClause", str] 

132 

133RESERVED_WORDS = { 

134 "all", 

135 "analyse", 

136 "analyze", 

137 "and", 

138 "any", 

139 "array", 

140 "as", 

141 "asc", 

142 "asymmetric", 

143 "authorization", 

144 "between", 

145 "binary", 

146 "both", 

147 "case", 

148 "cast", 

149 "check", 

150 "collate", 

151 "column", 

152 "constraint", 

153 "create", 

154 "cross", 

155 "current_date", 

156 "current_role", 

157 "current_time", 

158 "current_timestamp", 

159 "current_user", 

160 "default", 

161 "deferrable", 

162 "desc", 

163 "distinct", 

164 "do", 

165 "else", 

166 "end", 

167 "except", 

168 "false", 

169 "for", 

170 "foreign", 

171 "freeze", 

172 "from", 

173 "full", 

174 "grant", 

175 "group", 

176 "having", 

177 "ilike", 

178 "in", 

179 "initially", 

180 "inner", 

181 "intersect", 

182 "into", 

183 "is", 

184 "isnull", 

185 "join", 

186 "leading", 

187 "left", 

188 "like", 

189 "limit", 

190 "localtime", 

191 "localtimestamp", 

192 "natural", 

193 "new", 

194 "not", 

195 "notnull", 

196 "null", 

197 "off", 

198 "offset", 

199 "old", 

200 "on", 

201 "only", 

202 "or", 

203 "order", 

204 "outer", 

205 "overlaps", 

206 "placing", 

207 "primary", 

208 "references", 

209 "right", 

210 "select", 

211 "session_user", 

212 "set", 

213 "similar", 

214 "some", 

215 "symmetric", 

216 "table", 

217 "then", 

218 "to", 

219 "trailing", 

220 "true", 

221 "union", 

222 "unique", 

223 "user", 

224 "using", 

225 "verbose", 

226 "when", 

227 "where", 

228} 

229 

230LEGAL_CHARACTERS = re.compile(r"^[A-Z0-9_$]+$", re.I) 

231LEGAL_CHARACTERS_PLUS_SPACE = re.compile(r"^[A-Z0-9_ $]+$", re.I) 

232ILLEGAL_INITIAL_CHARACTERS = {str(x) for x in range(0, 10)}.union(["$"]) 

233 

234FK_ON_DELETE = re.compile( 

235 r"^(?:RESTRICT|CASCADE|SET NULL|NO ACTION|SET DEFAULT)$", re.I 

236) 

237FK_ON_UPDATE = re.compile( 

238 r"^(?:RESTRICT|CASCADE|SET NULL|NO ACTION|SET DEFAULT)$", re.I 

239) 

240FK_INITIALLY = re.compile(r"^(?:DEFERRED|IMMEDIATE)$", re.I) 

241BIND_PARAMS = re.compile(r"(?<![:\w\$\x5c]):([\w\$]+)(?![:\w\$])", re.UNICODE) 

242BIND_PARAMS_ESC = re.compile(r"\x5c(:[\w\$]*)(?![:\w\$])", re.UNICODE) 

243 

244_pyformat_template = "%%(%(name)s)s" 

245BIND_TEMPLATES = { 

246 "pyformat": _pyformat_template, 

247 "qmark": "?", 

248 "format": "%%s", 

249 "numeric": ":[_POSITION]", 

250 "numeric_dollar": "$[_POSITION]", 

251 "named": ":%(name)s", 

252} 

253 

254 

255OPERATORS = { 

256 # binary 

257 operators.and_: " AND ", 

258 operators.or_: " OR ", 

259 operators.add: " + ", 

260 operators.mul: " * ", 

261 operators.sub: " - ", 

262 operators.mod: " % ", 

263 operators.neg: "-", 

264 operators.lt: " < ", 

265 operators.le: " <= ", 

266 operators.ne: " != ", 

267 operators.gt: " > ", 

268 operators.ge: " >= ", 

269 operators.eq: " = ", 

270 operators.is_distinct_from: " IS DISTINCT FROM ", 

271 operators.is_not_distinct_from: " IS NOT DISTINCT FROM ", 

272 operators.concat_op: " || ", 

273 operators.match_op: " MATCH ", 

274 operators.not_match_op: " NOT MATCH ", 

275 operators.in_op: " IN ", 

276 operators.not_in_op: " NOT IN ", 

277 operators.comma_op: ", ", 

278 operators.from_: " FROM ", 

279 operators.as_: " AS ", 

280 operators.is_: " IS ", 

281 operators.is_not: " IS NOT ", 

282 operators.collate: " COLLATE ", 

283 # unary 

284 operators.exists: "EXISTS ", 

285 operators.distinct_op: "DISTINCT ", 

286 operators.inv: "NOT ", 

287 operators.any_op: "ANY ", 

288 operators.all_op: "ALL ", 

289 # modifiers 

290 operators.desc_op: " DESC", 

291 operators.asc_op: " ASC", 

292 operators.nulls_first_op: " NULLS FIRST", 

293 operators.nulls_last_op: " NULLS LAST", 

294 # bitwise 

295 operators.bitwise_xor_op: " ^ ", 

296 operators.bitwise_or_op: " | ", 

297 operators.bitwise_and_op: " & ", 

298 operators.bitwise_not_op: "~", 

299 operators.bitwise_lshift_op: " << ", 

300 operators.bitwise_rshift_op: " >> ", 

301} 

302 

303FUNCTIONS: Dict[Type[Function[Any]], str] = { 

304 functions.coalesce: "coalesce", 

305 functions.current_date: "CURRENT_DATE", 

306 functions.current_time: "CURRENT_TIME", 

307 functions.current_timestamp: "CURRENT_TIMESTAMP", 

308 functions.current_user: "CURRENT_USER", 

309 functions.localtime: "LOCALTIME", 

310 functions.localtimestamp: "LOCALTIMESTAMP", 

311 functions.random: "random", 

312 functions.sysdate: "sysdate", 

313 functions.session_user: "SESSION_USER", 

314 functions.user: "USER", 

315 functions.cube: "CUBE", 

316 functions.rollup: "ROLLUP", 

317 functions.grouping_sets: "GROUPING SETS", 

318} 

319 

320 

321EXTRACT_MAP = { 

322 "month": "month", 

323 "day": "day", 

324 "year": "year", 

325 "second": "second", 

326 "hour": "hour", 

327 "doy": "doy", 

328 "minute": "minute", 

329 "quarter": "quarter", 

330 "dow": "dow", 

331 "week": "week", 

332 "epoch": "epoch", 

333 "milliseconds": "milliseconds", 

334 "microseconds": "microseconds", 

335 "timezone_hour": "timezone_hour", 

336 "timezone_minute": "timezone_minute", 

337} 

338 

339COMPOUND_KEYWORDS = { 

340 selectable._CompoundSelectKeyword.UNION: "UNION", 

341 selectable._CompoundSelectKeyword.UNION_ALL: "UNION ALL", 

342 selectable._CompoundSelectKeyword.EXCEPT: "EXCEPT", 

343 selectable._CompoundSelectKeyword.EXCEPT_ALL: "EXCEPT ALL", 

344 selectable._CompoundSelectKeyword.INTERSECT: "INTERSECT", 

345 selectable._CompoundSelectKeyword.INTERSECT_ALL: "INTERSECT ALL", 

346} 

347 

348 

349class ResultColumnsEntry(NamedTuple): 

350 """Tracks a column expression that is expected to be represented 

351 in the result rows for this statement. 

352 

353 This normally refers to the columns clause of a SELECT statement 

354 but may also refer to a RETURNING clause, as well as for dialect-specific 

355 emulations. 

356 

357 """ 

358 

359 keyname: str 

360 """string name that's expected in cursor.description""" 

361 

362 name: str 

363 """column name, may be labeled""" 

364 

365 objects: Tuple[Any, ...] 

366 """sequence of objects that should be able to locate this column 

367 in a RowMapping. This is typically string names and aliases 

368 as well as Column objects. 

369 

370 """ 

371 

372 type: TypeEngine[Any] 

373 """Datatype to be associated with this column. This is where 

374 the "result processing" logic directly links the compiled statement 

375 to the rows that come back from the cursor. 

376 

377 """ 

378 

379 

380class _ResultMapAppender(Protocol): 

381 def __call__( 

382 self, 

383 keyname: str, 

384 name: str, 

385 objects: Sequence[Any], 

386 type_: TypeEngine[Any], 

387 ) -> None: ... 

388 

389 

390# integer indexes into ResultColumnsEntry used by cursor.py. 

391# some profiling showed integer access faster than named tuple 

392RM_RENDERED_NAME: Literal[0] = 0 

393RM_NAME: Literal[1] = 1 

394RM_OBJECTS: Literal[2] = 2 

395RM_TYPE: Literal[3] = 3 

396 

397 

398class _BaseCompilerStackEntry(TypedDict): 

399 asfrom_froms: Set[FromClause] 

400 correlate_froms: Set[FromClause] 

401 selectable: ReturnsRows 

402 

403 

404class _CompilerStackEntry(_BaseCompilerStackEntry, total=False): 

405 compile_state: CompileState 

406 need_result_map_for_nested: bool 

407 need_result_map_for_compound: bool 

408 select_0: ReturnsRows 

409 insert_from_select: Select[Unpack[TupleAny]] 

410 

411 

412class ExpandedState(NamedTuple): 

413 """represents state to use when producing "expanded" and 

414 "post compile" bound parameters for a statement. 

415 

416 "expanded" parameters are parameters that are generated at 

417 statement execution time to suit a number of parameters passed, the most 

418 prominent example being the individual elements inside of an IN expression. 

419 

420 "post compile" parameters are parameters where the SQL literal value 

421 will be rendered into the SQL statement at execution time, rather than 

422 being passed as separate parameters to the driver. 

423 

424 To create an :class:`.ExpandedState` instance, use the 

425 :meth:`.SQLCompiler.construct_expanded_state` method on any 

426 :class:`.SQLCompiler` instance. 

427 

428 """ 

429 

430 statement: str 

431 """String SQL statement with parameters fully expanded""" 

432 

433 parameters: _CoreSingleExecuteParams 

434 """Parameter dictionary with parameters fully expanded. 

435 

436 For a statement that uses named parameters, this dictionary will map 

437 exactly to the names in the statement. For a statement that uses 

438 positional parameters, the :attr:`.ExpandedState.positional_parameters` 

439 will yield a tuple with the positional parameter set. 

440 

441 """ 

442 

443 processors: Mapping[str, _BindProcessorType[Any]] 

444 """mapping of bound value processors""" 

445 

446 positiontup: Optional[Sequence[str]] 

447 """Sequence of string names indicating the order of positional 

448 parameters""" 

449 

450 parameter_expansion: Mapping[str, List[str]] 

451 """Mapping representing the intermediary link from original parameter 

452 name to list of "expanded" parameter names, for those parameters that 

453 were expanded.""" 

454 

455 @property 

456 def positional_parameters(self) -> Tuple[Any, ...]: 

457 """Tuple of positional parameters, for statements that were compiled 

458 using a positional paramstyle. 

459 

460 """ 

461 if self.positiontup is None: 

462 raise exc.InvalidRequestError( 

463 "statement does not use a positional paramstyle" 

464 ) 

465 return tuple(self.parameters[key] for key in self.positiontup) 

466 

467 @property 

468 def additional_parameters(self) -> _CoreSingleExecuteParams: 

469 """synonym for :attr:`.ExpandedState.parameters`.""" 

470 return self.parameters 

471 

472 

473class _InsertManyValues(NamedTuple): 

474 """represents state to use for executing an "insertmanyvalues" statement. 

475 

476 The primary consumers of this object are the 

477 :meth:`.SQLCompiler._deliver_insertmanyvalues_batches` and 

478 :meth:`.DefaultDialect._deliver_insertmanyvalues_batches` methods. 

479 

480 .. versionadded:: 2.0 

481 

482 """ 

483 

484 is_default_expr: bool 

485 """if True, the statement is of the form 

486 ``INSERT INTO TABLE DEFAULT VALUES``, and can't be rewritten as a "batch" 

487 

488 """ 

489 

490 single_values_expr: str 

491 """The rendered "values" clause of the INSERT statement. 

492 

493 This is typically the parenthesized section e.g. "(?, ?, ?)" or similar. 

494 The insertmanyvalues logic uses this string as a search and replace 

495 target. 

496 

497 """ 

498 

499 insert_crud_params: List[crud._CrudParamElementStr] 

500 """List of Column / bind names etc. used while rewriting the statement""" 

501 

502 num_positional_params_counted: int 

503 """the number of bound parameters in a single-row statement. 

504 

505 This count may be larger or smaller than the actual number of columns 

506 targeted in the INSERT, as it accommodates for SQL expressions 

507 in the values list that may have zero or more parameters embedded 

508 within them. 

509 

510 This count is part of what's used to organize rewritten parameter lists 

511 when batching. 

512 

513 """ 

514 

515 sort_by_parameter_order: bool = False 

516 """if the deterministic_returnined_order parameter were used on the 

517 insert. 

518 

519 All of the attributes following this will only be used if this is True. 

520 

521 """ 

522 

523 includes_upsert_behaviors: bool = False 

524 """if True, we have to accommodate for upsert behaviors. 

525 

526 This will in some cases downgrade "insertmanyvalues" that requests 

527 deterministic ordering. 

528 

529 """ 

530 

531 sentinel_columns: Optional[Sequence[Column[Any]]] = None 

532 """List of sentinel columns that were located. 

533 

534 This list is only here if the INSERT asked for 

535 sort_by_parameter_order=True, 

536 and dialect-appropriate sentinel columns were located. 

537 

538 .. versionadded:: 2.0.10 

539 

540 """ 

541 

542 num_sentinel_columns: int = 0 

543 """how many sentinel columns are in the above list, if any. 

544 

545 This is the same as 

546 ``len(sentinel_columns) if sentinel_columns is not None else 0`` 

547 

548 """ 

549 

550 sentinel_param_keys: Optional[Sequence[str]] = None 

551 """parameter str keys in each param dictionary / tuple 

552 that would link to the client side "sentinel" values for that row, which 

553 we can use to match up parameter sets to result rows. 

554 

555 This is only present if sentinel_columns is present and the INSERT 

556 statement actually refers to client side values for these sentinel 

557 columns. 

558 

559 .. versionadded:: 2.0.10 

560 

561 .. versionchanged:: 2.0.29 - the sequence is now string dictionary keys 

562 only, used against the "compiled parameteters" collection before 

563 the parameters were converted by bound parameter processors 

564 

565 """ 

566 

567 implicit_sentinel: bool = False 

568 """if True, we have exactly one sentinel column and it uses a server side 

569 value, currently has to generate an incrementing integer value. 

570 

571 The dialect in question would have asserted that it supports receiving 

572 these values back and sorting on that value as a means of guaranteeing 

573 correlation with the incoming parameter list. 

574 

575 .. versionadded:: 2.0.10 

576 

577 """ 

578 

579 embed_values_counter: bool = False 

580 """Whether to embed an incrementing integer counter in each parameter 

581 set within the VALUES clause as parameters are batched over. 

582 

583 This is only used for a specific INSERT..SELECT..VALUES..RETURNING syntax 

584 where a subquery is used to produce value tuples. Current support 

585 includes PostgreSQL, Microsoft SQL Server. 

586 

587 .. versionadded:: 2.0.10 

588 

589 """ 

590 

591 

592class _InsertManyValuesBatch(NamedTuple): 

593 """represents an individual batch SQL statement for insertmanyvalues. 

594 

595 This is passed through the 

596 :meth:`.SQLCompiler._deliver_insertmanyvalues_batches` and 

597 :meth:`.DefaultDialect._deliver_insertmanyvalues_batches` methods out 

598 to the :class:`.Connection` within the 

599 :meth:`.Connection._exec_insertmany_context` method. 

600 

601 .. versionadded:: 2.0.10 

602 

603 """ 

604 

605 replaced_statement: str 

606 replaced_parameters: _DBAPIAnyExecuteParams 

607 processed_setinputsizes: Optional[_GenericSetInputSizesType] 

608 batch: Sequence[_DBAPISingleExecuteParams] 

609 sentinel_values: Sequence[Tuple[Any, ...]] 

610 current_batch_size: int 

611 batchnum: int 

612 total_batches: int 

613 rows_sorted: bool 

614 is_downgraded: bool 

615 

616 

617class InsertmanyvaluesSentinelOpts(FastIntFlag): 

618 """bitflag enum indicating styles of PK defaults 

619 which can work as implicit sentinel columns 

620 

621 """ 

622 

623 NOT_SUPPORTED = 1 

624 AUTOINCREMENT = 2 

625 IDENTITY = 4 

626 SEQUENCE = 8 

627 

628 ANY_AUTOINCREMENT = AUTOINCREMENT | IDENTITY | SEQUENCE 

629 _SUPPORTED_OR_NOT = NOT_SUPPORTED | ANY_AUTOINCREMENT 

630 

631 USE_INSERT_FROM_SELECT = 16 

632 RENDER_SELECT_COL_CASTS = 64 

633 

634 

635class CompilerState(IntEnum): 

636 COMPILING = 0 

637 """statement is present, compilation phase in progress""" 

638 

639 STRING_APPLIED = 1 

640 """statement is present, string form of the statement has been applied. 

641 

642 Additional processors by subclasses may still be pending. 

643 

644 """ 

645 

646 NO_STATEMENT = 2 

647 """compiler does not have a statement to compile, is used 

648 for method access""" 

649 

650 

651class Linting(IntEnum): 

652 """represent preferences for the 'SQL linting' feature. 

653 

654 this feature currently includes support for flagging cartesian products 

655 in SQL statements. 

656 

657 """ 

658 

659 NO_LINTING = 0 

660 "Disable all linting." 

661 

662 COLLECT_CARTESIAN_PRODUCTS = 1 

663 """Collect data on FROMs and cartesian products and gather into 

664 'self.from_linter'""" 

665 

666 WARN_LINTING = 2 

667 "Emit warnings for linters that find problems" 

668 

669 FROM_LINTING = COLLECT_CARTESIAN_PRODUCTS | WARN_LINTING 

670 """Warn for cartesian products; combines COLLECT_CARTESIAN_PRODUCTS 

671 and WARN_LINTING""" 

672 

673 

674NO_LINTING, COLLECT_CARTESIAN_PRODUCTS, WARN_LINTING, FROM_LINTING = tuple( 

675 Linting 

676) 

677 

678 

679class FromLinter(collections.namedtuple("FromLinter", ["froms", "edges"])): 

680 """represents current state for the "cartesian product" detection 

681 feature.""" 

682 

683 def lint(self, start=None): 

684 froms = self.froms 

685 if not froms: 

686 return None, None 

687 

688 edges = set(self.edges) 

689 the_rest = set(froms) 

690 

691 if start is not None: 

692 start_with = start 

693 the_rest.remove(start_with) 

694 else: 

695 start_with = the_rest.pop() 

696 

697 stack = collections.deque([start_with]) 

698 

699 while stack and the_rest: 

700 node = stack.popleft() 

701 the_rest.discard(node) 

702 

703 # comparison of nodes in edges here is based on hash equality, as 

704 # there are "annotated" elements that match the non-annotated ones. 

705 # to remove the need for in-python hash() calls, use native 

706 # containment routines (e.g. "node in edge", "edge.index(node)") 

707 to_remove = {edge for edge in edges if node in edge} 

708 

709 # appendleft the node in each edge that is not 

710 # the one that matched. 

711 stack.extendleft(edge[not edge.index(node)] for edge in to_remove) 

712 edges.difference_update(to_remove) 

713 

714 # FROMS left over? boom 

715 if the_rest: 

716 return the_rest, start_with 

717 else: 

718 return None, None 

719 

720 def warn(self, stmt_type="SELECT"): 

721 the_rest, start_with = self.lint() 

722 

723 # FROMS left over? boom 

724 if the_rest: 

725 froms = the_rest 

726 if froms: 

727 template = ( 

728 "{stmt_type} statement has a cartesian product between " 

729 "FROM element(s) {froms} and " 

730 'FROM element "{start}". Apply join condition(s) ' 

731 "between each element to resolve." 

732 ) 

733 froms_str = ", ".join( 

734 f'"{self.froms[from_]}"' for from_ in froms 

735 ) 

736 message = template.format( 

737 stmt_type=stmt_type, 

738 froms=froms_str, 

739 start=self.froms[start_with], 

740 ) 

741 

742 util.warn(message) 

743 

744 

745class Compiled: 

746 """Represent a compiled SQL or DDL expression. 

747 

748 The ``__str__`` method of the ``Compiled`` object should produce 

749 the actual text of the statement. ``Compiled`` objects are 

750 specific to their underlying database dialect, and also may 

751 or may not be specific to the columns referenced within a 

752 particular set of bind parameters. In no case should the 

753 ``Compiled`` object be dependent on the actual values of those 

754 bind parameters, even though it may reference those values as 

755 defaults. 

756 """ 

757 

758 statement: Optional[ClauseElement] = None 

759 "The statement to compile." 

760 string: str = "" 

761 "The string representation of the ``statement``" 

762 

763 state: CompilerState 

764 """description of the compiler's state""" 

765 

766 is_sql = False 

767 is_ddl = False 

768 

769 _cached_metadata: Optional[CursorResultMetaData] = None 

770 

771 _result_columns: Optional[List[ResultColumnsEntry]] = None 

772 

773 schema_translate_map: Optional[SchemaTranslateMapType] = None 

774 

775 execution_options: _ExecuteOptions = util.EMPTY_DICT 

776 """ 

777 Execution options propagated from the statement. In some cases, 

778 sub-elements of the statement can modify these. 

779 """ 

780 

781 preparer: IdentifierPreparer 

782 

783 _annotations: _AnnotationDict = util.EMPTY_DICT 

784 

785 compile_state: Optional[CompileState] = None 

786 """Optional :class:`.CompileState` object that maintains additional 

787 state used by the compiler. 

788 

789 Major executable objects such as :class:`_expression.Insert`, 

790 :class:`_expression.Update`, :class:`_expression.Delete`, 

791 :class:`_expression.Select` will generate this 

792 state when compiled in order to calculate additional information about the 

793 object. For the top level object that is to be executed, the state can be 

794 stored here where it can also have applicability towards result set 

795 processing. 

796 

797 .. versionadded:: 1.4 

798 

799 """ 

800 

801 dml_compile_state: Optional[CompileState] = None 

802 """Optional :class:`.CompileState` assigned at the same point that 

803 .isinsert, .isupdate, or .isdelete is assigned. 

804 

805 This will normally be the same object as .compile_state, with the 

806 exception of cases like the :class:`.ORMFromStatementCompileState` 

807 object. 

808 

809 .. versionadded:: 1.4.40 

810 

811 """ 

812 

813 cache_key: Optional[CacheKey] = None 

814 """The :class:`.CacheKey` that was generated ahead of creating this 

815 :class:`.Compiled` object. 

816 

817 This is used for routines that need access to the original 

818 :class:`.CacheKey` instance generated when the :class:`.Compiled` 

819 instance was first cached, typically in order to reconcile 

820 the original list of :class:`.BindParameter` objects with a 

821 per-statement list that's generated on each call. 

822 

823 """ 

824 

825 _gen_time: float 

826 """Generation time of this :class:`.Compiled`, used for reporting 

827 cache stats.""" 

828 

829 def __init__( 

830 self, 

831 dialect: Dialect, 

832 statement: Optional[ClauseElement], 

833 schema_translate_map: Optional[SchemaTranslateMapType] = None, 

834 render_schema_translate: bool = False, 

835 compile_kwargs: Mapping[str, Any] = util.immutabledict(), 

836 ): 

837 """Construct a new :class:`.Compiled` object. 

838 

839 :param dialect: :class:`.Dialect` to compile against. 

840 

841 :param statement: :class:`_expression.ClauseElement` to be compiled. 

842 

843 :param schema_translate_map: dictionary of schema names to be 

844 translated when forming the resultant SQL 

845 

846 .. seealso:: 

847 

848 :ref:`schema_translating` 

849 

850 :param compile_kwargs: additional kwargs that will be 

851 passed to the initial call to :meth:`.Compiled.process`. 

852 

853 

854 """ 

855 self.dialect = dialect 

856 self.preparer = self.dialect.identifier_preparer 

857 if schema_translate_map: 

858 self.schema_translate_map = schema_translate_map 

859 self.preparer = self.preparer._with_schema_translate( 

860 schema_translate_map 

861 ) 

862 

863 if statement is not None: 

864 self.state = CompilerState.COMPILING 

865 self.statement = statement 

866 self.can_execute = statement.supports_execution 

867 self._annotations = statement._annotations 

868 if self.can_execute: 

869 if TYPE_CHECKING: 

870 assert isinstance(statement, Executable) 

871 self.execution_options = statement._execution_options 

872 self.string = self.process(self.statement, **compile_kwargs) 

873 

874 if render_schema_translate: 

875 self.string = self.preparer._render_schema_translates( 

876 self.string, schema_translate_map 

877 ) 

878 

879 self.state = CompilerState.STRING_APPLIED 

880 else: 

881 self.state = CompilerState.NO_STATEMENT 

882 

883 self._gen_time = perf_counter() 

884 

885 def __init_subclass__(cls) -> None: 

886 cls._init_compiler_cls() 

887 return super().__init_subclass__() 

888 

889 @classmethod 

890 def _init_compiler_cls(cls): 

891 pass 

892 

893 def _execute_on_connection( 

894 self, connection, distilled_params, execution_options 

895 ): 

896 if self.can_execute: 

897 return connection._execute_compiled( 

898 self, distilled_params, execution_options 

899 ) 

900 else: 

901 raise exc.ObjectNotExecutableError(self.statement) 

902 

903 def visit_unsupported_compilation(self, element, err, **kw): 

904 raise exc.UnsupportedCompilationError(self, type(element)) from err 

905 

906 @property 

907 def sql_compiler(self): 

908 """Return a Compiled that is capable of processing SQL expressions. 

909 

910 If this compiler is one, it would likely just return 'self'. 

911 

912 """ 

913 

914 raise NotImplementedError() 

915 

916 def process(self, obj: Visitable, **kwargs: Any) -> str: 

917 return obj._compiler_dispatch(self, **kwargs) 

918 

919 def __str__(self) -> str: 

920 """Return the string text of the generated SQL or DDL.""" 

921 

922 if self.state is CompilerState.STRING_APPLIED: 

923 return self.string 

924 else: 

925 return "" 

926 

927 def construct_params( 

928 self, 

929 params: Optional[_CoreSingleExecuteParams] = None, 

930 extracted_parameters: Optional[Sequence[BindParameter[Any]]] = None, 

931 escape_names: bool = True, 

932 ) -> Optional[_MutableCoreSingleExecuteParams]: 

933 """Return the bind params for this compiled object. 

934 

935 :param params: a dict of string/object pairs whose values will 

936 override bind values compiled in to the 

937 statement. 

938 """ 

939 

940 raise NotImplementedError() 

941 

942 @property 

943 def params(self): 

944 """Return the bind params for this compiled object.""" 

945 return self.construct_params() 

946 

947 

948class TypeCompiler(util.EnsureKWArg): 

949 """Produces DDL specification for TypeEngine objects.""" 

950 

951 ensure_kwarg = r"visit_\w+" 

952 

953 def __init__(self, dialect: Dialect): 

954 self.dialect = dialect 

955 

956 def process(self, type_: TypeEngine[Any], **kw: Any) -> str: 

957 if ( 

958 type_._variant_mapping 

959 and self.dialect.name in type_._variant_mapping 

960 ): 

961 type_ = type_._variant_mapping[self.dialect.name] 

962 return type_._compiler_dispatch(self, **kw) 

963 

964 def visit_unsupported_compilation( 

965 self, element: Any, err: Exception, **kw: Any 

966 ) -> NoReturn: 

967 raise exc.UnsupportedCompilationError(self, element) from err 

968 

969 

970# this was a Visitable, but to allow accurate detection of 

971# column elements this is actually a column element 

972class _CompileLabel( 

973 roles.BinaryElementRole[Any], elements.CompilerColumnElement 

974): 

975 """lightweight label object which acts as an expression.Label.""" 

976 

977 __visit_name__ = "label" 

978 __slots__ = "element", "name", "_alt_names" 

979 

980 def __init__(self, col, name, alt_names=()): 

981 self.element = col 

982 self.name = name 

983 self._alt_names = (col,) + alt_names 

984 

985 @property 

986 def proxy_set(self): 

987 return self.element.proxy_set 

988 

989 @property 

990 def type(self): 

991 return self.element.type 

992 

993 def self_group(self, **kw): 

994 return self 

995 

996 

997class ilike_case_insensitive( 

998 roles.BinaryElementRole[Any], elements.CompilerColumnElement 

999): 

1000 """produce a wrapping element for a case-insensitive portion of 

1001 an ILIKE construct. 

1002 

1003 The construct usually renders the ``lower()`` function, but on 

1004 PostgreSQL will pass silently with the assumption that "ILIKE" 

1005 is being used. 

1006 

1007 .. versionadded:: 2.0 

1008 

1009 """ 

1010 

1011 __visit_name__ = "ilike_case_insensitive_operand" 

1012 __slots__ = "element", "comparator" 

1013 

1014 def __init__(self, element): 

1015 self.element = element 

1016 self.comparator = element.comparator 

1017 

1018 @property 

1019 def proxy_set(self): 

1020 return self.element.proxy_set 

1021 

1022 @property 

1023 def type(self): 

1024 return self.element.type 

1025 

1026 def self_group(self, **kw): 

1027 return self 

1028 

1029 def _with_binary_element_type(self, type_): 

1030 return ilike_case_insensitive( 

1031 self.element._with_binary_element_type(type_) 

1032 ) 

1033 

1034 

1035class SQLCompiler(Compiled): 

1036 """Default implementation of :class:`.Compiled`. 

1037 

1038 Compiles :class:`_expression.ClauseElement` objects into SQL strings. 

1039 

1040 """ 

1041 

1042 extract_map = EXTRACT_MAP 

1043 

1044 bindname_escape_characters: ClassVar[Mapping[str, str]] = ( 

1045 util.immutabledict( 

1046 { 

1047 "%": "P", 

1048 "(": "A", 

1049 ")": "Z", 

1050 ":": "C", 

1051 ".": "_", 

1052 "[": "_", 

1053 "]": "_", 

1054 " ": "_", 

1055 } 

1056 ) 

1057 ) 

1058 """A mapping (e.g. dict or similar) containing a lookup of 

1059 characters keyed to replacement characters which will be applied to all 

1060 'bind names' used in SQL statements as a form of 'escaping'; the given 

1061 characters are replaced entirely with the 'replacement' character when 

1062 rendered in the SQL statement, and a similar translation is performed 

1063 on the incoming names used in parameter dictionaries passed to methods 

1064 like :meth:`_engine.Connection.execute`. 

1065 

1066 This allows bound parameter names used in :func:`_sql.bindparam` and 

1067 other constructs to have any arbitrary characters present without any 

1068 concern for characters that aren't allowed at all on the target database. 

1069 

1070 Third party dialects can establish their own dictionary here to replace the 

1071 default mapping, which will ensure that the particular characters in the 

1072 mapping will never appear in a bound parameter name. 

1073 

1074 The dictionary is evaluated at **class creation time**, so cannot be 

1075 modified at runtime; it must be present on the class when the class 

1076 is first declared. 

1077 

1078 Note that for dialects that have additional bound parameter rules such 

1079 as additional restrictions on leading characters, the 

1080 :meth:`_sql.SQLCompiler.bindparam_string` method may need to be augmented. 

1081 See the cx_Oracle compiler for an example of this. 

1082 

1083 .. versionadded:: 2.0.0rc1 

1084 

1085 """ 

1086 

1087 _bind_translate_re: ClassVar[Pattern[str]] 

1088 _bind_translate_chars: ClassVar[Mapping[str, str]] 

1089 

1090 is_sql = True 

1091 

1092 compound_keywords = COMPOUND_KEYWORDS 

1093 

1094 isdelete: bool = False 

1095 isinsert: bool = False 

1096 isupdate: bool = False 

1097 """class-level defaults which can be set at the instance 

1098 level to define if this Compiled instance represents 

1099 INSERT/UPDATE/DELETE 

1100 """ 

1101 

1102 postfetch: Optional[List[Column[Any]]] 

1103 """list of columns that can be post-fetched after INSERT or UPDATE to 

1104 receive server-updated values""" 

1105 

1106 insert_prefetch: Sequence[Column[Any]] = () 

1107 """list of columns for which default values should be evaluated before 

1108 an INSERT takes place""" 

1109 

1110 update_prefetch: Sequence[Column[Any]] = () 

1111 """list of columns for which onupdate default values should be evaluated 

1112 before an UPDATE takes place""" 

1113 

1114 implicit_returning: Optional[Sequence[ColumnElement[Any]]] = None 

1115 """list of "implicit" returning columns for a toplevel INSERT or UPDATE 

1116 statement, used to receive newly generated values of columns. 

1117 

1118 .. versionadded:: 2.0 ``implicit_returning`` replaces the previous 

1119 ``returning`` collection, which was not a generalized RETURNING 

1120 collection and instead was in fact specific to the "implicit returning" 

1121 feature. 

1122 

1123 """ 

1124 

1125 isplaintext: bool = False 

1126 

1127 binds: Dict[str, BindParameter[Any]] 

1128 """a dictionary of bind parameter keys to BindParameter instances.""" 

1129 

1130 bind_names: Dict[BindParameter[Any], str] 

1131 """a dictionary of BindParameter instances to "compiled" names 

1132 that are actually present in the generated SQL""" 

1133 

1134 stack: List[_CompilerStackEntry] 

1135 """major statements such as SELECT, INSERT, UPDATE, DELETE are 

1136 tracked in this stack using an entry format.""" 

1137 

1138 returning_precedes_values: bool = False 

1139 """set to True classwide to generate RETURNING 

1140 clauses before the VALUES or WHERE clause (i.e. MSSQL) 

1141 """ 

1142 

1143 render_table_with_column_in_update_from: bool = False 

1144 """set to True classwide to indicate the SET clause 

1145 in a multi-table UPDATE statement should qualify 

1146 columns with the table name (i.e. MySQL only) 

1147 """ 

1148 

1149 ansi_bind_rules: bool = False 

1150 """SQL 92 doesn't allow bind parameters to be used 

1151 in the columns clause of a SELECT, nor does it allow 

1152 ambiguous expressions like "? = ?". A compiler 

1153 subclass can set this flag to False if the target 

1154 driver/DB enforces this 

1155 """ 

1156 

1157 bindtemplate: str 

1158 """template to render bound parameters based on paramstyle.""" 

1159 

1160 compilation_bindtemplate: str 

1161 """template used by compiler to render parameters before positional 

1162 paramstyle application""" 

1163 

1164 _numeric_binds_identifier_char: str 

1165 """Character that's used to as the identifier of a numerical bind param. 

1166 For example if this char is set to ``$``, numerical binds will be rendered 

1167 in the form ``$1, $2, $3``. 

1168 """ 

1169 

1170 _result_columns: List[ResultColumnsEntry] 

1171 """relates label names in the final SQL to a tuple of local 

1172 column/label name, ColumnElement object (if any) and 

1173 TypeEngine. CursorResult uses this for type processing and 

1174 column targeting""" 

1175 

1176 _textual_ordered_columns: bool = False 

1177 """tell the result object that the column names as rendered are important, 

1178 but they are also "ordered" vs. what is in the compiled object here. 

1179 

1180 As of 1.4.42 this condition is only present when the statement is a 

1181 TextualSelect, e.g. text("....").columns(...), where it is required 

1182 that the columns are considered positionally and not by name. 

1183 

1184 """ 

1185 

1186 _ad_hoc_textual: bool = False 

1187 """tell the result that we encountered text() or '*' constructs in the 

1188 middle of the result columns, but we also have compiled columns, so 

1189 if the number of columns in cursor.description does not match how many 

1190 expressions we have, that means we can't rely on positional at all and 

1191 should match on name. 

1192 

1193 """ 

1194 

1195 _ordered_columns: bool = True 

1196 """ 

1197 if False, means we can't be sure the list of entries 

1198 in _result_columns is actually the rendered order. Usually 

1199 True unless using an unordered TextualSelect. 

1200 """ 

1201 

1202 _loose_column_name_matching: bool = False 

1203 """tell the result object that the SQL statement is textual, wants to match 

1204 up to Column objects, and may be using the ._tq_label in the SELECT rather 

1205 than the base name. 

1206 

1207 """ 

1208 

1209 _numeric_binds: bool = False 

1210 """ 

1211 True if paramstyle is "numeric". This paramstyle is trickier than 

1212 all the others. 

1213 

1214 """ 

1215 

1216 _render_postcompile: bool = False 

1217 """ 

1218 whether to render out POSTCOMPILE params during the compile phase. 

1219 

1220 This attribute is used only for end-user invocation of stmt.compile(); 

1221 it's never used for actual statement execution, where instead the 

1222 dialect internals access and render the internal postcompile structure 

1223 directly. 

1224 

1225 """ 

1226 

1227 _post_compile_expanded_state: Optional[ExpandedState] = None 

1228 """When render_postcompile is used, the ``ExpandedState`` used to create 

1229 the "expanded" SQL is assigned here, and then used by the ``.params`` 

1230 accessor and ``.construct_params()`` methods for their return values. 

1231 

1232 .. versionadded:: 2.0.0rc1 

1233 

1234 """ 

1235 

1236 _pre_expanded_string: Optional[str] = None 

1237 """Stores the original string SQL before 'post_compile' is applied, 

1238 for cases where 'post_compile' were used. 

1239 

1240 """ 

1241 

1242 _pre_expanded_positiontup: Optional[List[str]] = None 

1243 

1244 _insertmanyvalues: Optional[_InsertManyValues] = None 

1245 

1246 _insert_crud_params: Optional[crud._CrudParamSequence] = None 

1247 

1248 literal_execute_params: FrozenSet[BindParameter[Any]] = frozenset() 

1249 """bindparameter objects that are rendered as literal values at statement 

1250 execution time. 

1251 

1252 """ 

1253 

1254 post_compile_params: FrozenSet[BindParameter[Any]] = frozenset() 

1255 """bindparameter objects that are rendered as bound parameter placeholders 

1256 at statement execution time. 

1257 

1258 """ 

1259 

1260 escaped_bind_names: util.immutabledict[str, str] = util.EMPTY_DICT 

1261 """Late escaping of bound parameter names that has to be converted 

1262 to the original name when looking in the parameter dictionary. 

1263 

1264 """ 

1265 

1266 has_out_parameters = False 

1267 """if True, there are bindparam() objects that have the isoutparam 

1268 flag set.""" 

1269 

1270 postfetch_lastrowid = False 

1271 """if True, and this in insert, use cursor.lastrowid to populate 

1272 result.inserted_primary_key. """ 

1273 

1274 _cache_key_bind_match: Optional[ 

1275 Tuple[ 

1276 Dict[ 

1277 BindParameter[Any], 

1278 List[BindParameter[Any]], 

1279 ], 

1280 Dict[ 

1281 str, 

1282 BindParameter[Any], 

1283 ], 

1284 ] 

1285 ] = None 

1286 """a mapping that will relate the BindParameter object we compile 

1287 to those that are part of the extracted collection of parameters 

1288 in the cache key, if we were given a cache key. 

1289 

1290 """ 

1291 

1292 positiontup: Optional[List[str]] = None 

1293 """for a compiled construct that uses a positional paramstyle, will be 

1294 a sequence of strings, indicating the names of bound parameters in order. 

1295 

1296 This is used in order to render bound parameters in their correct order, 

1297 and is combined with the :attr:`_sql.Compiled.params` dictionary to 

1298 render parameters. 

1299 

1300 This sequence always contains the unescaped name of the parameters. 

1301 

1302 .. seealso:: 

1303 

1304 :ref:`faq_sql_expression_string` - includes a usage example for 

1305 debugging use cases. 

1306 

1307 """ 

1308 _values_bindparam: Optional[List[str]] = None 

1309 

1310 _visited_bindparam: Optional[List[str]] = None 

1311 

1312 inline: bool = False 

1313 

1314 ctes: Optional[MutableMapping[CTE, str]] 

1315 

1316 # Detect same CTE references - Dict[(level, name), cte] 

1317 # Level is required for supporting nesting 

1318 ctes_by_level_name: Dict[Tuple[int, str], CTE] 

1319 

1320 # To retrieve key/level in ctes_by_level_name - 

1321 # Dict[cte_reference, (level, cte_name, cte_opts)] 

1322 level_name_by_cte: Dict[CTE, Tuple[int, str, selectable._CTEOpts]] 

1323 

1324 ctes_recursive: bool 

1325 

1326 _post_compile_pattern = re.compile(r"__\[POSTCOMPILE_(\S+?)(~~.+?~~)?\]") 

1327 _pyformat_pattern = re.compile(r"%\(([^)]+?)\)s") 

1328 _positional_pattern = re.compile( 

1329 f"{_pyformat_pattern.pattern}|{_post_compile_pattern.pattern}" 

1330 ) 

1331 

1332 @classmethod 

1333 def _init_compiler_cls(cls): 

1334 cls._init_bind_translate() 

1335 

1336 @classmethod 

1337 def _init_bind_translate(cls): 

1338 reg = re.escape("".join(cls.bindname_escape_characters)) 

1339 cls._bind_translate_re = re.compile(f"[{reg}]") 

1340 cls._bind_translate_chars = cls.bindname_escape_characters 

1341 

1342 def __init__( 

1343 self, 

1344 dialect: Dialect, 

1345 statement: Optional[ClauseElement], 

1346 cache_key: Optional[CacheKey] = None, 

1347 column_keys: Optional[Sequence[str]] = None, 

1348 for_executemany: bool = False, 

1349 linting: Linting = NO_LINTING, 

1350 _supporting_against: Optional[SQLCompiler] = None, 

1351 **kwargs: Any, 

1352 ): 

1353 """Construct a new :class:`.SQLCompiler` object. 

1354 

1355 :param dialect: :class:`.Dialect` to be used 

1356 

1357 :param statement: :class:`_expression.ClauseElement` to be compiled 

1358 

1359 :param column_keys: a list of column names to be compiled into an 

1360 INSERT or UPDATE statement. 

1361 

1362 :param for_executemany: whether INSERT / UPDATE statements should 

1363 expect that they are to be invoked in an "executemany" style, 

1364 which may impact how the statement will be expected to return the 

1365 values of defaults and autoincrement / sequences and similar. 

1366 Depending on the backend and driver in use, support for retrieving 

1367 these values may be disabled which means SQL expressions may 

1368 be rendered inline, RETURNING may not be rendered, etc. 

1369 

1370 :param kwargs: additional keyword arguments to be consumed by the 

1371 superclass. 

1372 

1373 """ 

1374 self.column_keys = column_keys 

1375 

1376 self.cache_key = cache_key 

1377 

1378 if cache_key: 

1379 cksm = {b.key: b for b in cache_key[1]} 

1380 ckbm = {b: [b] for b in cache_key[1]} 

1381 self._cache_key_bind_match = (ckbm, cksm) 

1382 

1383 # compile INSERT/UPDATE defaults/sequences to expect executemany 

1384 # style execution, which may mean no pre-execute of defaults, 

1385 # or no RETURNING 

1386 self.for_executemany = for_executemany 

1387 

1388 self.linting = linting 

1389 

1390 # a dictionary of bind parameter keys to BindParameter 

1391 # instances. 

1392 self.binds = {} 

1393 

1394 # a dictionary of BindParameter instances to "compiled" names 

1395 # that are actually present in the generated SQL 

1396 self.bind_names = util.column_dict() 

1397 

1398 # stack which keeps track of nested SELECT statements 

1399 self.stack = [] 

1400 

1401 self._result_columns = [] 

1402 

1403 # true if the paramstyle is positional 

1404 self.positional = dialect.positional 

1405 if self.positional: 

1406 self._numeric_binds = nb = dialect.paramstyle.startswith("numeric") 

1407 if nb: 

1408 self._numeric_binds_identifier_char = ( 

1409 "$" if dialect.paramstyle == "numeric_dollar" else ":" 

1410 ) 

1411 

1412 self.compilation_bindtemplate = _pyformat_template 

1413 else: 

1414 self.compilation_bindtemplate = BIND_TEMPLATES[dialect.paramstyle] 

1415 

1416 self.ctes = None 

1417 

1418 self.label_length = ( 

1419 dialect.label_length or dialect.max_identifier_length 

1420 ) 

1421 

1422 # a map which tracks "anonymous" identifiers that are created on 

1423 # the fly here 

1424 self.anon_map = prefix_anon_map() 

1425 

1426 # a map which tracks "truncated" names based on 

1427 # dialect.label_length or dialect.max_identifier_length 

1428 self.truncated_names: Dict[Tuple[str, str], str] = {} 

1429 self._truncated_counters: Dict[str, int] = {} 

1430 

1431 Compiled.__init__(self, dialect, statement, **kwargs) 

1432 

1433 if self.isinsert or self.isupdate or self.isdelete: 

1434 if TYPE_CHECKING: 

1435 assert isinstance(statement, UpdateBase) 

1436 

1437 if self.isinsert or self.isupdate: 

1438 if TYPE_CHECKING: 

1439 assert isinstance(statement, ValuesBase) 

1440 if statement._inline: 

1441 self.inline = True 

1442 elif self.for_executemany and ( 

1443 not self.isinsert 

1444 or ( 

1445 self.dialect.insert_executemany_returning 

1446 and statement._return_defaults 

1447 ) 

1448 ): 

1449 self.inline = True 

1450 

1451 self.bindtemplate = BIND_TEMPLATES[dialect.paramstyle] 

1452 

1453 if _supporting_against: 

1454 self.__dict__.update( 

1455 { 

1456 k: v 

1457 for k, v in _supporting_against.__dict__.items() 

1458 if k 

1459 not in { 

1460 "state", 

1461 "dialect", 

1462 "preparer", 

1463 "positional", 

1464 "_numeric_binds", 

1465 "compilation_bindtemplate", 

1466 "bindtemplate", 

1467 } 

1468 } 

1469 ) 

1470 

1471 if self.state is CompilerState.STRING_APPLIED: 

1472 if self.positional: 

1473 if self._numeric_binds: 

1474 self._process_numeric() 

1475 else: 

1476 self._process_positional() 

1477 

1478 if self._render_postcompile: 

1479 parameters = self.construct_params( 

1480 escape_names=False, 

1481 _no_postcompile=True, 

1482 ) 

1483 

1484 self._process_parameters_for_postcompile( 

1485 parameters, _populate_self=True 

1486 ) 

1487 

1488 @property 

1489 def insert_single_values_expr(self) -> Optional[str]: 

1490 """When an INSERT is compiled with a single set of parameters inside 

1491 a VALUES expression, the string is assigned here, where it can be 

1492 used for insert batching schemes to rewrite the VALUES expression. 

1493 

1494 .. versionadded:: 1.3.8 

1495 

1496 .. versionchanged:: 2.0 This collection is no longer used by 

1497 SQLAlchemy's built-in dialects, in favor of the currently 

1498 internal ``_insertmanyvalues`` collection that is used only by 

1499 :class:`.SQLCompiler`. 

1500 

1501 """ 

1502 if self._insertmanyvalues is None: 

1503 return None 

1504 else: 

1505 return self._insertmanyvalues.single_values_expr 

1506 

1507 @util.ro_memoized_property 

1508 def effective_returning(self) -> Optional[Sequence[ColumnElement[Any]]]: 

1509 """The effective "returning" columns for INSERT, UPDATE or DELETE. 

1510 

1511 This is either the so-called "implicit returning" columns which are 

1512 calculated by the compiler on the fly, or those present based on what's 

1513 present in ``self.statement._returning`` (expanded into individual 

1514 columns using the ``._all_selected_columns`` attribute) i.e. those set 

1515 explicitly using the :meth:`.UpdateBase.returning` method. 

1516 

1517 .. versionadded:: 2.0 

1518 

1519 """ 

1520 if self.implicit_returning: 

1521 return self.implicit_returning 

1522 elif self.statement is not None and is_dml(self.statement): 

1523 return [ 

1524 c 

1525 for c in self.statement._all_selected_columns 

1526 if is_column_element(c) 

1527 ] 

1528 

1529 else: 

1530 return None 

1531 

1532 @property 

1533 def returning(self): 

1534 """backwards compatibility; returns the 

1535 effective_returning collection. 

1536 

1537 """ 

1538 return self.effective_returning 

1539 

1540 @property 

1541 def current_executable(self): 

1542 """Return the current 'executable' that is being compiled. 

1543 

1544 This is currently the :class:`_sql.Select`, :class:`_sql.Insert`, 

1545 :class:`_sql.Update`, :class:`_sql.Delete`, 

1546 :class:`_sql.CompoundSelect` object that is being compiled. 

1547 Specifically it's assigned to the ``self.stack`` list of elements. 

1548 

1549 When a statement like the above is being compiled, it normally 

1550 is also assigned to the ``.statement`` attribute of the 

1551 :class:`_sql.Compiler` object. However, all SQL constructs are 

1552 ultimately nestable, and this attribute should never be consulted 

1553 by a ``visit_`` method, as it is not guaranteed to be assigned 

1554 nor guaranteed to correspond to the current statement being compiled. 

1555 

1556 .. versionadded:: 1.3.21 

1557 

1558 For compatibility with previous versions, use the following 

1559 recipe:: 

1560 

1561 statement = getattr(self, "current_executable", False) 

1562 if statement is False: 

1563 statement = self.stack[-1]["selectable"] 

1564 

1565 For versions 1.4 and above, ensure only .current_executable 

1566 is used; the format of "self.stack" may change. 

1567 

1568 

1569 """ 

1570 try: 

1571 return self.stack[-1]["selectable"] 

1572 except IndexError as ie: 

1573 raise IndexError("Compiler does not have a stack entry") from ie 

1574 

1575 @property 

1576 def prefetch(self): 

1577 return list(self.insert_prefetch) + list(self.update_prefetch) 

1578 

1579 @util.memoized_property 

1580 def _global_attributes(self) -> Dict[Any, Any]: 

1581 return {} 

1582 

1583 @util.memoized_instancemethod 

1584 def _init_cte_state(self) -> MutableMapping[CTE, str]: 

1585 """Initialize collections related to CTEs only if 

1586 a CTE is located, to save on the overhead of 

1587 these collections otherwise. 

1588 

1589 """ 

1590 # collect CTEs to tack on top of a SELECT 

1591 # To store the query to print - Dict[cte, text_query] 

1592 ctes: MutableMapping[CTE, str] = util.OrderedDict() 

1593 self.ctes = ctes 

1594 

1595 # Detect same CTE references - Dict[(level, name), cte] 

1596 # Level is required for supporting nesting 

1597 self.ctes_by_level_name = {} 

1598 

1599 # To retrieve key/level in ctes_by_level_name - 

1600 # Dict[cte_reference, (level, cte_name, cte_opts)] 

1601 self.level_name_by_cte = {} 

1602 

1603 self.ctes_recursive = False 

1604 

1605 return ctes 

1606 

1607 @contextlib.contextmanager 

1608 def _nested_result(self): 

1609 """special API to support the use case of 'nested result sets'""" 

1610 result_columns, ordered_columns = ( 

1611 self._result_columns, 

1612 self._ordered_columns, 

1613 ) 

1614 self._result_columns, self._ordered_columns = [], False 

1615 

1616 try: 

1617 if self.stack: 

1618 entry = self.stack[-1] 

1619 entry["need_result_map_for_nested"] = True 

1620 else: 

1621 entry = None 

1622 yield self._result_columns, self._ordered_columns 

1623 finally: 

1624 if entry: 

1625 entry.pop("need_result_map_for_nested") 

1626 self._result_columns, self._ordered_columns = ( 

1627 result_columns, 

1628 ordered_columns, 

1629 ) 

1630 

1631 def _process_positional(self): 

1632 assert not self.positiontup 

1633 assert self.state is CompilerState.STRING_APPLIED 

1634 assert not self._numeric_binds 

1635 

1636 if self.dialect.paramstyle == "format": 

1637 placeholder = "%s" 

1638 else: 

1639 assert self.dialect.paramstyle == "qmark" 

1640 placeholder = "?" 

1641 

1642 positions = [] 

1643 

1644 def find_position(m: re.Match[str]) -> str: 

1645 normal_bind = m.group(1) 

1646 if normal_bind: 

1647 positions.append(normal_bind) 

1648 return placeholder 

1649 else: 

1650 # this a post-compile bind 

1651 positions.append(m.group(2)) 

1652 return m.group(0) 

1653 

1654 self.string = re.sub( 

1655 self._positional_pattern, find_position, self.string 

1656 ) 

1657 

1658 if self.escaped_bind_names: 

1659 reverse_escape = {v: k for k, v in self.escaped_bind_names.items()} 

1660 assert len(self.escaped_bind_names) == len(reverse_escape) 

1661 self.positiontup = [ 

1662 reverse_escape.get(name, name) for name in positions 

1663 ] 

1664 else: 

1665 self.positiontup = positions 

1666 

1667 if self._insertmanyvalues: 

1668 positions = [] 

1669 

1670 single_values_expr = re.sub( 

1671 self._positional_pattern, 

1672 find_position, 

1673 self._insertmanyvalues.single_values_expr, 

1674 ) 

1675 insert_crud_params = [ 

1676 ( 

1677 v[0], 

1678 v[1], 

1679 re.sub(self._positional_pattern, find_position, v[2]), 

1680 v[3], 

1681 ) 

1682 for v in self._insertmanyvalues.insert_crud_params 

1683 ] 

1684 

1685 self._insertmanyvalues = self._insertmanyvalues._replace( 

1686 single_values_expr=single_values_expr, 

1687 insert_crud_params=insert_crud_params, 

1688 ) 

1689 

1690 def _process_numeric(self): 

1691 assert self._numeric_binds 

1692 assert self.state is CompilerState.STRING_APPLIED 

1693 

1694 num = 1 

1695 param_pos: Dict[str, str] = {} 

1696 order: Iterable[str] 

1697 if self._insertmanyvalues and self._values_bindparam is not None: 

1698 # bindparams that are not in values are always placed first. 

1699 # this avoids the need of changing them when using executemany 

1700 # values () () 

1701 order = itertools.chain( 

1702 ( 

1703 name 

1704 for name in self.bind_names.values() 

1705 if name not in self._values_bindparam 

1706 ), 

1707 self.bind_names.values(), 

1708 ) 

1709 else: 

1710 order = self.bind_names.values() 

1711 

1712 for bind_name in order: 

1713 if bind_name in param_pos: 

1714 continue 

1715 bind = self.binds[bind_name] 

1716 if ( 

1717 bind in self.post_compile_params 

1718 or bind in self.literal_execute_params 

1719 ): 

1720 # set to None to just mark the in positiontup, it will not 

1721 # be replaced below. 

1722 param_pos[bind_name] = None # type: ignore 

1723 else: 

1724 ph = f"{self._numeric_binds_identifier_char}{num}" 

1725 num += 1 

1726 param_pos[bind_name] = ph 

1727 

1728 self.next_numeric_pos = num 

1729 

1730 self.positiontup = list(param_pos) 

1731 if self.escaped_bind_names: 

1732 len_before = len(param_pos) 

1733 param_pos = { 

1734 self.escaped_bind_names.get(name, name): pos 

1735 for name, pos in param_pos.items() 

1736 } 

1737 assert len(param_pos) == len_before 

1738 

1739 # Can't use format here since % chars are not escaped. 

1740 self.string = self._pyformat_pattern.sub( 

1741 lambda m: param_pos[m.group(1)], self.string 

1742 ) 

1743 

1744 if self._insertmanyvalues: 

1745 single_values_expr = ( 

1746 # format is ok here since single_values_expr includes only 

1747 # place-holders 

1748 self._insertmanyvalues.single_values_expr 

1749 % param_pos 

1750 ) 

1751 insert_crud_params = [ 

1752 (v[0], v[1], "%s", v[3]) 

1753 for v in self._insertmanyvalues.insert_crud_params 

1754 ] 

1755 

1756 self._insertmanyvalues = self._insertmanyvalues._replace( 

1757 # This has the numbers (:1, :2) 

1758 single_values_expr=single_values_expr, 

1759 # The single binds are instead %s so they can be formatted 

1760 insert_crud_params=insert_crud_params, 

1761 ) 

1762 

1763 @util.memoized_property 

1764 def _bind_processors( 

1765 self, 

1766 ) -> MutableMapping[ 

1767 str, Union[_BindProcessorType[Any], Sequence[_BindProcessorType[Any]]] 

1768 ]: 

1769 # mypy is not able to see the two value types as the above Union, 

1770 # it just sees "object". don't know how to resolve 

1771 return { 

1772 key: value # type: ignore 

1773 for key, value in ( 

1774 ( 

1775 self.bind_names[bindparam], 

1776 ( 

1777 bindparam.type._cached_bind_processor(self.dialect) 

1778 if not bindparam.type._is_tuple_type 

1779 else tuple( 

1780 elem_type._cached_bind_processor(self.dialect) 

1781 for elem_type in cast( 

1782 TupleType, bindparam.type 

1783 ).types 

1784 ) 

1785 ), 

1786 ) 

1787 for bindparam in self.bind_names 

1788 ) 

1789 if value is not None 

1790 } 

1791 

1792 def is_subquery(self): 

1793 return len(self.stack) > 1 

1794 

1795 @property 

1796 def sql_compiler(self): 

1797 return self 

1798 

1799 def construct_expanded_state( 

1800 self, 

1801 params: Optional[_CoreSingleExecuteParams] = None, 

1802 escape_names: bool = True, 

1803 ) -> ExpandedState: 

1804 """Return a new :class:`.ExpandedState` for a given parameter set. 

1805 

1806 For queries that use "expanding" or other late-rendered parameters, 

1807 this method will provide for both the finalized SQL string as well 

1808 as the parameters that would be used for a particular parameter set. 

1809 

1810 .. versionadded:: 2.0.0rc1 

1811 

1812 """ 

1813 parameters = self.construct_params( 

1814 params, 

1815 escape_names=escape_names, 

1816 _no_postcompile=True, 

1817 ) 

1818 return self._process_parameters_for_postcompile( 

1819 parameters, 

1820 ) 

1821 

1822 def construct_params( 

1823 self, 

1824 params: Optional[_CoreSingleExecuteParams] = None, 

1825 extracted_parameters: Optional[Sequence[BindParameter[Any]]] = None, 

1826 escape_names: bool = True, 

1827 _group_number: Optional[int] = None, 

1828 _check: bool = True, 

1829 _no_postcompile: bool = False, 

1830 ) -> _MutableCoreSingleExecuteParams: 

1831 """return a dictionary of bind parameter keys and values""" 

1832 

1833 if self._render_postcompile and not _no_postcompile: 

1834 assert self._post_compile_expanded_state is not None 

1835 if not params: 

1836 return dict(self._post_compile_expanded_state.parameters) 

1837 else: 

1838 raise exc.InvalidRequestError( 

1839 "can't construct new parameters when render_postcompile " 

1840 "is used; the statement is hard-linked to the original " 

1841 "parameters. Use construct_expanded_state to generate a " 

1842 "new statement and parameters." 

1843 ) 

1844 

1845 has_escaped_names = escape_names and bool(self.escaped_bind_names) 

1846 

1847 if extracted_parameters: 

1848 # related the bound parameters collected in the original cache key 

1849 # to those collected in the incoming cache key. They will not have 

1850 # matching names but they will line up positionally in the same 

1851 # way. The parameters present in self.bind_names may be clones of 

1852 # these original cache key params in the case of DML but the .key 

1853 # will be guaranteed to match. 

1854 if self.cache_key is None: 

1855 raise exc.CompileError( 

1856 "This compiled object has no original cache key; " 

1857 "can't pass extracted_parameters to construct_params" 

1858 ) 

1859 else: 

1860 orig_extracted = self.cache_key[1] 

1861 

1862 ckbm_tuple = self._cache_key_bind_match 

1863 assert ckbm_tuple is not None 

1864 ckbm, _ = ckbm_tuple 

1865 resolved_extracted = { 

1866 bind: extracted 

1867 for b, extracted in zip(orig_extracted, extracted_parameters) 

1868 for bind in ckbm[b] 

1869 } 

1870 else: 

1871 resolved_extracted = None 

1872 

1873 if params: 

1874 pd = {} 

1875 for bindparam, name in self.bind_names.items(): 

1876 escaped_name = ( 

1877 self.escaped_bind_names.get(name, name) 

1878 if has_escaped_names 

1879 else name 

1880 ) 

1881 

1882 if bindparam.key in params: 

1883 pd[escaped_name] = params[bindparam.key] 

1884 elif name in params: 

1885 pd[escaped_name] = params[name] 

1886 

1887 elif _check and bindparam.required: 

1888 if _group_number: 

1889 raise exc.InvalidRequestError( 

1890 "A value is required for bind parameter %r, " 

1891 "in parameter group %d" 

1892 % (bindparam.key, _group_number), 

1893 code="cd3x", 

1894 ) 

1895 else: 

1896 raise exc.InvalidRequestError( 

1897 "A value is required for bind parameter %r" 

1898 % bindparam.key, 

1899 code="cd3x", 

1900 ) 

1901 else: 

1902 if resolved_extracted: 

1903 value_param = resolved_extracted.get( 

1904 bindparam, bindparam 

1905 ) 

1906 else: 

1907 value_param = bindparam 

1908 

1909 if bindparam.callable: 

1910 pd[escaped_name] = value_param.effective_value 

1911 else: 

1912 pd[escaped_name] = value_param.value 

1913 return pd 

1914 else: 

1915 pd = {} 

1916 for bindparam, name in self.bind_names.items(): 

1917 escaped_name = ( 

1918 self.escaped_bind_names.get(name, name) 

1919 if has_escaped_names 

1920 else name 

1921 ) 

1922 

1923 if _check and bindparam.required: 

1924 if _group_number: 

1925 raise exc.InvalidRequestError( 

1926 "A value is required for bind parameter %r, " 

1927 "in parameter group %d" 

1928 % (bindparam.key, _group_number), 

1929 code="cd3x", 

1930 ) 

1931 else: 

1932 raise exc.InvalidRequestError( 

1933 "A value is required for bind parameter %r" 

1934 % bindparam.key, 

1935 code="cd3x", 

1936 ) 

1937 

1938 if resolved_extracted: 

1939 value_param = resolved_extracted.get(bindparam, bindparam) 

1940 else: 

1941 value_param = bindparam 

1942 

1943 if bindparam.callable: 

1944 pd[escaped_name] = value_param.effective_value 

1945 else: 

1946 pd[escaped_name] = value_param.value 

1947 

1948 return pd 

1949 

1950 @util.memoized_instancemethod 

1951 def _get_set_input_sizes_lookup(self): 

1952 dialect = self.dialect 

1953 

1954 include_types = dialect.include_set_input_sizes 

1955 exclude_types = dialect.exclude_set_input_sizes 

1956 

1957 dbapi = dialect.dbapi 

1958 

1959 def lookup_type(typ): 

1960 dbtype = typ._unwrapped_dialect_impl(dialect).get_dbapi_type(dbapi) 

1961 

1962 if ( 

1963 dbtype is not None 

1964 and (exclude_types is None or dbtype not in exclude_types) 

1965 and (include_types is None or dbtype in include_types) 

1966 ): 

1967 return dbtype 

1968 else: 

1969 return None 

1970 

1971 inputsizes = {} 

1972 

1973 literal_execute_params = self.literal_execute_params 

1974 

1975 for bindparam in self.bind_names: 

1976 if bindparam in literal_execute_params: 

1977 continue 

1978 

1979 if bindparam.type._is_tuple_type: 

1980 inputsizes[bindparam] = [ 

1981 lookup_type(typ) 

1982 for typ in cast(TupleType, bindparam.type).types 

1983 ] 

1984 else: 

1985 inputsizes[bindparam] = lookup_type(bindparam.type) 

1986 

1987 return inputsizes 

1988 

1989 @property 

1990 def params(self): 

1991 """Return the bind param dictionary embedded into this 

1992 compiled object, for those values that are present. 

1993 

1994 .. seealso:: 

1995 

1996 :ref:`faq_sql_expression_string` - includes a usage example for 

1997 debugging use cases. 

1998 

1999 """ 

2000 return self.construct_params(_check=False) 

2001 

2002 def _process_parameters_for_postcompile( 

2003 self, 

2004 parameters: _MutableCoreSingleExecuteParams, 

2005 _populate_self: bool = False, 

2006 ) -> ExpandedState: 

2007 """handle special post compile parameters. 

2008 

2009 These include: 

2010 

2011 * "expanding" parameters -typically IN tuples that are rendered 

2012 on a per-parameter basis for an otherwise fixed SQL statement string. 

2013 

2014 * literal_binds compiled with the literal_execute flag. Used for 

2015 things like SQL Server "TOP N" where the driver does not accommodate 

2016 N as a bound parameter. 

2017 

2018 """ 

2019 

2020 expanded_parameters = {} 

2021 new_positiontup: Optional[List[str]] 

2022 

2023 pre_expanded_string = self._pre_expanded_string 

2024 if pre_expanded_string is None: 

2025 pre_expanded_string = self.string 

2026 

2027 if self.positional: 

2028 new_positiontup = [] 

2029 

2030 pre_expanded_positiontup = self._pre_expanded_positiontup 

2031 if pre_expanded_positiontup is None: 

2032 pre_expanded_positiontup = self.positiontup 

2033 

2034 else: 

2035 new_positiontup = pre_expanded_positiontup = None 

2036 

2037 processors = self._bind_processors 

2038 single_processors = cast( 

2039 "Mapping[str, _BindProcessorType[Any]]", processors 

2040 ) 

2041 tuple_processors = cast( 

2042 "Mapping[str, Sequence[_BindProcessorType[Any]]]", processors 

2043 ) 

2044 

2045 new_processors: Dict[str, _BindProcessorType[Any]] = {} 

2046 

2047 replacement_expressions: Dict[str, Any] = {} 

2048 to_update_sets: Dict[str, Any] = {} 

2049 

2050 # notes: 

2051 # *unescaped* parameter names in: 

2052 # self.bind_names, self.binds, self._bind_processors, self.positiontup 

2053 # 

2054 # *escaped* parameter names in: 

2055 # construct_params(), replacement_expressions 

2056 

2057 numeric_positiontup: Optional[List[str]] = None 

2058 

2059 if self.positional and pre_expanded_positiontup is not None: 

2060 names: Iterable[str] = pre_expanded_positiontup 

2061 if self._numeric_binds: 

2062 numeric_positiontup = [] 

2063 else: 

2064 names = self.bind_names.values() 

2065 

2066 ebn = self.escaped_bind_names 

2067 for name in names: 

2068 escaped_name = ebn.get(name, name) if ebn else name 

2069 parameter = self.binds[name] 

2070 

2071 if parameter in self.literal_execute_params: 

2072 if escaped_name not in replacement_expressions: 

2073 replacement_expressions[escaped_name] = ( 

2074 self.render_literal_bindparam( 

2075 parameter, 

2076 render_literal_value=parameters.pop(escaped_name), 

2077 ) 

2078 ) 

2079 continue 

2080 

2081 if parameter in self.post_compile_params: 

2082 if escaped_name in replacement_expressions: 

2083 to_update = to_update_sets[escaped_name] 

2084 values = None 

2085 else: 

2086 # we are removing the parameter from parameters 

2087 # because it is a list value, which is not expected by 

2088 # TypeEngine objects that would otherwise be asked to 

2089 # process it. the single name is being replaced with 

2090 # individual numbered parameters for each value in the 

2091 # param. 

2092 # 

2093 # note we are also inserting *escaped* parameter names 

2094 # into the given dictionary. default dialect will 

2095 # use these param names directly as they will not be 

2096 # in the escaped_bind_names dictionary. 

2097 values = parameters.pop(name) 

2098 

2099 leep_res = self._literal_execute_expanding_parameter( 

2100 escaped_name, parameter, values 

2101 ) 

2102 (to_update, replacement_expr) = leep_res 

2103 

2104 to_update_sets[escaped_name] = to_update 

2105 replacement_expressions[escaped_name] = replacement_expr 

2106 

2107 if not parameter.literal_execute: 

2108 parameters.update(to_update) 

2109 if parameter.type._is_tuple_type: 

2110 assert values is not None 

2111 new_processors.update( 

2112 ( 

2113 "%s_%s_%s" % (name, i, j), 

2114 tuple_processors[name][j - 1], 

2115 ) 

2116 for i, tuple_element in enumerate(values, 1) 

2117 for j, _ in enumerate(tuple_element, 1) 

2118 if name in tuple_processors 

2119 and tuple_processors[name][j - 1] is not None 

2120 ) 

2121 else: 

2122 new_processors.update( 

2123 (key, single_processors[name]) 

2124 for key, _ in to_update 

2125 if name in single_processors 

2126 ) 

2127 if numeric_positiontup is not None: 

2128 numeric_positiontup.extend( 

2129 name for name, _ in to_update 

2130 ) 

2131 elif new_positiontup is not None: 

2132 # to_update has escaped names, but that's ok since 

2133 # these are new names, that aren't in the 

2134 # escaped_bind_names dict. 

2135 new_positiontup.extend(name for name, _ in to_update) 

2136 expanded_parameters[name] = [ 

2137 expand_key for expand_key, _ in to_update 

2138 ] 

2139 elif new_positiontup is not None: 

2140 new_positiontup.append(name) 

2141 

2142 def process_expanding(m): 

2143 key = m.group(1) 

2144 expr = replacement_expressions[key] 

2145 

2146 # if POSTCOMPILE included a bind_expression, render that 

2147 # around each element 

2148 if m.group(2): 

2149 tok = m.group(2).split("~~") 

2150 be_left, be_right = tok[1], tok[3] 

2151 expr = ", ".join( 

2152 "%s%s%s" % (be_left, exp, be_right) 

2153 for exp in expr.split(", ") 

2154 ) 

2155 return expr 

2156 

2157 statement = re.sub( 

2158 self._post_compile_pattern, process_expanding, pre_expanded_string 

2159 ) 

2160 

2161 if numeric_positiontup is not None: 

2162 assert new_positiontup is not None 

2163 param_pos = { 

2164 key: f"{self._numeric_binds_identifier_char}{num}" 

2165 for num, key in enumerate( 

2166 numeric_positiontup, self.next_numeric_pos 

2167 ) 

2168 } 

2169 # Can't use format here since % chars are not escaped. 

2170 statement = self._pyformat_pattern.sub( 

2171 lambda m: param_pos[m.group(1)], statement 

2172 ) 

2173 new_positiontup.extend(numeric_positiontup) 

2174 

2175 expanded_state = ExpandedState( 

2176 statement, 

2177 parameters, 

2178 new_processors, 

2179 new_positiontup, 

2180 expanded_parameters, 

2181 ) 

2182 

2183 if _populate_self: 

2184 # this is for the "render_postcompile" flag, which is not 

2185 # otherwise used internally and is for end-user debugging and 

2186 # special use cases. 

2187 self._pre_expanded_string = pre_expanded_string 

2188 self._pre_expanded_positiontup = pre_expanded_positiontup 

2189 self.string = expanded_state.statement 

2190 self.positiontup = ( 

2191 list(expanded_state.positiontup or ()) 

2192 if self.positional 

2193 else None 

2194 ) 

2195 self._post_compile_expanded_state = expanded_state 

2196 

2197 return expanded_state 

2198 

2199 @util.preload_module("sqlalchemy.engine.cursor") 

2200 def _create_result_map(self): 

2201 """utility method used for unit tests only.""" 

2202 cursor = util.preloaded.engine_cursor 

2203 return cursor.CursorResultMetaData._create_description_match_map( 

2204 self._result_columns 

2205 ) 

2206 

2207 # assigned by crud.py for insert/update statements 

2208 _get_bind_name_for_col: _BindNameForColProtocol 

2209 

2210 @util.memoized_property 

2211 def _within_exec_param_key_getter(self) -> Callable[[Any], str]: 

2212 getter = self._get_bind_name_for_col 

2213 return getter 

2214 

2215 @util.memoized_property 

2216 @util.preload_module("sqlalchemy.engine.result") 

2217 def _inserted_primary_key_from_lastrowid_getter(self): 

2218 result = util.preloaded.engine_result 

2219 

2220 param_key_getter = self._within_exec_param_key_getter 

2221 

2222 assert self.compile_state is not None 

2223 statement = self.compile_state.statement 

2224 

2225 if TYPE_CHECKING: 

2226 assert isinstance(statement, Insert) 

2227 

2228 table = statement.table 

2229 

2230 getters = [ 

2231 (operator.methodcaller("get", param_key_getter(col), None), col) 

2232 for col in table.primary_key 

2233 ] 

2234 

2235 autoinc_getter = None 

2236 autoinc_col = table._autoincrement_column 

2237 if autoinc_col is not None: 

2238 # apply type post processors to the lastrowid 

2239 lastrowid_processor = autoinc_col.type._cached_result_processor( 

2240 self.dialect, None 

2241 ) 

2242 autoinc_key = param_key_getter(autoinc_col) 

2243 

2244 # if a bind value is present for the autoincrement column 

2245 # in the parameters, we need to do the logic dictated by 

2246 # #7998; honor a non-None user-passed parameter over lastrowid. 

2247 # previously in the 1.4 series we weren't fetching lastrowid 

2248 # at all if the key were present in the parameters 

2249 if autoinc_key in self.binds: 

2250 

2251 def _autoinc_getter(lastrowid, parameters): 

2252 param_value = parameters.get(autoinc_key, lastrowid) 

2253 if param_value is not None: 

2254 # they supplied non-None parameter, use that. 

2255 # SQLite at least is observed to return the wrong 

2256 # cursor.lastrowid for INSERT..ON CONFLICT so it 

2257 # can't be used in all cases 

2258 return param_value 

2259 else: 

2260 # use lastrowid 

2261 return lastrowid 

2262 

2263 # work around mypy https://github.com/python/mypy/issues/14027 

2264 autoinc_getter = _autoinc_getter 

2265 

2266 else: 

2267 lastrowid_processor = None 

2268 

2269 row_fn = result.result_tuple([col.key for col in table.primary_key]) 

2270 

2271 def get(lastrowid, parameters): 

2272 """given cursor.lastrowid value and the parameters used for INSERT, 

2273 return a "row" that represents the primary key, either by 

2274 using the "lastrowid" or by extracting values from the parameters 

2275 that were sent along with the INSERT. 

2276 

2277 """ 

2278 if lastrowid_processor is not None: 

2279 lastrowid = lastrowid_processor(lastrowid) 

2280 

2281 if lastrowid is None: 

2282 return row_fn(getter(parameters) for getter, col in getters) 

2283 else: 

2284 return row_fn( 

2285 ( 

2286 ( 

2287 autoinc_getter(lastrowid, parameters) 

2288 if autoinc_getter is not None 

2289 else lastrowid 

2290 ) 

2291 if col is autoinc_col 

2292 else getter(parameters) 

2293 ) 

2294 for getter, col in getters 

2295 ) 

2296 

2297 return get 

2298 

2299 @util.memoized_property 

2300 @util.preload_module("sqlalchemy.engine.result") 

2301 def _inserted_primary_key_from_returning_getter(self): 

2302 if typing.TYPE_CHECKING: 

2303 from ..engine import result 

2304 else: 

2305 result = util.preloaded.engine_result 

2306 

2307 assert self.compile_state is not None 

2308 statement = self.compile_state.statement 

2309 

2310 if TYPE_CHECKING: 

2311 assert isinstance(statement, Insert) 

2312 

2313 param_key_getter = self._within_exec_param_key_getter 

2314 table = statement.table 

2315 

2316 returning = self.implicit_returning 

2317 assert returning is not None 

2318 ret = {col: idx for idx, col in enumerate(returning)} 

2319 

2320 getters = cast( 

2321 "List[Tuple[Callable[[Any], Any], bool]]", 

2322 [ 

2323 ( 

2324 (operator.itemgetter(ret[col]), True) 

2325 if col in ret 

2326 else ( 

2327 operator.methodcaller( 

2328 "get", param_key_getter(col), None 

2329 ), 

2330 False, 

2331 ) 

2332 ) 

2333 for col in table.primary_key 

2334 ], 

2335 ) 

2336 

2337 row_fn = result.result_tuple([col.key for col in table.primary_key]) 

2338 

2339 def get(row, parameters): 

2340 return row_fn( 

2341 getter(row) if use_row else getter(parameters) 

2342 for getter, use_row in getters 

2343 ) 

2344 

2345 return get 

2346 

2347 def default_from(self): 

2348 """Called when a SELECT statement has no froms, and no FROM clause is 

2349 to be appended. 

2350 

2351 Gives Oracle a chance to tack on a ``FROM DUAL`` to the string output. 

2352 

2353 """ 

2354 return "" 

2355 

2356 def visit_override_binds(self, override_binds, **kw): 

2357 """SQL compile the nested element of an _OverrideBinds with 

2358 bindparams swapped out. 

2359 

2360 The _OverrideBinds is not normally expected to be compiled; it 

2361 is meant to be used when an already cached statement is to be used, 

2362 the compilation was already performed, and only the bound params should 

2363 be swapped in at execution time. 

2364 

2365 However, there are test cases that exericise this object, and 

2366 additionally the ORM subquery loader is known to feed in expressions 

2367 which include this construct into new queries (discovered in #11173), 

2368 so it has to do the right thing at compile time as well. 

2369 

2370 """ 

2371 

2372 # get SQL text first 

2373 sqltext = override_binds.element._compiler_dispatch(self, **kw) 

2374 

2375 # for a test compile that is not for caching, change binds after the 

2376 # fact. note that we don't try to 

2377 # swap the bindparam as we compile, because our element may be 

2378 # elsewhere in the statement already (e.g. a subquery or perhaps a 

2379 # CTE) and was already visited / compiled. See 

2380 # test_relationship_criteria.py -> 

2381 # test_selectinload_local_criteria_subquery 

2382 for k in override_binds.translate: 

2383 if k not in self.binds: 

2384 continue 

2385 bp = self.binds[k] 

2386 

2387 # so this would work, just change the value of bp in place. 

2388 # but we dont want to mutate things outside. 

2389 # bp.value = override_binds.translate[bp.key] 

2390 # continue 

2391 

2392 # instead, need to replace bp with new_bp or otherwise accommodate 

2393 # in all internal collections 

2394 new_bp = bp._with_value( 

2395 override_binds.translate[bp.key], 

2396 maintain_key=True, 

2397 required=False, 

2398 ) 

2399 

2400 name = self.bind_names[bp] 

2401 self.binds[k] = self.binds[name] = new_bp 

2402 self.bind_names[new_bp] = name 

2403 self.bind_names.pop(bp, None) 

2404 

2405 if bp in self.post_compile_params: 

2406 self.post_compile_params |= {new_bp} 

2407 if bp in self.literal_execute_params: 

2408 self.literal_execute_params |= {new_bp} 

2409 

2410 ckbm_tuple = self._cache_key_bind_match 

2411 if ckbm_tuple: 

2412 ckbm, cksm = ckbm_tuple 

2413 for bp in bp._cloned_set: 

2414 if bp.key in cksm: 

2415 cb = cksm[bp.key] 

2416 ckbm[cb].append(new_bp) 

2417 

2418 return sqltext 

2419 

2420 def visit_grouping(self, grouping, asfrom=False, **kwargs): 

2421 return "(" + grouping.element._compiler_dispatch(self, **kwargs) + ")" 

2422 

2423 def visit_select_statement_grouping(self, grouping, **kwargs): 

2424 return "(" + grouping.element._compiler_dispatch(self, **kwargs) + ")" 

2425 

2426 def visit_label_reference( 

2427 self, element, within_columns_clause=False, **kwargs 

2428 ): 

2429 if self.stack and self.dialect.supports_simple_order_by_label: 

2430 try: 

2431 compile_state = cast( 

2432 "Union[SelectState, CompoundSelectState]", 

2433 self.stack[-1]["compile_state"], 

2434 ) 

2435 except KeyError as ke: 

2436 raise exc.CompileError( 

2437 "Can't resolve label reference for ORDER BY / " 

2438 "GROUP BY / DISTINCT etc." 

2439 ) from ke 

2440 

2441 ( 

2442 with_cols, 

2443 only_froms, 

2444 only_cols, 

2445 ) = compile_state._label_resolve_dict 

2446 if within_columns_clause: 

2447 resolve_dict = only_froms 

2448 else: 

2449 resolve_dict = only_cols 

2450 

2451 # this can be None in the case that a _label_reference() 

2452 # were subject to a replacement operation, in which case 

2453 # the replacement of the Label element may have changed 

2454 # to something else like a ColumnClause expression. 

2455 order_by_elem = element.element._order_by_label_element 

2456 

2457 if ( 

2458 order_by_elem is not None 

2459 and order_by_elem.name in resolve_dict 

2460 and order_by_elem.shares_lineage( 

2461 resolve_dict[order_by_elem.name] 

2462 ) 

2463 ): 

2464 kwargs["render_label_as_label"] = ( 

2465 element.element._order_by_label_element 

2466 ) 

2467 return self.process( 

2468 element.element, 

2469 within_columns_clause=within_columns_clause, 

2470 **kwargs, 

2471 ) 

2472 

2473 def visit_textual_label_reference( 

2474 self, element, within_columns_clause=False, **kwargs 

2475 ): 

2476 if not self.stack: 

2477 # compiling the element outside of the context of a SELECT 

2478 return self.process(element._text_clause) 

2479 

2480 try: 

2481 compile_state = cast( 

2482 "Union[SelectState, CompoundSelectState]", 

2483 self.stack[-1]["compile_state"], 

2484 ) 

2485 except KeyError as ke: 

2486 coercions._no_text_coercion( 

2487 element.element, 

2488 extra=( 

2489 "Can't resolve label reference for ORDER BY / " 

2490 "GROUP BY / DISTINCT etc." 

2491 ), 

2492 exc_cls=exc.CompileError, 

2493 err=ke, 

2494 ) 

2495 

2496 with_cols, only_froms, only_cols = compile_state._label_resolve_dict 

2497 try: 

2498 if within_columns_clause: 

2499 col = only_froms[element.element] 

2500 else: 

2501 col = with_cols[element.element] 

2502 except KeyError as err: 

2503 coercions._no_text_coercion( 

2504 element.element, 

2505 extra=( 

2506 "Can't resolve label reference for ORDER BY / " 

2507 "GROUP BY / DISTINCT etc." 

2508 ), 

2509 exc_cls=exc.CompileError, 

2510 err=err, 

2511 ) 

2512 else: 

2513 kwargs["render_label_as_label"] = col 

2514 return self.process( 

2515 col, within_columns_clause=within_columns_clause, **kwargs 

2516 ) 

2517 

2518 def visit_label( 

2519 self, 

2520 label, 

2521 add_to_result_map=None, 

2522 within_label_clause=False, 

2523 within_columns_clause=False, 

2524 render_label_as_label=None, 

2525 result_map_targets=(), 

2526 **kw, 

2527 ): 

2528 # only render labels within the columns clause 

2529 # or ORDER BY clause of a select. dialect-specific compilers 

2530 # can modify this behavior. 

2531 render_label_with_as = ( 

2532 within_columns_clause and not within_label_clause 

2533 ) 

2534 render_label_only = render_label_as_label is label 

2535 

2536 if render_label_only or render_label_with_as: 

2537 if isinstance(label.name, elements._truncated_label): 

2538 labelname = self._truncated_identifier("colident", label.name) 

2539 else: 

2540 labelname = label.name 

2541 

2542 if render_label_with_as: 

2543 if add_to_result_map is not None: 

2544 add_to_result_map( 

2545 labelname, 

2546 label.name, 

2547 (label, labelname) + label._alt_names + result_map_targets, 

2548 label.type, 

2549 ) 

2550 return ( 

2551 label.element._compiler_dispatch( 

2552 self, 

2553 within_columns_clause=True, 

2554 within_label_clause=True, 

2555 **kw, 

2556 ) 

2557 + OPERATORS[operators.as_] 

2558 + self.preparer.format_label(label, labelname) 

2559 ) 

2560 elif render_label_only: 

2561 return self.preparer.format_label(label, labelname) 

2562 else: 

2563 return label.element._compiler_dispatch( 

2564 self, within_columns_clause=False, **kw 

2565 ) 

2566 

2567 def _fallback_column_name(self, column): 

2568 raise exc.CompileError( 

2569 "Cannot compile Column object until its 'name' is assigned." 

2570 ) 

2571 

2572 def visit_lambda_element(self, element, **kw): 

2573 sql_element = element._resolved 

2574 return self.process(sql_element, **kw) 

2575 

2576 def visit_column( 

2577 self, 

2578 column: ColumnClause[Any], 

2579 add_to_result_map: Optional[_ResultMapAppender] = None, 

2580 include_table: bool = True, 

2581 result_map_targets: Tuple[Any, ...] = (), 

2582 ambiguous_table_name_map: Optional[_AmbiguousTableNameMap] = None, 

2583 **kwargs: Any, 

2584 ) -> str: 

2585 name = orig_name = column.name 

2586 if name is None: 

2587 name = self._fallback_column_name(column) 

2588 

2589 is_literal = column.is_literal 

2590 if not is_literal and isinstance(name, elements._truncated_label): 

2591 name = self._truncated_identifier("colident", name) 

2592 

2593 if add_to_result_map is not None: 

2594 targets = (column, name, column.key) + result_map_targets 

2595 if column._tq_label: 

2596 targets += (column._tq_label,) 

2597 

2598 add_to_result_map(name, orig_name, targets, column.type) 

2599 

2600 if is_literal: 

2601 # note we are not currently accommodating for 

2602 # literal_column(quoted_name('ident', True)) here 

2603 name = self.escape_literal_column(name) 

2604 else: 

2605 name = self.preparer.quote(name) 

2606 table = column.table 

2607 if table is None or not include_table or not table.named_with_column: 

2608 return name 

2609 else: 

2610 effective_schema = self.preparer.schema_for_object(table) 

2611 

2612 if effective_schema: 

2613 schema_prefix = ( 

2614 self.preparer.quote_schema(effective_schema) + "." 

2615 ) 

2616 else: 

2617 schema_prefix = "" 

2618 

2619 if TYPE_CHECKING: 

2620 assert isinstance(table, NamedFromClause) 

2621 tablename = table.name 

2622 

2623 if ( 

2624 not effective_schema 

2625 and ambiguous_table_name_map 

2626 and tablename in ambiguous_table_name_map 

2627 ): 

2628 tablename = ambiguous_table_name_map[tablename] 

2629 

2630 if isinstance(tablename, elements._truncated_label): 

2631 tablename = self._truncated_identifier("alias", tablename) 

2632 

2633 return schema_prefix + self.preparer.quote(tablename) + "." + name 

2634 

2635 def visit_collation(self, element, **kw): 

2636 return self.preparer.format_collation(element.collation) 

2637 

2638 def visit_fromclause(self, fromclause, **kwargs): 

2639 return fromclause.name 

2640 

2641 def visit_index(self, index, **kwargs): 

2642 return index.name 

2643 

2644 def visit_typeclause(self, typeclause, **kw): 

2645 kw["type_expression"] = typeclause 

2646 kw["identifier_preparer"] = self.preparer 

2647 return self.dialect.type_compiler_instance.process( 

2648 typeclause.type, **kw 

2649 ) 

2650 

2651 def post_process_text(self, text): 

2652 if self.preparer._double_percents: 

2653 text = text.replace("%", "%%") 

2654 return text 

2655 

2656 def escape_literal_column(self, text): 

2657 if self.preparer._double_percents: 

2658 text = text.replace("%", "%%") 

2659 return text 

2660 

2661 def visit_textclause(self, textclause, add_to_result_map=None, **kw): 

2662 def do_bindparam(m): 

2663 name = m.group(1) 

2664 if name in textclause._bindparams: 

2665 return self.process(textclause._bindparams[name], **kw) 

2666 else: 

2667 return self.bindparam_string(name, **kw) 

2668 

2669 if not self.stack: 

2670 self.isplaintext = True 

2671 

2672 if add_to_result_map: 

2673 # text() object is present in the columns clause of a 

2674 # select(). Add a no-name entry to the result map so that 

2675 # row[text()] produces a result 

2676 add_to_result_map(None, None, (textclause,), sqltypes.NULLTYPE) 

2677 

2678 # un-escape any \:params 

2679 return BIND_PARAMS_ESC.sub( 

2680 lambda m: m.group(1), 

2681 BIND_PARAMS.sub( 

2682 do_bindparam, self.post_process_text(textclause.text) 

2683 ), 

2684 ) 

2685 

2686 def visit_textual_select( 

2687 self, taf, compound_index=None, asfrom=False, **kw 

2688 ): 

2689 toplevel = not self.stack 

2690 entry = self._default_stack_entry if toplevel else self.stack[-1] 

2691 

2692 new_entry: _CompilerStackEntry = { 

2693 "correlate_froms": set(), 

2694 "asfrom_froms": set(), 

2695 "selectable": taf, 

2696 } 

2697 self.stack.append(new_entry) 

2698 

2699 if taf._independent_ctes: 

2700 self._dispatch_independent_ctes(taf, kw) 

2701 

2702 populate_result_map = ( 

2703 toplevel 

2704 or ( 

2705 compound_index == 0 

2706 and entry.get("need_result_map_for_compound", False) 

2707 ) 

2708 or entry.get("need_result_map_for_nested", False) 

2709 ) 

2710 

2711 if populate_result_map: 

2712 self._ordered_columns = self._textual_ordered_columns = ( 

2713 taf.positional 

2714 ) 

2715 

2716 # enable looser result column matching when the SQL text links to 

2717 # Column objects by name only 

2718 self._loose_column_name_matching = not taf.positional and bool( 

2719 taf.column_args 

2720 ) 

2721 

2722 for c in taf.column_args: 

2723 self.process( 

2724 c, 

2725 within_columns_clause=True, 

2726 add_to_result_map=self._add_to_result_map, 

2727 ) 

2728 

2729 text = self.process(taf.element, **kw) 

2730 if self.ctes: 

2731 nesting_level = len(self.stack) if not toplevel else None 

2732 text = self._render_cte_clause(nesting_level=nesting_level) + text 

2733 

2734 self.stack.pop(-1) 

2735 

2736 return text 

2737 

2738 def visit_null(self, expr, **kw): 

2739 return "NULL" 

2740 

2741 def visit_true(self, expr, **kw): 

2742 if self.dialect.supports_native_boolean: 

2743 return "true" 

2744 else: 

2745 return "1" 

2746 

2747 def visit_false(self, expr, **kw): 

2748 if self.dialect.supports_native_boolean: 

2749 return "false" 

2750 else: 

2751 return "0" 

2752 

2753 def _generate_delimited_list(self, elements, separator, **kw): 

2754 return separator.join( 

2755 s 

2756 for s in (c._compiler_dispatch(self, **kw) for c in elements) 

2757 if s 

2758 ) 

2759 

2760 def _generate_delimited_and_list(self, clauses, **kw): 

2761 lcc, clauses = elements.BooleanClauseList._process_clauses_for_boolean( 

2762 operators.and_, 

2763 elements.True_._singleton, 

2764 elements.False_._singleton, 

2765 clauses, 

2766 ) 

2767 if lcc == 1: 

2768 return clauses[0]._compiler_dispatch(self, **kw) 

2769 else: 

2770 separator = OPERATORS[operators.and_] 

2771 return separator.join( 

2772 s 

2773 for s in (c._compiler_dispatch(self, **kw) for c in clauses) 

2774 if s 

2775 ) 

2776 

2777 def visit_tuple(self, clauselist, **kw): 

2778 return "(%s)" % self.visit_clauselist(clauselist, **kw) 

2779 

2780 def visit_clauselist(self, clauselist, **kw): 

2781 sep = clauselist.operator 

2782 if sep is None: 

2783 sep = " " 

2784 else: 

2785 sep = OPERATORS[clauselist.operator] 

2786 

2787 return self._generate_delimited_list(clauselist.clauses, sep, **kw) 

2788 

2789 def visit_expression_clauselist(self, clauselist, **kw): 

2790 operator_ = clauselist.operator 

2791 

2792 disp = self._get_operator_dispatch( 

2793 operator_, "expression_clauselist", None 

2794 ) 

2795 if disp: 

2796 return disp(clauselist, operator_, **kw) 

2797 

2798 try: 

2799 opstring = OPERATORS[operator_] 

2800 except KeyError as err: 

2801 raise exc.UnsupportedCompilationError(self, operator_) from err 

2802 else: 

2803 kw["_in_operator_expression"] = True 

2804 return self._generate_delimited_list( 

2805 clauselist.clauses, opstring, **kw 

2806 ) 

2807 

2808 def visit_case(self, clause, **kwargs): 

2809 x = "CASE " 

2810 if clause.value is not None: 

2811 x += clause.value._compiler_dispatch(self, **kwargs) + " " 

2812 for cond, result in clause.whens: 

2813 x += ( 

2814 "WHEN " 

2815 + cond._compiler_dispatch(self, **kwargs) 

2816 + " THEN " 

2817 + result._compiler_dispatch(self, **kwargs) 

2818 + " " 

2819 ) 

2820 if clause.else_ is not None: 

2821 x += ( 

2822 "ELSE " + clause.else_._compiler_dispatch(self, **kwargs) + " " 

2823 ) 

2824 x += "END" 

2825 return x 

2826 

2827 def visit_type_coerce(self, type_coerce, **kw): 

2828 return type_coerce.typed_expression._compiler_dispatch(self, **kw) 

2829 

2830 def visit_cast(self, cast, **kwargs): 

2831 type_clause = cast.typeclause._compiler_dispatch(self, **kwargs) 

2832 match = re.match("(.*)( COLLATE .*)", type_clause) 

2833 return "CAST(%s AS %s)%s" % ( 

2834 cast.clause._compiler_dispatch(self, **kwargs), 

2835 match.group(1) if match else type_clause, 

2836 match.group(2) if match else "", 

2837 ) 

2838 

2839 def visit_frame_clause(self, frameclause, **kw): 

2840 

2841 if frameclause.lower_type is elements._FrameClauseType.RANGE_UNBOUNDED: 

2842 left = "UNBOUNDED PRECEDING" 

2843 elif frameclause.lower_type is elements._FrameClauseType.RANGE_CURRENT: 

2844 left = "CURRENT ROW" 

2845 else: 

2846 val = self.process(frameclause.lower_integer_bind, **kw) 

2847 if ( 

2848 frameclause.lower_type 

2849 is elements._FrameClauseType.RANGE_PRECEDING 

2850 ): 

2851 left = f"{val} PRECEDING" 

2852 else: 

2853 left = f"{val} FOLLOWING" 

2854 

2855 if frameclause.upper_type is elements._FrameClauseType.RANGE_UNBOUNDED: 

2856 right = "UNBOUNDED FOLLOWING" 

2857 elif frameclause.upper_type is elements._FrameClauseType.RANGE_CURRENT: 

2858 right = "CURRENT ROW" 

2859 else: 

2860 val = self.process(frameclause.upper_integer_bind, **kw) 

2861 if ( 

2862 frameclause.upper_type 

2863 is elements._FrameClauseType.RANGE_PRECEDING 

2864 ): 

2865 right = f"{val} PRECEDING" 

2866 else: 

2867 right = f"{val} FOLLOWING" 

2868 

2869 return f"{left} AND {right}" 

2870 

2871 def visit_over(self, over, **kwargs): 

2872 text = over.element._compiler_dispatch(self, **kwargs) 

2873 if over.range_ is not None: 

2874 range_ = f"RANGE BETWEEN {self.process(over.range_, **kwargs)}" 

2875 elif over.rows is not None: 

2876 range_ = f"ROWS BETWEEN {self.process(over.rows, **kwargs)}" 

2877 else: 

2878 range_ = None 

2879 

2880 return "%s OVER (%s)" % ( 

2881 text, 

2882 " ".join( 

2883 [ 

2884 "%s BY %s" 

2885 % (word, clause._compiler_dispatch(self, **kwargs)) 

2886 for word, clause in ( 

2887 ("PARTITION", over.partition_by), 

2888 ("ORDER", over.order_by), 

2889 ) 

2890 if clause is not None and len(clause) 

2891 ] 

2892 + ([range_] if range_ else []) 

2893 ), 

2894 ) 

2895 

2896 def visit_withingroup(self, withingroup, **kwargs): 

2897 return "%s WITHIN GROUP (ORDER BY %s)" % ( 

2898 withingroup.element._compiler_dispatch(self, **kwargs), 

2899 withingroup.order_by._compiler_dispatch(self, **kwargs), 

2900 ) 

2901 

2902 def visit_funcfilter(self, funcfilter, **kwargs): 

2903 return "%s FILTER (WHERE %s)" % ( 

2904 funcfilter.func._compiler_dispatch(self, **kwargs), 

2905 funcfilter.criterion._compiler_dispatch(self, **kwargs), 

2906 ) 

2907 

2908 def visit_extract(self, extract, **kwargs): 

2909 field = self.extract_map.get(extract.field, extract.field) 

2910 return "EXTRACT(%s FROM %s)" % ( 

2911 field, 

2912 extract.expr._compiler_dispatch(self, **kwargs), 

2913 ) 

2914 

2915 def visit_scalar_function_column(self, element, **kw): 

2916 compiled_fn = self.visit_function(element.fn, **kw) 

2917 compiled_col = self.visit_column(element, **kw) 

2918 return "(%s).%s" % (compiled_fn, compiled_col) 

2919 

2920 def visit_function( 

2921 self, 

2922 func: Function[Any], 

2923 add_to_result_map: Optional[_ResultMapAppender] = None, 

2924 **kwargs: Any, 

2925 ) -> str: 

2926 if add_to_result_map is not None: 

2927 add_to_result_map(func.name, func.name, (func.name,), func.type) 

2928 

2929 disp = getattr(self, "visit_%s_func" % func.name.lower(), None) 

2930 

2931 text: str 

2932 

2933 if disp: 

2934 text = disp(func, **kwargs) 

2935 else: 

2936 name = FUNCTIONS.get(func._deannotate().__class__, None) 

2937 if name: 

2938 if func._has_args: 

2939 name += "%(expr)s" 

2940 else: 

2941 name = func.name 

2942 name = ( 

2943 self.preparer.quote(name) 

2944 if self.preparer._requires_quotes_illegal_chars(name) 

2945 or isinstance(name, elements.quoted_name) 

2946 else name 

2947 ) 

2948 name = name + "%(expr)s" 

2949 text = ".".join( 

2950 [ 

2951 ( 

2952 self.preparer.quote(tok) 

2953 if self.preparer._requires_quotes_illegal_chars(tok) 

2954 or isinstance(name, elements.quoted_name) 

2955 else tok 

2956 ) 

2957 for tok in func.packagenames 

2958 ] 

2959 + [name] 

2960 ) % {"expr": self.function_argspec(func, **kwargs)} 

2961 

2962 if func._with_ordinality: 

2963 text += " WITH ORDINALITY" 

2964 return text 

2965 

2966 def visit_next_value_func(self, next_value, **kw): 

2967 return self.visit_sequence(next_value.sequence) 

2968 

2969 def visit_sequence(self, sequence, **kw): 

2970 raise NotImplementedError( 

2971 "Dialect '%s' does not support sequence increments." 

2972 % self.dialect.name 

2973 ) 

2974 

2975 def function_argspec(self, func, **kwargs): 

2976 return func.clause_expr._compiler_dispatch(self, **kwargs) 

2977 

2978 def visit_compound_select( 

2979 self, cs, asfrom=False, compound_index=None, **kwargs 

2980 ): 

2981 toplevel = not self.stack 

2982 

2983 compile_state = cs._compile_state_factory(cs, self, **kwargs) 

2984 

2985 if toplevel and not self.compile_state: 

2986 self.compile_state = compile_state 

2987 

2988 compound_stmt = compile_state.statement 

2989 

2990 entry = self._default_stack_entry if toplevel else self.stack[-1] 

2991 need_result_map = toplevel or ( 

2992 not compound_index 

2993 and entry.get("need_result_map_for_compound", False) 

2994 ) 

2995 

2996 # indicates there is already a CompoundSelect in play 

2997 if compound_index == 0: 

2998 entry["select_0"] = cs 

2999 

3000 self.stack.append( 

3001 { 

3002 "correlate_froms": entry["correlate_froms"], 

3003 "asfrom_froms": entry["asfrom_froms"], 

3004 "selectable": cs, 

3005 "compile_state": compile_state, 

3006 "need_result_map_for_compound": need_result_map, 

3007 } 

3008 ) 

3009 

3010 if compound_stmt._independent_ctes: 

3011 self._dispatch_independent_ctes(compound_stmt, kwargs) 

3012 

3013 keyword = self.compound_keywords[cs.keyword] 

3014 

3015 text = (" " + keyword + " ").join( 

3016 ( 

3017 c._compiler_dispatch( 

3018 self, asfrom=asfrom, compound_index=i, **kwargs 

3019 ) 

3020 for i, c in enumerate(cs.selects) 

3021 ) 

3022 ) 

3023 

3024 kwargs["include_table"] = False 

3025 text += self.group_by_clause(cs, **dict(asfrom=asfrom, **kwargs)) 

3026 text += self.order_by_clause(cs, **kwargs) 

3027 if cs._has_row_limiting_clause: 

3028 text += self._row_limit_clause(cs, **kwargs) 

3029 

3030 if self.ctes: 

3031 nesting_level = len(self.stack) if not toplevel else None 

3032 text = ( 

3033 self._render_cte_clause( 

3034 nesting_level=nesting_level, 

3035 include_following_stack=True, 

3036 ) 

3037 + text 

3038 ) 

3039 

3040 self.stack.pop(-1) 

3041 return text 

3042 

3043 def _row_limit_clause(self, cs, **kwargs): 

3044 if cs._fetch_clause is not None: 

3045 return self.fetch_clause(cs, **kwargs) 

3046 else: 

3047 return self.limit_clause(cs, **kwargs) 

3048 

3049 def _get_operator_dispatch(self, operator_, qualifier1, qualifier2): 

3050 attrname = "visit_%s_%s%s" % ( 

3051 operator_.__name__, 

3052 qualifier1, 

3053 "_" + qualifier2 if qualifier2 else "", 

3054 ) 

3055 return getattr(self, attrname, None) 

3056 

3057 def visit_unary( 

3058 self, unary, add_to_result_map=None, result_map_targets=(), **kw 

3059 ): 

3060 if add_to_result_map is not None: 

3061 result_map_targets += (unary,) 

3062 kw["add_to_result_map"] = add_to_result_map 

3063 kw["result_map_targets"] = result_map_targets 

3064 

3065 if unary.operator: 

3066 if unary.modifier: 

3067 raise exc.CompileError( 

3068 "Unary expression does not support operator " 

3069 "and modifier simultaneously" 

3070 ) 

3071 disp = self._get_operator_dispatch( 

3072 unary.operator, "unary", "operator" 

3073 ) 

3074 if disp: 

3075 return disp(unary, unary.operator, **kw) 

3076 else: 

3077 return self._generate_generic_unary_operator( 

3078 unary, OPERATORS[unary.operator], **kw 

3079 ) 

3080 elif unary.modifier: 

3081 disp = self._get_operator_dispatch( 

3082 unary.modifier, "unary", "modifier" 

3083 ) 

3084 if disp: 

3085 return disp(unary, unary.modifier, **kw) 

3086 else: 

3087 return self._generate_generic_unary_modifier( 

3088 unary, OPERATORS[unary.modifier], **kw 

3089 ) 

3090 else: 

3091 raise exc.CompileError( 

3092 "Unary expression has no operator or modifier" 

3093 ) 

3094 

3095 def visit_truediv_binary(self, binary, operator, **kw): 

3096 if self.dialect.div_is_floordiv: 

3097 return ( 

3098 self.process(binary.left, **kw) 

3099 + " / " 

3100 # TODO: would need a fast cast again here, 

3101 # unless we want to use an implicit cast like "+ 0.0" 

3102 + self.process( 

3103 elements.Cast( 

3104 binary.right, 

3105 ( 

3106 binary.right.type 

3107 if binary.right.type._type_affinity 

3108 is sqltypes.Numeric 

3109 else sqltypes.Numeric() 

3110 ), 

3111 ), 

3112 **kw, 

3113 ) 

3114 ) 

3115 else: 

3116 return ( 

3117 self.process(binary.left, **kw) 

3118 + " / " 

3119 + self.process(binary.right, **kw) 

3120 ) 

3121 

3122 def visit_floordiv_binary(self, binary, operator, **kw): 

3123 if ( 

3124 self.dialect.div_is_floordiv 

3125 and binary.right.type._type_affinity is sqltypes.Integer 

3126 ): 

3127 return ( 

3128 self.process(binary.left, **kw) 

3129 + " / " 

3130 + self.process(binary.right, **kw) 

3131 ) 

3132 else: 

3133 return "FLOOR(%s)" % ( 

3134 self.process(binary.left, **kw) 

3135 + " / " 

3136 + self.process(binary.right, **kw) 

3137 ) 

3138 

3139 def visit_is_true_unary_operator(self, element, operator, **kw): 

3140 if ( 

3141 element._is_implicitly_boolean 

3142 or self.dialect.supports_native_boolean 

3143 ): 

3144 return self.process(element.element, **kw) 

3145 else: 

3146 return "%s = 1" % self.process(element.element, **kw) 

3147 

3148 def visit_is_false_unary_operator(self, element, operator, **kw): 

3149 if ( 

3150 element._is_implicitly_boolean 

3151 or self.dialect.supports_native_boolean 

3152 ): 

3153 return "NOT %s" % self.process(element.element, **kw) 

3154 else: 

3155 return "%s = 0" % self.process(element.element, **kw) 

3156 

3157 def visit_not_match_op_binary(self, binary, operator, **kw): 

3158 return "NOT %s" % self.visit_binary( 

3159 binary, override_operator=operators.match_op 

3160 ) 

3161 

3162 def visit_not_in_op_binary(self, binary, operator, **kw): 

3163 # The brackets are required in the NOT IN operation because the empty 

3164 # case is handled using the form "(col NOT IN (null) OR 1 = 1)". 

3165 # The presence of the OR makes the brackets required. 

3166 return "(%s)" % self._generate_generic_binary( 

3167 binary, OPERATORS[operator], **kw 

3168 ) 

3169 

3170 def visit_empty_set_op_expr(self, type_, expand_op, **kw): 

3171 if expand_op is operators.not_in_op: 

3172 if len(type_) > 1: 

3173 return "(%s)) OR (1 = 1" % ( 

3174 ", ".join("NULL" for element in type_) 

3175 ) 

3176 else: 

3177 return "NULL) OR (1 = 1" 

3178 elif expand_op is operators.in_op: 

3179 if len(type_) > 1: 

3180 return "(%s)) AND (1 != 1" % ( 

3181 ", ".join("NULL" for element in type_) 

3182 ) 

3183 else: 

3184 return "NULL) AND (1 != 1" 

3185 else: 

3186 return self.visit_empty_set_expr(type_) 

3187 

3188 def visit_empty_set_expr(self, element_types, **kw): 

3189 raise NotImplementedError( 

3190 "Dialect '%s' does not support empty set expression." 

3191 % self.dialect.name 

3192 ) 

3193 

3194 def _literal_execute_expanding_parameter_literal_binds( 

3195 self, parameter, values, bind_expression_template=None 

3196 ): 

3197 typ_dialect_impl = parameter.type._unwrapped_dialect_impl(self.dialect) 

3198 

3199 if not values: 

3200 # empty IN expression. note we don't need to use 

3201 # bind_expression_template here because there are no 

3202 # expressions to render. 

3203 

3204 if typ_dialect_impl._is_tuple_type: 

3205 replacement_expression = ( 

3206 "VALUES " if self.dialect.tuple_in_values else "" 

3207 ) + self.visit_empty_set_op_expr( 

3208 parameter.type.types, parameter.expand_op 

3209 ) 

3210 

3211 else: 

3212 replacement_expression = self.visit_empty_set_op_expr( 

3213 [parameter.type], parameter.expand_op 

3214 ) 

3215 

3216 elif typ_dialect_impl._is_tuple_type or ( 

3217 typ_dialect_impl._isnull 

3218 and isinstance(values[0], collections_abc.Sequence) 

3219 and not isinstance(values[0], (str, bytes)) 

3220 ): 

3221 if typ_dialect_impl._has_bind_expression: 

3222 raise NotImplementedError( 

3223 "bind_expression() on TupleType not supported with " 

3224 "literal_binds" 

3225 ) 

3226 

3227 replacement_expression = ( 

3228 "VALUES " if self.dialect.tuple_in_values else "" 

3229 ) + ", ".join( 

3230 "(%s)" 

3231 % ( 

3232 ", ".join( 

3233 self.render_literal_value(value, param_type) 

3234 for value, param_type in zip( 

3235 tuple_element, parameter.type.types 

3236 ) 

3237 ) 

3238 ) 

3239 for i, tuple_element in enumerate(values) 

3240 ) 

3241 else: 

3242 if bind_expression_template: 

3243 post_compile_pattern = self._post_compile_pattern 

3244 m = post_compile_pattern.search(bind_expression_template) 

3245 assert m and m.group( 

3246 2 

3247 ), "unexpected format for expanding parameter" 

3248 

3249 tok = m.group(2).split("~~") 

3250 be_left, be_right = tok[1], tok[3] 

3251 replacement_expression = ", ".join( 

3252 "%s%s%s" 

3253 % ( 

3254 be_left, 

3255 self.render_literal_value(value, parameter.type), 

3256 be_right, 

3257 ) 

3258 for value in values 

3259 ) 

3260 else: 

3261 replacement_expression = ", ".join( 

3262 self.render_literal_value(value, parameter.type) 

3263 for value in values 

3264 ) 

3265 

3266 return (), replacement_expression 

3267 

3268 def _literal_execute_expanding_parameter(self, name, parameter, values): 

3269 if parameter.literal_execute: 

3270 return self._literal_execute_expanding_parameter_literal_binds( 

3271 parameter, values 

3272 ) 

3273 

3274 dialect = self.dialect 

3275 typ_dialect_impl = parameter.type._unwrapped_dialect_impl(dialect) 

3276 

3277 if self._numeric_binds: 

3278 bind_template = self.compilation_bindtemplate 

3279 else: 

3280 bind_template = self.bindtemplate 

3281 

3282 if ( 

3283 self.dialect._bind_typing_render_casts 

3284 and typ_dialect_impl.render_bind_cast 

3285 ): 

3286 

3287 def _render_bindtemplate(name): 

3288 return self.render_bind_cast( 

3289 parameter.type, 

3290 typ_dialect_impl, 

3291 bind_template % {"name": name}, 

3292 ) 

3293 

3294 else: 

3295 

3296 def _render_bindtemplate(name): 

3297 return bind_template % {"name": name} 

3298 

3299 if not values: 

3300 to_update = [] 

3301 if typ_dialect_impl._is_tuple_type: 

3302 replacement_expression = self.visit_empty_set_op_expr( 

3303 parameter.type.types, parameter.expand_op 

3304 ) 

3305 else: 

3306 replacement_expression = self.visit_empty_set_op_expr( 

3307 [parameter.type], parameter.expand_op 

3308 ) 

3309 

3310 elif typ_dialect_impl._is_tuple_type or ( 

3311 typ_dialect_impl._isnull 

3312 and isinstance(values[0], collections_abc.Sequence) 

3313 and not isinstance(values[0], (str, bytes)) 

3314 ): 

3315 assert not typ_dialect_impl._is_array 

3316 to_update = [ 

3317 ("%s_%s_%s" % (name, i, j), value) 

3318 for i, tuple_element in enumerate(values, 1) 

3319 for j, value in enumerate(tuple_element, 1) 

3320 ] 

3321 

3322 replacement_expression = ( 

3323 "VALUES " if dialect.tuple_in_values else "" 

3324 ) + ", ".join( 

3325 "(%s)" 

3326 % ( 

3327 ", ".join( 

3328 _render_bindtemplate( 

3329 to_update[i * len(tuple_element) + j][0] 

3330 ) 

3331 for j, value in enumerate(tuple_element) 

3332 ) 

3333 ) 

3334 for i, tuple_element in enumerate(values) 

3335 ) 

3336 else: 

3337 to_update = [ 

3338 ("%s_%s" % (name, i), value) 

3339 for i, value in enumerate(values, 1) 

3340 ] 

3341 replacement_expression = ", ".join( 

3342 _render_bindtemplate(key) for key, value in to_update 

3343 ) 

3344 

3345 return to_update, replacement_expression 

3346 

3347 def visit_binary( 

3348 self, 

3349 binary, 

3350 override_operator=None, 

3351 eager_grouping=False, 

3352 from_linter=None, 

3353 lateral_from_linter=None, 

3354 **kw, 

3355 ): 

3356 if from_linter and operators.is_comparison(binary.operator): 

3357 if lateral_from_linter is not None: 

3358 enclosing_lateral = kw["enclosing_lateral"] 

3359 lateral_from_linter.edges.update( 

3360 itertools.product( 

3361 _de_clone( 

3362 binary.left._from_objects + [enclosing_lateral] 

3363 ), 

3364 _de_clone( 

3365 binary.right._from_objects + [enclosing_lateral] 

3366 ), 

3367 ) 

3368 ) 

3369 else: 

3370 from_linter.edges.update( 

3371 itertools.product( 

3372 _de_clone(binary.left._from_objects), 

3373 _de_clone(binary.right._from_objects), 

3374 ) 

3375 ) 

3376 

3377 # don't allow "? = ?" to render 

3378 if ( 

3379 self.ansi_bind_rules 

3380 and isinstance(binary.left, elements.BindParameter) 

3381 and isinstance(binary.right, elements.BindParameter) 

3382 ): 

3383 kw["literal_execute"] = True 

3384 

3385 operator_ = override_operator or binary.operator 

3386 disp = self._get_operator_dispatch(operator_, "binary", None) 

3387 if disp: 

3388 return disp(binary, operator_, **kw) 

3389 else: 

3390 try: 

3391 opstring = OPERATORS[operator_] 

3392 except KeyError as err: 

3393 raise exc.UnsupportedCompilationError(self, operator_) from err 

3394 else: 

3395 return self._generate_generic_binary( 

3396 binary, 

3397 opstring, 

3398 from_linter=from_linter, 

3399 lateral_from_linter=lateral_from_linter, 

3400 **kw, 

3401 ) 

3402 

3403 def visit_function_as_comparison_op_binary(self, element, operator, **kw): 

3404 return self.process(element.sql_function, **kw) 

3405 

3406 def visit_mod_binary(self, binary, operator, **kw): 

3407 if self.preparer._double_percents: 

3408 return ( 

3409 self.process(binary.left, **kw) 

3410 + " %% " 

3411 + self.process(binary.right, **kw) 

3412 ) 

3413 else: 

3414 return ( 

3415 self.process(binary.left, **kw) 

3416 + " % " 

3417 + self.process(binary.right, **kw) 

3418 ) 

3419 

3420 def visit_custom_op_binary(self, element, operator, **kw): 

3421 kw["eager_grouping"] = operator.eager_grouping 

3422 return self._generate_generic_binary( 

3423 element, 

3424 " " + self.escape_literal_column(operator.opstring) + " ", 

3425 **kw, 

3426 ) 

3427 

3428 def visit_custom_op_unary_operator(self, element, operator, **kw): 

3429 return self._generate_generic_unary_operator( 

3430 element, self.escape_literal_column(operator.opstring) + " ", **kw 

3431 ) 

3432 

3433 def visit_custom_op_unary_modifier(self, element, operator, **kw): 

3434 return self._generate_generic_unary_modifier( 

3435 element, " " + self.escape_literal_column(operator.opstring), **kw 

3436 ) 

3437 

3438 def _generate_generic_binary( 

3439 self, binary, opstring, eager_grouping=False, **kw 

3440 ): 

3441 _in_operator_expression = kw.get("_in_operator_expression", False) 

3442 

3443 kw["_in_operator_expression"] = True 

3444 kw["_binary_op"] = binary.operator 

3445 text = ( 

3446 binary.left._compiler_dispatch( 

3447 self, eager_grouping=eager_grouping, **kw 

3448 ) 

3449 + opstring 

3450 + binary.right._compiler_dispatch( 

3451 self, eager_grouping=eager_grouping, **kw 

3452 ) 

3453 ) 

3454 

3455 if _in_operator_expression and eager_grouping: 

3456 text = "(%s)" % text 

3457 return text 

3458 

3459 def _generate_generic_unary_operator(self, unary, opstring, **kw): 

3460 return opstring + unary.element._compiler_dispatch(self, **kw) 

3461 

3462 def _generate_generic_unary_modifier(self, unary, opstring, **kw): 

3463 return unary.element._compiler_dispatch(self, **kw) + opstring 

3464 

3465 @util.memoized_property 

3466 def _like_percent_literal(self): 

3467 return elements.literal_column("'%'", type_=sqltypes.STRINGTYPE) 

3468 

3469 def visit_ilike_case_insensitive_operand(self, element, **kw): 

3470 return f"lower({element.element._compiler_dispatch(self, **kw)})" 

3471 

3472 def visit_contains_op_binary(self, binary, operator, **kw): 

3473 binary = binary._clone() 

3474 percent = self._like_percent_literal 

3475 binary.right = percent.concat(binary.right).concat(percent) 

3476 return self.visit_like_op_binary(binary, operator, **kw) 

3477 

3478 def visit_not_contains_op_binary(self, binary, operator, **kw): 

3479 binary = binary._clone() 

3480 percent = self._like_percent_literal 

3481 binary.right = percent.concat(binary.right).concat(percent) 

3482 return self.visit_not_like_op_binary(binary, operator, **kw) 

3483 

3484 def visit_icontains_op_binary(self, binary, operator, **kw): 

3485 binary = binary._clone() 

3486 percent = self._like_percent_literal 

3487 binary.left = ilike_case_insensitive(binary.left) 

3488 binary.right = percent.concat( 

3489 ilike_case_insensitive(binary.right) 

3490 ).concat(percent) 

3491 return self.visit_ilike_op_binary(binary, operator, **kw) 

3492 

3493 def visit_not_icontains_op_binary(self, binary, operator, **kw): 

3494 binary = binary._clone() 

3495 percent = self._like_percent_literal 

3496 binary.left = ilike_case_insensitive(binary.left) 

3497 binary.right = percent.concat( 

3498 ilike_case_insensitive(binary.right) 

3499 ).concat(percent) 

3500 return self.visit_not_ilike_op_binary(binary, operator, **kw) 

3501 

3502 def visit_startswith_op_binary(self, binary, operator, **kw): 

3503 binary = binary._clone() 

3504 percent = self._like_percent_literal 

3505 binary.right = percent._rconcat(binary.right) 

3506 return self.visit_like_op_binary(binary, operator, **kw) 

3507 

3508 def visit_not_startswith_op_binary(self, binary, operator, **kw): 

3509 binary = binary._clone() 

3510 percent = self._like_percent_literal 

3511 binary.right = percent._rconcat(binary.right) 

3512 return self.visit_not_like_op_binary(binary, operator, **kw) 

3513 

3514 def visit_istartswith_op_binary(self, binary, operator, **kw): 

3515 binary = binary._clone() 

3516 percent = self._like_percent_literal 

3517 binary.left = ilike_case_insensitive(binary.left) 

3518 binary.right = percent._rconcat(ilike_case_insensitive(binary.right)) 

3519 return self.visit_ilike_op_binary(binary, operator, **kw) 

3520 

3521 def visit_not_istartswith_op_binary(self, binary, operator, **kw): 

3522 binary = binary._clone() 

3523 percent = self._like_percent_literal 

3524 binary.left = ilike_case_insensitive(binary.left) 

3525 binary.right = percent._rconcat(ilike_case_insensitive(binary.right)) 

3526 return self.visit_not_ilike_op_binary(binary, operator, **kw) 

3527 

3528 def visit_endswith_op_binary(self, binary, operator, **kw): 

3529 binary = binary._clone() 

3530 percent = self._like_percent_literal 

3531 binary.right = percent.concat(binary.right) 

3532 return self.visit_like_op_binary(binary, operator, **kw) 

3533 

3534 def visit_not_endswith_op_binary(self, binary, operator, **kw): 

3535 binary = binary._clone() 

3536 percent = self._like_percent_literal 

3537 binary.right = percent.concat(binary.right) 

3538 return self.visit_not_like_op_binary(binary, operator, **kw) 

3539 

3540 def visit_iendswith_op_binary(self, binary, operator, **kw): 

3541 binary = binary._clone() 

3542 percent = self._like_percent_literal 

3543 binary.left = ilike_case_insensitive(binary.left) 

3544 binary.right = percent.concat(ilike_case_insensitive(binary.right)) 

3545 return self.visit_ilike_op_binary(binary, operator, **kw) 

3546 

3547 def visit_not_iendswith_op_binary(self, binary, operator, **kw): 

3548 binary = binary._clone() 

3549 percent = self._like_percent_literal 

3550 binary.left = ilike_case_insensitive(binary.left) 

3551 binary.right = percent.concat(ilike_case_insensitive(binary.right)) 

3552 return self.visit_not_ilike_op_binary(binary, operator, **kw) 

3553 

3554 def visit_like_op_binary(self, binary, operator, **kw): 

3555 escape = binary.modifiers.get("escape", None) 

3556 

3557 return "%s LIKE %s" % ( 

3558 binary.left._compiler_dispatch(self, **kw), 

3559 binary.right._compiler_dispatch(self, **kw), 

3560 ) + ( 

3561 " ESCAPE " + self.render_literal_value(escape, sqltypes.STRINGTYPE) 

3562 if escape is not None 

3563 else "" 

3564 ) 

3565 

3566 def visit_not_like_op_binary(self, binary, operator, **kw): 

3567 escape = binary.modifiers.get("escape", None) 

3568 return "%s NOT LIKE %s" % ( 

3569 binary.left._compiler_dispatch(self, **kw), 

3570 binary.right._compiler_dispatch(self, **kw), 

3571 ) + ( 

3572 " ESCAPE " + self.render_literal_value(escape, sqltypes.STRINGTYPE) 

3573 if escape is not None 

3574 else "" 

3575 ) 

3576 

3577 def visit_ilike_op_binary(self, binary, operator, **kw): 

3578 if operator is operators.ilike_op: 

3579 binary = binary._clone() 

3580 binary.left = ilike_case_insensitive(binary.left) 

3581 binary.right = ilike_case_insensitive(binary.right) 

3582 # else we assume ilower() has been applied 

3583 

3584 return self.visit_like_op_binary(binary, operator, **kw) 

3585 

3586 def visit_not_ilike_op_binary(self, binary, operator, **kw): 

3587 if operator is operators.not_ilike_op: 

3588 binary = binary._clone() 

3589 binary.left = ilike_case_insensitive(binary.left) 

3590 binary.right = ilike_case_insensitive(binary.right) 

3591 # else we assume ilower() has been applied 

3592 

3593 return self.visit_not_like_op_binary(binary, operator, **kw) 

3594 

3595 def visit_between_op_binary(self, binary, operator, **kw): 

3596 symmetric = binary.modifiers.get("symmetric", False) 

3597 return self._generate_generic_binary( 

3598 binary, " BETWEEN SYMMETRIC " if symmetric else " BETWEEN ", **kw 

3599 ) 

3600 

3601 def visit_not_between_op_binary(self, binary, operator, **kw): 

3602 symmetric = binary.modifiers.get("symmetric", False) 

3603 return self._generate_generic_binary( 

3604 binary, 

3605 " NOT BETWEEN SYMMETRIC " if symmetric else " NOT BETWEEN ", 

3606 **kw, 

3607 ) 

3608 

3609 def visit_regexp_match_op_binary(self, binary, operator, **kw): 

3610 raise exc.CompileError( 

3611 "%s dialect does not support regular expressions" 

3612 % self.dialect.name 

3613 ) 

3614 

3615 def visit_not_regexp_match_op_binary(self, binary, operator, **kw): 

3616 raise exc.CompileError( 

3617 "%s dialect does not support regular expressions" 

3618 % self.dialect.name 

3619 ) 

3620 

3621 def visit_regexp_replace_op_binary(self, binary, operator, **kw): 

3622 raise exc.CompileError( 

3623 "%s dialect does not support regular expression replacements" 

3624 % self.dialect.name 

3625 ) 

3626 

3627 def visit_bindparam( 

3628 self, 

3629 bindparam, 

3630 within_columns_clause=False, 

3631 literal_binds=False, 

3632 skip_bind_expression=False, 

3633 literal_execute=False, 

3634 render_postcompile=False, 

3635 **kwargs, 

3636 ): 

3637 

3638 if not skip_bind_expression: 

3639 impl = bindparam.type.dialect_impl(self.dialect) 

3640 if impl._has_bind_expression: 

3641 bind_expression = impl.bind_expression(bindparam) 

3642 wrapped = self.process( 

3643 bind_expression, 

3644 skip_bind_expression=True, 

3645 within_columns_clause=within_columns_clause, 

3646 literal_binds=literal_binds and not bindparam.expanding, 

3647 literal_execute=literal_execute, 

3648 render_postcompile=render_postcompile, 

3649 **kwargs, 

3650 ) 

3651 if bindparam.expanding: 

3652 # for postcompile w/ expanding, move the "wrapped" part 

3653 # of this into the inside 

3654 

3655 m = re.match( 

3656 r"^(.*)\(__\[POSTCOMPILE_(\S+?)\]\)(.*)$", wrapped 

3657 ) 

3658 assert m, "unexpected format for expanding parameter" 

3659 wrapped = "(__[POSTCOMPILE_%s~~%s~~REPL~~%s~~])" % ( 

3660 m.group(2), 

3661 m.group(1), 

3662 m.group(3), 

3663 ) 

3664 

3665 if literal_binds: 

3666 ret = self.render_literal_bindparam( 

3667 bindparam, 

3668 within_columns_clause=True, 

3669 bind_expression_template=wrapped, 

3670 **kwargs, 

3671 ) 

3672 return f"({ret})" 

3673 

3674 return wrapped 

3675 

3676 if not literal_binds: 

3677 literal_execute = ( 

3678 literal_execute 

3679 or bindparam.literal_execute 

3680 or (within_columns_clause and self.ansi_bind_rules) 

3681 ) 

3682 post_compile = literal_execute or bindparam.expanding 

3683 else: 

3684 post_compile = False 

3685 

3686 if literal_binds: 

3687 ret = self.render_literal_bindparam( 

3688 bindparam, within_columns_clause=True, **kwargs 

3689 ) 

3690 if bindparam.expanding: 

3691 ret = f"({ret})" 

3692 return ret 

3693 

3694 name = self._truncate_bindparam(bindparam) 

3695 

3696 if name in self.binds: 

3697 existing = self.binds[name] 

3698 if existing is not bindparam: 

3699 if ( 

3700 (existing.unique or bindparam.unique) 

3701 and not existing.proxy_set.intersection( 

3702 bindparam.proxy_set 

3703 ) 

3704 and not existing._cloned_set.intersection( 

3705 bindparam._cloned_set 

3706 ) 

3707 ): 

3708 raise exc.CompileError( 

3709 "Bind parameter '%s' conflicts with " 

3710 "unique bind parameter of the same name" % name 

3711 ) 

3712 elif existing.expanding != bindparam.expanding: 

3713 raise exc.CompileError( 

3714 "Can't reuse bound parameter name '%s' in both " 

3715 "'expanding' (e.g. within an IN expression) and " 

3716 "non-expanding contexts. If this parameter is to " 

3717 "receive a list/array value, set 'expanding=True' on " 

3718 "it for expressions that aren't IN, otherwise use " 

3719 "a different parameter name." % (name,) 

3720 ) 

3721 elif existing._is_crud or bindparam._is_crud: 

3722 if existing._is_crud and bindparam._is_crud: 

3723 # TODO: this condition is not well understood. 

3724 # see tests in test/sql/test_update.py 

3725 raise exc.CompileError( 

3726 "Encountered unsupported case when compiling an " 

3727 "INSERT or UPDATE statement. If this is a " 

3728 "multi-table " 

3729 "UPDATE statement, please provide string-named " 

3730 "arguments to the " 

3731 "values() method with distinct names; support for " 

3732 "multi-table UPDATE statements that " 

3733 "target multiple tables for UPDATE is very " 

3734 "limited", 

3735 ) 

3736 else: 

3737 raise exc.CompileError( 

3738 f"bindparam() name '{bindparam.key}' is reserved " 

3739 "for automatic usage in the VALUES or SET " 

3740 "clause of this " 

3741 "insert/update statement. Please use a " 

3742 "name other than column name when using " 

3743 "bindparam() " 

3744 "with insert() or update() (for example, " 

3745 f"'b_{bindparam.key}')." 

3746 ) 

3747 

3748 self.binds[bindparam.key] = self.binds[name] = bindparam 

3749 

3750 # if we are given a cache key that we're going to match against, 

3751 # relate the bindparam here to one that is most likely present 

3752 # in the "extracted params" portion of the cache key. this is used 

3753 # to set up a positional mapping that is used to determine the 

3754 # correct parameters for a subsequent use of this compiled with 

3755 # a different set of parameter values. here, we accommodate for 

3756 # parameters that may have been cloned both before and after the cache 

3757 # key was been generated. 

3758 ckbm_tuple = self._cache_key_bind_match 

3759 

3760 if ckbm_tuple: 

3761 ckbm, cksm = ckbm_tuple 

3762 for bp in bindparam._cloned_set: 

3763 if bp.key in cksm: 

3764 cb = cksm[bp.key] 

3765 ckbm[cb].append(bindparam) 

3766 

3767 if bindparam.isoutparam: 

3768 self.has_out_parameters = True 

3769 

3770 if post_compile: 

3771 if render_postcompile: 

3772 self._render_postcompile = True 

3773 

3774 if literal_execute: 

3775 self.literal_execute_params |= {bindparam} 

3776 else: 

3777 self.post_compile_params |= {bindparam} 

3778 

3779 ret = self.bindparam_string( 

3780 name, 

3781 post_compile=post_compile, 

3782 expanding=bindparam.expanding, 

3783 bindparam_type=bindparam.type, 

3784 **kwargs, 

3785 ) 

3786 

3787 if bindparam.expanding: 

3788 ret = f"({ret})" 

3789 

3790 return ret 

3791 

3792 def render_bind_cast(self, type_, dbapi_type, sqltext): 

3793 raise NotImplementedError() 

3794 

3795 def render_literal_bindparam( 

3796 self, 

3797 bindparam, 

3798 render_literal_value=NO_ARG, 

3799 bind_expression_template=None, 

3800 **kw, 

3801 ): 

3802 if render_literal_value is not NO_ARG: 

3803 value = render_literal_value 

3804 else: 

3805 if bindparam.value is None and bindparam.callable is None: 

3806 op = kw.get("_binary_op", None) 

3807 if op and op not in (operators.is_, operators.is_not): 

3808 util.warn_limited( 

3809 "Bound parameter '%s' rendering literal NULL in a SQL " 

3810 "expression; comparisons to NULL should not use " 

3811 "operators outside of 'is' or 'is not'", 

3812 (bindparam.key,), 

3813 ) 

3814 return self.process(sqltypes.NULLTYPE, **kw) 

3815 value = bindparam.effective_value 

3816 

3817 if bindparam.expanding: 

3818 leep = self._literal_execute_expanding_parameter_literal_binds 

3819 to_update, replacement_expr = leep( 

3820 bindparam, 

3821 value, 

3822 bind_expression_template=bind_expression_template, 

3823 ) 

3824 return replacement_expr 

3825 else: 

3826 return self.render_literal_value(value, bindparam.type) 

3827 

3828 def render_literal_value(self, value, type_): 

3829 """Render the value of a bind parameter as a quoted literal. 

3830 

3831 This is used for statement sections that do not accept bind parameters 

3832 on the target driver/database. 

3833 

3834 This should be implemented by subclasses using the quoting services 

3835 of the DBAPI. 

3836 

3837 """ 

3838 

3839 if value is None and not type_.should_evaluate_none: 

3840 # issue #10535 - handle NULL in the compiler without placing 

3841 # this onto each type, except for "evaluate None" types 

3842 # (e.g. JSON) 

3843 return self.process(elements.Null._instance()) 

3844 

3845 processor = type_._cached_literal_processor(self.dialect) 

3846 if processor: 

3847 try: 

3848 return processor(value) 

3849 except Exception as e: 

3850 raise exc.CompileError( 

3851 f"Could not render literal value " 

3852 f'"{sql_util._repr_single_value(value)}" ' 

3853 f"with datatype " 

3854 f"{type_}; see parent stack trace for " 

3855 "more detail." 

3856 ) from e 

3857 

3858 else: 

3859 raise exc.CompileError( 

3860 f"No literal value renderer is available for literal value " 

3861 f'"{sql_util._repr_single_value(value)}" ' 

3862 f"with datatype {type_}" 

3863 ) 

3864 

3865 def _truncate_bindparam(self, bindparam): 

3866 if bindparam in self.bind_names: 

3867 return self.bind_names[bindparam] 

3868 

3869 bind_name = bindparam.key 

3870 if isinstance(bind_name, elements._truncated_label): 

3871 bind_name = self._truncated_identifier("bindparam", bind_name) 

3872 

3873 # add to bind_names for translation 

3874 self.bind_names[bindparam] = bind_name 

3875 

3876 return bind_name 

3877 

3878 def _truncated_identifier( 

3879 self, ident_class: str, name: _truncated_label 

3880 ) -> str: 

3881 if (ident_class, name) in self.truncated_names: 

3882 return self.truncated_names[(ident_class, name)] 

3883 

3884 anonname = name.apply_map(self.anon_map) 

3885 

3886 if len(anonname) > self.label_length - 6: 

3887 counter = self._truncated_counters.get(ident_class, 1) 

3888 truncname = ( 

3889 anonname[0 : max(self.label_length - 6, 0)] 

3890 + "_" 

3891 + hex(counter)[2:] 

3892 ) 

3893 self._truncated_counters[ident_class] = counter + 1 

3894 else: 

3895 truncname = anonname 

3896 self.truncated_names[(ident_class, name)] = truncname 

3897 return truncname 

3898 

3899 def _anonymize(self, name: str) -> str: 

3900 return name % self.anon_map 

3901 

3902 def bindparam_string( 

3903 self, 

3904 name: str, 

3905 post_compile: bool = False, 

3906 expanding: bool = False, 

3907 escaped_from: Optional[str] = None, 

3908 bindparam_type: Optional[TypeEngine[Any]] = None, 

3909 accumulate_bind_names: Optional[Set[str]] = None, 

3910 visited_bindparam: Optional[List[str]] = None, 

3911 **kw: Any, 

3912 ) -> str: 

3913 # TODO: accumulate_bind_names is passed by crud.py to gather 

3914 # names on a per-value basis, visited_bindparam is passed by 

3915 # visit_insert() to collect all parameters in the statement. 

3916 # see if this gathering can be simplified somehow 

3917 if accumulate_bind_names is not None: 

3918 accumulate_bind_names.add(name) 

3919 if visited_bindparam is not None: 

3920 visited_bindparam.append(name) 

3921 

3922 if not escaped_from: 

3923 if self._bind_translate_re.search(name): 

3924 # not quite the translate use case as we want to 

3925 # also get a quick boolean if we even found 

3926 # unusual characters in the name 

3927 new_name = self._bind_translate_re.sub( 

3928 lambda m: self._bind_translate_chars[m.group(0)], 

3929 name, 

3930 ) 

3931 escaped_from = name 

3932 name = new_name 

3933 

3934 if escaped_from: 

3935 self.escaped_bind_names = self.escaped_bind_names.union( 

3936 {escaped_from: name} 

3937 ) 

3938 if post_compile: 

3939 ret = "__[POSTCOMPILE_%s]" % name 

3940 if expanding: 

3941 # for expanding, bound parameters or literal values will be 

3942 # rendered per item 

3943 return ret 

3944 

3945 # otherwise, for non-expanding "literal execute", apply 

3946 # bind casts as determined by the datatype 

3947 if bindparam_type is not None: 

3948 type_impl = bindparam_type._unwrapped_dialect_impl( 

3949 self.dialect 

3950 ) 

3951 if type_impl.render_literal_cast: 

3952 ret = self.render_bind_cast(bindparam_type, type_impl, ret) 

3953 return ret 

3954 elif self.state is CompilerState.COMPILING: 

3955 ret = self.compilation_bindtemplate % {"name": name} 

3956 else: 

3957 ret = self.bindtemplate % {"name": name} 

3958 

3959 if ( 

3960 bindparam_type is not None 

3961 and self.dialect._bind_typing_render_casts 

3962 ): 

3963 type_impl = bindparam_type._unwrapped_dialect_impl(self.dialect) 

3964 if type_impl.render_bind_cast: 

3965 ret = self.render_bind_cast(bindparam_type, type_impl, ret) 

3966 

3967 return ret 

3968 

3969 def _dispatch_independent_ctes(self, stmt, kw): 

3970 local_kw = kw.copy() 

3971 local_kw.pop("cte_opts", None) 

3972 for cte, opt in zip( 

3973 stmt._independent_ctes, stmt._independent_ctes_opts 

3974 ): 

3975 cte._compiler_dispatch(self, cte_opts=opt, **local_kw) 

3976