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

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

3311 statements  

1# sql/compiler.py 

2# Copyright (C) 2005-2026 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""" 

26 

27from __future__ import annotations 

28 

29import collections 

30import collections.abc as collections_abc 

31import contextlib 

32from enum import IntEnum 

33import functools 

34import itertools 

35import operator 

36import re 

37from time import perf_counter 

38import typing 

39from typing import AbstractSet 

40from typing import Any 

41from typing import Callable 

42from typing import cast 

43from typing import ClassVar 

44from typing import Dict 

45from typing import Final 

46from typing import FrozenSet 

47from typing import Iterable 

48from typing import Iterator 

49from typing import List 

50from typing import Literal 

51from typing import Mapping 

52from typing import MutableMapping 

53from typing import NamedTuple 

54from typing import NoReturn 

55from typing import Optional 

56from typing import Pattern 

57from typing import Protocol 

58from typing import Sequence 

59from typing import Set 

60from typing import Tuple 

61from typing import Type 

62from typing import TYPE_CHECKING 

63from typing import TypedDict 

64from typing import Union 

65 

66from . import base 

67from . import coercions 

68from . import crud 

69from . import elements 

70from . import functions 

71from . import operators 

72from . import roles 

73from . import schema 

74from . import selectable 

75from . import sqltypes 

76from . import util as sql_util 

77from ._typing import is_column_element 

78from ._typing import is_dml 

79from .base import _de_clone 

80from .base import _from_objects 

81from .base import _NONE_NAME 

82from .base import _SentinelDefaultCharacterization 

83from .base import NO_ARG 

84from .elements import quoted_name 

85from .sqltypes import TupleType 

86from .visitors import prefix_anon_map 

87from .. import exc 

88from .. import util 

89from ..util import FastIntFlag 

90from ..util.typing import Self 

91from ..util.typing import TupleAny 

92from ..util.typing import Unpack 

93 

94if typing.TYPE_CHECKING: 

95 from .annotation import _AnnotationDict 

96 from .base import _AmbiguousTableNameMap 

97 from .base import CompileState 

98 from .base import Executable 

99 from .base import ExecutableStatement 

100 from .cache_key import CacheKey 

101 from .ddl import _TableViaSelect 

102 from .ddl import CreateTableAs 

103 from .ddl import CreateView 

104 from .ddl import ExecutableDDLElement 

105 from .dml import Delete 

106 from .dml import Insert 

107 from .dml import Update 

108 from .dml import UpdateBase 

109 from .dml import UpdateDMLState 

110 from .dml import ValuesBase 

111 from .elements import _truncated_label 

112 from .elements import BinaryExpression 

113 from .elements import BindParameter 

114 from .elements import ClauseElement 

115 from .elements import ColumnClause 

116 from .elements import ColumnElement 

117 from .elements import False_ 

118 from .elements import Label 

119 from .elements import Null 

120 from .elements import True_ 

121 from .functions import Function 

122 from .schema import CheckConstraint 

123 from .schema import Column 

124 from .schema import Constraint 

125 from .schema import ForeignKeyConstraint 

126 from .schema import IdentityOptions 

127 from .schema import Index 

128 from .schema import PrimaryKeyConstraint 

129 from .schema import Table 

130 from .schema import UniqueConstraint 

131 from .selectable import _ColumnsClauseElement 

132 from .selectable import AliasedReturnsRows 

133 from .selectable import CompoundSelectState 

134 from .selectable import CTE 

135 from .selectable import FromClause 

136 from .selectable import NamedFromClause 

137 from .selectable import ReturnsRows 

138 from .selectable import Select 

139 from .selectable import SelectState 

140 from .type_api import _BindProcessorType 

141 from .type_api import TypeDecorator 

142 from .type_api import TypeEngine 

143 from .type_api import UserDefinedType 

144 from .visitors import Visitable 

145 from ..engine.cursor import CursorResultMetaData 

146 from ..engine.interfaces import _CoreSingleExecuteParams 

147 from ..engine.interfaces import _DBAPIAnyExecuteParams 

148 from ..engine.interfaces import _DBAPIMultiExecuteParams 

149 from ..engine.interfaces import _DBAPISingleExecuteParams 

150 from ..engine.interfaces import _ExecuteOptions 

151 from ..engine.interfaces import _GenericSetInputSizesType 

152 from ..engine.interfaces import _MutableCoreSingleExecuteParams 

153 from ..engine.interfaces import Dialect 

154 from ..engine.interfaces import SchemaTranslateMapType 

155 

156 

157_FromHintsType = Dict["FromClause", str] 

158 

159RESERVED_WORDS = { 

160 "all", 

161 "analyse", 

162 "analyze", 

163 "and", 

164 "any", 

165 "array", 

166 "as", 

167 "asc", 

168 "asymmetric", 

169 "authorization", 

170 "between", 

171 "binary", 

172 "both", 

173 "case", 

174 "cast", 

175 "check", 

176 "collate", 

177 "column", 

178 "constraint", 

179 "create", 

180 "cross", 

181 "current_date", 

182 "current_role", 

183 "current_time", 

184 "current_timestamp", 

185 "current_user", 

186 "default", 

187 "deferrable", 

188 "desc", 

189 "distinct", 

190 "do", 

191 "else", 

192 "end", 

193 "except", 

194 "false", 

195 "for", 

196 "foreign", 

197 "freeze", 

198 "from", 

199 "full", 

200 "grant", 

201 "group", 

202 "having", 

203 "ilike", 

204 "in", 

205 "initially", 

206 "inner", 

207 "intersect", 

208 "into", 

209 "is", 

210 "isnull", 

211 "join", 

212 "leading", 

213 "left", 

214 "like", 

215 "limit", 

216 "localtime", 

217 "localtimestamp", 

218 "natural", 

219 "new", 

220 "not", 

221 "notnull", 

222 "null", 

223 "off", 

224 "offset", 

225 "old", 

226 "on", 

227 "only", 

228 "or", 

229 "order", 

230 "outer", 

231 "overlaps", 

232 "placing", 

233 "primary", 

234 "references", 

235 "right", 

236 "select", 

237 "session_user", 

238 "set", 

239 "similar", 

240 "some", 

241 "symmetric", 

242 "table", 

243 "then", 

244 "to", 

245 "trailing", 

246 "true", 

247 "union", 

248 "unique", 

249 "user", 

250 "using", 

251 "verbose", 

252 "when", 

253 "where", 

254} 

255 

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

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

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

259 

260FK_ON_DELETE = re.compile( 

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

262) 

263FK_ON_UPDATE = re.compile( 

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

265) 

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

267_WINDOW_EXCLUDE_RE = re.compile( 

268 r"^(?:CURRENT ROW|GROUP|TIES|NO OTHERS)$", re.I 

269) 

270BIND_PARAMS = elements.TextClause._bind_params_regex 

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

272 

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

274BIND_TEMPLATES = { 

275 "pyformat": _pyformat_template, 

276 "qmark": "?", 

277 "format": "%%s", 

278 "numeric": ":[_POSITION]", 

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

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

281} 

282 

283 

284OPERATORS = { 

285 # binary 

286 operators.and_: " AND ", 

287 operators.or_: " OR ", 

288 operators.add: " + ", 

289 operators.mul: " * ", 

290 operators.sub: " - ", 

291 operators.mod: " % ", 

292 operators.neg: "-", 

293 operators.lt: " < ", 

294 operators.le: " <= ", 

295 operators.ne: " != ", 

296 operators.gt: " > ", 

297 operators.ge: " >= ", 

298 operators.eq: " = ", 

299 operators.is_distinct_from: " IS DISTINCT FROM ", 

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

301 operators.concat_op: " || ", 

302 operators.match_op: " MATCH ", 

303 operators.not_match_op: " NOT MATCH ", 

304 operators.in_op: " IN ", 

305 operators.not_in_op: " NOT IN ", 

306 operators.comma_op: ", ", 

307 operators.from_: " FROM ", 

308 operators.as_: " AS ", 

309 operators.is_: " IS ", 

310 operators.is_not: " IS NOT ", 

311 operators.collate: " COLLATE ", 

312 # unary 

313 operators.exists: "EXISTS ", 

314 operators.distinct_op: "DISTINCT ", 

315 operators.inv: "NOT ", 

316 operators.any_op: "ANY ", 

317 operators.all_op: "ALL ", 

318 # modifiers 

319 operators.desc_op: " DESC", 

320 operators.asc_op: " ASC", 

321 operators.nulls_first_op: " NULLS FIRST", 

322 operators.nulls_last_op: " NULLS LAST", 

323 # bitwise 

324 operators.bitwise_xor_op: " ^ ", 

325 operators.bitwise_or_op: " | ", 

326 operators.bitwise_and_op: " & ", 

327 operators.bitwise_not_op: "~", 

328 operators.bitwise_lshift_op: " << ", 

329 operators.bitwise_rshift_op: " >> ", 

330} 

331 

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

333 functions.coalesce: "coalesce", 

334 functions.current_date: "CURRENT_DATE", 

335 functions.current_time: "CURRENT_TIME", 

336 functions.current_timestamp: "CURRENT_TIMESTAMP", 

337 functions.current_user: "CURRENT_USER", 

338 functions.localtime: "LOCALTIME", 

339 functions.localtimestamp: "LOCALTIMESTAMP", 

340 functions.random: "random", 

341 functions.sysdate: "sysdate", 

342 functions.session_user: "SESSION_USER", 

343 functions.user: "USER", 

344 functions.cube: "CUBE", 

345 functions.rollup: "ROLLUP", 

346 functions.grouping_sets: "GROUPING SETS", 

347} 

348 

349 

350EXTRACT_MAP = { 

351 "month": "month", 

352 "day": "day", 

353 "year": "year", 

354 "second": "second", 

355 "hour": "hour", 

356 "doy": "doy", 

357 "minute": "minute", 

358 "quarter": "quarter", 

359 "dow": "dow", 

360 "week": "week", 

361 "epoch": "epoch", 

362 "milliseconds": "milliseconds", 

363 "microseconds": "microseconds", 

364 "timezone_hour": "timezone_hour", 

365 "timezone_minute": "timezone_minute", 

366} 

367 

368COMPOUND_KEYWORDS = { 

369 selectable._CompoundSelectKeyword.UNION: "UNION", 

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

371 selectable._CompoundSelectKeyword.EXCEPT: "EXCEPT", 

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

373 selectable._CompoundSelectKeyword.INTERSECT: "INTERSECT", 

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

375} 

376 

377 

378class ResultColumnsEntry(NamedTuple): 

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

380 in the result rows for this statement. 

381 

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

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

384 emulations. 

385 

386 """ 

387 

388 keyname: str 

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

390 

391 name: str 

392 """column name, may be labeled""" 

393 

394 objects: Tuple[Any, ...] 

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

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

397 as well as Column objects. 

398 

399 """ 

400 

401 type: TypeEngine[Any] 

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

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

404 to the rows that come back from the cursor. 

405 

406 """ 

407 

408 

409class _ResultMapAppender(Protocol): 

410 def __call__( 

411 self, 

412 keyname: str, 

413 name: str, 

414 objects: Sequence[Any], 

415 type_: TypeEngine[Any], 

416 ) -> None: ... 

417 

418 

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

420# some profiling showed integer access faster than named tuple 

421RM_RENDERED_NAME: Literal[0] = 0 

422RM_NAME: Literal[1] = 1 

423RM_OBJECTS: Literal[2] = 2 

424RM_TYPE: Literal[3] = 3 

425 

426 

427class _BaseCompilerStackEntry(TypedDict): 

428 asfrom_froms: Set[FromClause] 

429 correlate_froms: Set[FromClause] 

430 selectable: ReturnsRows 

431 

432 

433class _CompilerStackEntry(_BaseCompilerStackEntry, total=False): 

434 compile_state: CompileState 

435 need_result_map_for_nested: bool 

436 need_result_map_for_compound: bool 

437 select_0: ReturnsRows 

438 insert_from_select: Select[Unpack[TupleAny]] 

439 

440 

441class ExpandedState(NamedTuple): 

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

443 "post compile" bound parameters for a statement. 

444 

445 "expanded" parameters are parameters that are generated at 

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

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

448 

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

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

451 being passed as separate parameters to the driver. 

452 

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

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

455 :class:`.SQLCompiler` instance. 

456 

457 """ 

458 

459 statement: str 

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

461 

462 parameters: _CoreSingleExecuteParams 

463 """Parameter dictionary with parameters fully expanded. 

464 

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

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

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

468 will yield a tuple with the positional parameter set. 

469 

470 """ 

471 

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

473 """mapping of bound value processors""" 

474 

475 positiontup: Optional[Sequence[str]] 

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

477 parameters""" 

478 

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

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

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

482 were expanded.""" 

483 

484 @property 

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

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

487 using a positional paramstyle. 

488 

489 """ 

490 if self.positiontup is None: 

491 raise exc.InvalidRequestError( 

492 "statement does not use a positional paramstyle" 

493 ) 

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

495 

496 @property 

497 def additional_parameters(self) -> _CoreSingleExecuteParams: 

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

499 return self.parameters 

500 

501 

502class _InsertManyValues(NamedTuple): 

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

504 

505 The primary consumers of this object are the 

506 :meth:`.SQLCompiler._deliver_insertmanyvalues_batches` and 

507 :meth:`.DefaultDialect._deliver_insertmanyvalues_batches` methods. 

508 

509 .. versionadded:: 2.0 

510 

511 """ 

512 

513 is_default_expr: bool 

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

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

516 

517 """ 

518 

519 single_values_expr: str 

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

521 

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

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

524 target. 

525 

526 """ 

527 

528 insert_crud_params: List[crud._CrudParamElementStr] 

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

530 

531 num_positional_params_counted: int 

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

533 

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

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

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

537 within them. 

538 

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

540 when batching. 

541 

542 """ 

543 

544 sort_by_parameter_order: bool = False 

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

546 insert. 

547 

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

549 

550 """ 

551 

552 includes_upsert_behaviors: bool = False 

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

554 

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

556 deterministic ordering. 

557 

558 """ 

559 

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

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

562 

563 This list is only here if the INSERT asked for 

564 sort_by_parameter_order=True, 

565 and dialect-appropriate sentinel columns were located. 

566 

567 .. versionadded:: 2.0.10 

568 

569 """ 

570 

571 num_sentinel_columns: int = 0 

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

573 

574 This is the same as 

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

576 

577 """ 

578 

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

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

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

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

583 

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

585 statement actually refers to client side values for these sentinel 

586 columns. 

587 

588 .. versionadded:: 2.0.10 

589 

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

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

592 the parameters were converted by bound parameter processors 

593 

594 """ 

595 

596 implicit_sentinel: bool = False 

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

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

599 

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

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

602 correlation with the incoming parameter list. 

603 

604 .. versionadded:: 2.0.10 

605 

606 """ 

607 

608 has_upsert_bound_parameters: bool = False 

609 """if True, the upsert SET clause contains bound parameters that will 

610 receive their values from the parameters dict (i.e., parametrized 

611 bindparams where value is None and callable is None). 

612 

613 This means we can't batch multiple rows in a single statement, since 

614 each row would need different values in the SET clause but there's only 

615 one SET clause per statement. See issue #13130. 

616 

617 .. versionadded:: 2.0.37 

618 

619 """ 

620 

621 embed_values_counter: bool = False 

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

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

624 

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

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

627 includes PostgreSQL, Microsoft SQL Server. 

628 

629 .. versionadded:: 2.0.10 

630 

631 """ 

632 

633 

634class _InsertManyValuesBatch(NamedTuple): 

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

636 

637 This is passed through the 

638 :meth:`.SQLCompiler._deliver_insertmanyvalues_batches` and 

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

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

641 :meth:`.Connection._exec_insertmany_context` method. 

642 

643 .. versionadded:: 2.0.10 

644 

645 """ 

646 

647 replaced_statement: str 

648 replaced_parameters: _DBAPIAnyExecuteParams 

649 processed_setinputsizes: Optional[_GenericSetInputSizesType] 

650 batch: Sequence[_DBAPISingleExecuteParams] 

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

652 current_batch_size: int 

653 batchnum: int 

654 total_batches: int 

655 rows_sorted: bool 

656 is_downgraded: bool 

657 

658 

659class InsertmanyvaluesSentinelOpts(FastIntFlag): 

660 """bitflag enum indicating styles of PK defaults 

661 which can work as implicit sentinel columns 

662 

663 """ 

664 

665 NOT_SUPPORTED = 1 

666 AUTOINCREMENT = 2 

667 IDENTITY = 4 

668 SEQUENCE = 8 

669 MONOTONIC_FUNCTION = 16 

670 

671 ANY_AUTOINCREMENT = ( 

672 AUTOINCREMENT | IDENTITY | SEQUENCE | MONOTONIC_FUNCTION 

673 ) 

674 _SUPPORTED_OR_NOT = NOT_SUPPORTED | ANY_AUTOINCREMENT 

675 

676 USE_INSERT_FROM_SELECT = 32 

677 RENDER_SELECT_COL_CASTS = 64 

678 

679 

680class AggregateOrderByStyle(IntEnum): 

681 """Describes backend database's capabilities with ORDER BY for aggregate 

682 functions 

683 

684 .. versionadded:: 2.1 

685 

686 """ 

687 

688 NONE = 0 

689 """database has no ORDER BY for aggregate functions""" 

690 

691 INLINE = 1 

692 """ORDER BY is rendered inside the function's argument list, typically as 

693 the last element""" 

694 

695 WITHIN_GROUP = 2 

696 """the WITHIN GROUP (ORDER BY ...) phrase is used for all aggregate 

697 functions (not just the ordered set ones)""" 

698 

699 

700class CompilerState(IntEnum): 

701 COMPILING = 0 

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

703 

704 STRING_APPLIED = 1 

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

706 

707 Additional processors by subclasses may still be pending. 

708 

709 """ 

710 

711 NO_STATEMENT = 2 

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

713 for method access""" 

714 

715 

716class Linting(IntEnum): 

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

718 

719 this feature currently includes support for flagging cartesian products 

720 in SQL statements. 

721 

722 """ 

723 

724 NO_LINTING = 0 

725 "Disable all linting." 

726 

727 COLLECT_CARTESIAN_PRODUCTS = 1 

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

729 'self.from_linter'""" 

730 

731 WARN_LINTING = 2 

732 "Emit warnings for linters that find problems" 

733 

734 FROM_LINTING = COLLECT_CARTESIAN_PRODUCTS | WARN_LINTING 

735 """Warn for cartesian products; combines COLLECT_CARTESIAN_PRODUCTS 

736 and WARN_LINTING""" 

737 

738 

739NO_LINTING, COLLECT_CARTESIAN_PRODUCTS, WARN_LINTING, FROM_LINTING = tuple( 

740 Linting 

741) 

742 

743 

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

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

746 feature.""" 

747 

748 def lint(self, start=None): 

749 froms = self.froms 

750 if not froms: 

751 return None, None 

752 

753 edges = set(self.edges) 

754 the_rest = set(froms) 

755 

756 if start is not None: 

757 start_with = start 

758 the_rest.remove(start_with) 

759 else: 

760 start_with = the_rest.pop() 

761 

762 stack = collections.deque([start_with]) 

763 

764 while stack and the_rest: 

765 node = stack.popleft() 

766 the_rest.discard(node) 

767 

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

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

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

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

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

773 

774 # appendleft the node in each edge that is not 

775 # the one that matched. 

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

777 edges.difference_update(to_remove) 

778 

779 # FROMS left over? boom 

780 if the_rest: 

781 return the_rest, start_with 

782 else: 

783 return None, None 

784 

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

786 the_rest, start_with = self.lint() 

787 

788 # FROMS left over? boom 

789 if the_rest: 

790 froms = the_rest 

791 if froms: 

792 template = ( 

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

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

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

796 "between each element to resolve." 

797 ) 

798 froms_str = ", ".join( 

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

800 ) 

801 message = template.format( 

802 stmt_type=stmt_type, 

803 froms=froms_str, 

804 start=self.froms[start_with], 

805 ) 

806 

807 util.warn(message) 

808 

809 

810class Compiled: 

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

812 

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

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

815 specific to their underlying database dialect, and also may 

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

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

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

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

820 defaults. 

821 """ 

822 

823 statement: Optional[ClauseElement] = None 

824 "The statement to compile." 

825 string: str = "" 

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

827 

828 state: CompilerState 

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

830 

831 is_sql = False 

832 is_ddl = False 

833 

834 _cached_metadata: Optional[CursorResultMetaData] = None 

835 

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

837 

838 schema_translate_map: Optional[SchemaTranslateMapType] = None 

839 

840 execution_options: _ExecuteOptions = util.EMPTY_DICT 

841 """ 

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

843 sub-elements of the statement can modify these. 

844 """ 

845 

846 preparer: IdentifierPreparer 

847 

848 _annotations: _AnnotationDict = util.EMPTY_DICT 

849 

850 compile_state: Optional[CompileState] = None 

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

852 state used by the compiler. 

853 

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

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

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

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

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

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

860 processing. 

861 

862 .. versionadded:: 1.4 

863 

864 """ 

865 

866 dml_compile_state: Optional[CompileState] = None 

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

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

869 

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

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

872 object. 

873 

874 .. versionadded:: 1.4.40 

875 

876 """ 

877 

878 cache_key: Optional[CacheKey] = None 

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

880 :class:`.Compiled` object. 

881 

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

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

884 instance was first cached, typically in order to reconcile 

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

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

887 

888 """ 

889 

890 _gen_time: float 

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

892 cache stats.""" 

893 

894 def __init__( 

895 self, 

896 dialect: Dialect, 

897 statement: Optional[ClauseElement], 

898 schema_translate_map: Optional[SchemaTranslateMapType] = None, 

899 render_schema_translate: bool = False, 

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

901 ): 

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

903 

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

905 

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

907 

908 :param schema_translate_map: dictionary of schema names to be 

909 translated when forming the resultant SQL 

910 

911 .. seealso:: 

912 

913 :ref:`schema_translating` 

914 

915 :param compile_kwargs: additional kwargs that will be 

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

917 

918 

919 """ 

920 self.dialect = dialect 

921 self.preparer = self.dialect.identifier_preparer 

922 if schema_translate_map: 

923 self.schema_translate_map = schema_translate_map 

924 self.preparer = self.preparer._with_schema_translate( 

925 schema_translate_map 

926 ) 

927 

928 if statement is not None: 

929 self.state = CompilerState.COMPILING 

930 self.statement = statement 

931 self.can_execute = statement.supports_execution 

932 self._annotations = statement._annotations 

933 if self.can_execute: 

934 if TYPE_CHECKING: 

935 assert isinstance(statement, Executable) 

936 self.execution_options = statement._execution_options 

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

938 

939 if render_schema_translate: 

940 assert schema_translate_map is not None 

941 self.string = self.preparer._render_schema_translates( 

942 self.string, schema_translate_map 

943 ) 

944 

945 self.state = CompilerState.STRING_APPLIED 

946 else: 

947 self.state = CompilerState.NO_STATEMENT 

948 

949 self._gen_time = perf_counter() 

950 

951 def __init_subclass__(cls) -> None: 

952 cls._init_compiler_cls() 

953 return super().__init_subclass__() 

954 

955 @classmethod 

956 def _init_compiler_cls(cls): 

957 pass 

958 

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

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

961 

962 @property 

963 def sql_compiler(self) -> SQLCompiler: 

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

965 

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

967 

968 """ 

969 

970 raise NotImplementedError() 

971 

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

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

974 

975 def __str__(self) -> str: 

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

977 

978 if self.state is CompilerState.STRING_APPLIED: 

979 return self.string 

980 else: 

981 return "" 

982 

983 def construct_params( 

984 self, 

985 params: Optional[_CoreSingleExecuteParams] = None, 

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

987 escape_names: bool = True, 

988 ) -> Optional[_MutableCoreSingleExecuteParams]: 

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

990 

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

992 override bind values compiled in to the 

993 statement. 

994 """ 

995 

996 raise NotImplementedError() 

997 

998 @property 

999 def params(self): 

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

1001 return self.construct_params() 

1002 

1003 

1004class TypeCompiler(util.EnsureKWArg): 

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

1006 

1007 ensure_kwarg = r"visit_\w+" 

1008 

1009 def __init__(self, dialect: Dialect): 

1010 self.dialect = dialect 

1011 

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

1013 if ( 

1014 type_._variant_mapping 

1015 and self.dialect.name in type_._variant_mapping 

1016 ): 

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

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

1019 

1020 def visit_unsupported_compilation( 

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

1022 ) -> NoReturn: 

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

1024 

1025 

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

1027# column elements this is actually a column element 

1028class _CompileLabel( 

1029 roles.BinaryElementRole[Any], elements.CompilerColumnElement 

1030): 

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

1032 

1033 __visit_name__ = "label" 

1034 __slots__ = "element", "name", "_alt_names" 

1035 

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

1037 self.element = col 

1038 self.name = name 

1039 self._alt_names = (col,) + alt_names 

1040 

1041 @property 

1042 def proxy_set(self): 

1043 return self.element.proxy_set 

1044 

1045 @property 

1046 def type(self): 

1047 return self.element.type 

1048 

1049 def self_group(self, **kw): 

1050 return self 

1051 

1052 

1053class aggregate_orderby_inline( 

1054 roles.BinaryElementRole[Any], elements.CompilerColumnElement 

1055): 

1056 """produce ORDER BY inside of function argument lists""" 

1057 

1058 __visit_name__ = "aggregate_orderby_inline" 

1059 __slots__ = "element", "aggregate_order_by" 

1060 

1061 def __init__(self, element, orderby): 

1062 self.element = element 

1063 self.aggregate_order_by = orderby 

1064 

1065 def __iter__(self): 

1066 return iter(self.element) 

1067 

1068 @property 

1069 def proxy_set(self): 

1070 return self.element.proxy_set 

1071 

1072 @property 

1073 def type(self): 

1074 return self.element.type 

1075 

1076 def self_group(self, **kw): 

1077 return self 

1078 

1079 def _with_binary_element_type(self, type_): 

1080 return aggregate_orderby_inline( 

1081 self.element._with_binary_element_type(type_), 

1082 self.aggregate_order_by, 

1083 ) 

1084 

1085 

1086class ilike_case_insensitive( 

1087 roles.BinaryElementRole[Any], elements.CompilerColumnElement 

1088): 

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

1090 an ILIKE construct. 

1091 

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

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

1094 is being used. 

1095 

1096 .. versionadded:: 2.0 

1097 

1098 """ 

1099 

1100 __visit_name__ = "ilike_case_insensitive_operand" 

1101 __slots__ = "element", "comparator" 

1102 

1103 def __init__(self, element): 

1104 self.element = element 

1105 self.comparator = element.comparator 

1106 

1107 @property 

1108 def proxy_set(self): 

1109 return self.element.proxy_set 

1110 

1111 @property 

1112 def type(self): 

1113 return self.element.type 

1114 

1115 def self_group(self, **kw): 

1116 return self 

1117 

1118 def _with_binary_element_type(self, type_): 

1119 return ilike_case_insensitive( 

1120 self.element._with_binary_element_type(type_) 

1121 ) 

1122 

1123 

1124class SQLCompiler(Compiled): 

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

1126 

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

1128 

1129 """ 

1130 

1131 extract_map = EXTRACT_MAP 

1132 

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

1134 util.immutabledict( 

1135 { 

1136 "%": "P", 

1137 "(": "A", 

1138 ")": "Z", 

1139 ":": "C", 

1140 ".": "_", 

1141 "[": "_", 

1142 "]": "_", 

1143 " ": "_", 

1144 } 

1145 ) 

1146 ) 

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

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

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

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

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

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

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

1154 

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

1156 other constructs to have any arbitrary characters present without any 

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

1158 

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

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

1161 mapping will never appear in a bound parameter name. 

1162 

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

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

1165 is first declared. 

1166 

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

1168 as additional restrictions on leading characters, the 

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

1170 See the cx_Oracle compiler for an example of this. 

1171 

1172 .. versionadded:: 2.0.0rc1 

1173 

1174 """ 

1175 

1176 _bind_translate_re: ClassVar[Pattern[str]] 

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

1178 

1179 is_sql = True 

1180 

1181 compound_keywords = COMPOUND_KEYWORDS 

1182 

1183 isdelete: bool = False 

1184 isinsert: bool = False 

1185 isupdate: bool = False 

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

1187 level to define if this Compiled instance represents 

1188 INSERT/UPDATE/DELETE 

1189 """ 

1190 

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

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

1193 receive server-updated values""" 

1194 

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

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

1197 an INSERT takes place""" 

1198 

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

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

1201 before an UPDATE takes place""" 

1202 

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

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

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

1206 

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

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

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

1210 feature. 

1211 

1212 """ 

1213 

1214 isplaintext: bool = False 

1215 

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

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

1218 

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

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

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

1222 

1223 stack: List[_CompilerStackEntry] 

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

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

1226 

1227 returning_precedes_values: bool = False 

1228 """set to True classwide to generate RETURNING 

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

1230 """ 

1231 

1232 render_table_with_column_in_update_from: bool = False 

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

1234 in a multi-table UPDATE statement should qualify 

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

1236 """ 

1237 

1238 ansi_bind_rules: bool = False 

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

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

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

1242 subclass can set this flag to False if the target 

1243 driver/DB enforces this 

1244 """ 

1245 

1246 bindtemplate: str 

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

1248 

1249 compilation_bindtemplate: str 

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

1251 paramstyle application""" 

1252 

1253 _numeric_binds_identifier_char: str 

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

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

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

1257 """ 

1258 

1259 _result_columns: List[ResultColumnsEntry] 

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

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

1262 TypeEngine. CursorResult uses this for type processing and 

1263 column targeting""" 

1264 

1265 _textual_ordered_columns: bool = False 

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

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

1268 

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

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

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

1272 

1273 """ 

1274 

1275 _ad_hoc_textual: bool = False 

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

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

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

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

1280 should match on name. 

1281 

1282 """ 

1283 

1284 _ordered_columns: bool = True 

1285 """ 

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

1287 in _result_columns is actually the rendered order. Usually 

1288 True unless using an unordered TextualSelect. 

1289 """ 

1290 

1291 _loose_column_name_matching: bool = False 

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

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

1294 than the base name. 

1295 

1296 """ 

1297 

1298 _numeric_binds: bool = False 

1299 """ 

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

1301 all the others. 

1302 

1303 """ 

1304 

1305 _render_postcompile: bool = False 

1306 """ 

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

1308 

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

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

1311 dialect internals access and render the internal postcompile structure 

1312 directly. 

1313 

1314 """ 

1315 

1316 _post_compile_expanded_state: Optional[ExpandedState] = None 

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

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

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

1320 

1321 .. versionadded:: 2.0.0rc1 

1322 

1323 """ 

1324 

1325 _pre_expanded_string: Optional[str] = None 

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

1327 for cases where 'post_compile' were used. 

1328 

1329 """ 

1330 

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

1332 

1333 _insertmanyvalues: Optional[_InsertManyValues] = None 

1334 

1335 _insert_crud_params: Optional[crud._CrudParamSequence] = None 

1336 

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

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

1339 execution time. 

1340 

1341 """ 

1342 

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

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

1345 at statement execution time. 

1346 

1347 """ 

1348 

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

1350 

1351 # the names ``escaped_bind_names`` maps *to*, maintained alongside it so 

1352 # that collision checks don't rebuild the value collection each time. 

1353 # only assigned once a name actually needs escaping, which is rare; 

1354 # ``escaped_bind_names`` being empty means this was never set. 

1355 _escaped_bind_names_used: Set[str] 

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

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

1358 

1359 """ 

1360 

1361 has_out_parameters = False 

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

1363 flag set.""" 

1364 

1365 postfetch_lastrowid = False 

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

1367 result.inserted_primary_key. """ 

1368 

1369 _cache_key_bind_match: Optional[ 

1370 Tuple[ 

1371 Dict[ 

1372 BindParameter[Any], 

1373 List[BindParameter[Any]], 

1374 ], 

1375 Dict[ 

1376 str, 

1377 BindParameter[Any], 

1378 ], 

1379 ] 

1380 ] = None 

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

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

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

1384 

1385 """ 

1386 

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

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

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

1390 

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

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

1393 render parameters. 

1394 

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

1396 

1397 .. seealso:: 

1398 

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

1400 debugging use cases. 

1401 

1402 """ 

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

1404 

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

1406 

1407 inline: bool = False 

1408 

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

1410 

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

1412 # Level is required for supporting nesting 

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

1414 

1415 # To retrieve key/level in ctes_by_level_name - 

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

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

1418 

1419 ctes_recursive: bool 

1420 

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

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

1423 _positional_pattern = re.compile( 

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

1425 ) 

1426 

1427 # variants of the above for dialects that double percent signs in 

1428 # literal text; a "%%" pair is matched first so that an escaped 

1429 # percent sign is never taken as the start of a bound parameter 

1430 _pct_escaped_pyformat_pattern = re.compile( 

1431 f"%%|{_pyformat_pattern.pattern}" 

1432 ) 

1433 _pct_escaped_positional_pattern = re.compile( 

1434 f"%%|{_positional_pattern.pattern}" 

1435 ) 

1436 

1437 _collect_params: Final[bool] 

1438 _collected_params: util.immutabledict[str, Any] 

1439 

1440 @classmethod 

1441 def _init_compiler_cls(cls): 

1442 cls._init_bind_translate() 

1443 

1444 @classmethod 

1445 def _init_bind_translate(cls): 

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

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

1448 cls._bind_translate_chars = cls.bindname_escape_characters 

1449 

1450 def __init__( 

1451 self, 

1452 dialect: Dialect, 

1453 statement: Optional[ClauseElement], 

1454 cache_key: Optional[CacheKey] = None, 

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

1456 for_executemany: bool = False, 

1457 linting: Linting = NO_LINTING, 

1458 _supporting_against: Optional[SQLCompiler] = None, 

1459 **kwargs: Any, 

1460 ): 

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

1462 

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

1464 

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

1466 

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

1468 INSERT or UPDATE statement. 

1469 

1470 :param for_executemany: whether INSERT / UPDATE statements should 

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

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

1473 values of defaults and autoincrement / sequences and similar. 

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

1475 these values may be disabled which means SQL expressions may 

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

1477 

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

1479 superclass. 

1480 

1481 """ 

1482 self.column_keys = column_keys 

1483 

1484 self.cache_key = cache_key 

1485 

1486 if cache_key: 

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

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

1489 self._cache_key_bind_match = (ckbm, cksm) 

1490 

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

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

1493 # or no RETURNING 

1494 self.for_executemany = for_executemany 

1495 

1496 self.linting = linting 

1497 

1498 # a dictionary of bind parameter keys to BindParameter 

1499 # instances. 

1500 self.binds = {} 

1501 

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

1503 # that are actually present in the generated SQL 

1504 self.bind_names = util.column_dict() 

1505 

1506 # stack which keeps track of nested SELECT statements 

1507 self.stack = [] 

1508 

1509 self._result_columns = [] 

1510 

1511 # true if the paramstyle is positional 

1512 self.positional = dialect.positional 

1513 if self.positional: 

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

1515 if nb: 

1516 self._numeric_binds_identifier_char = ( 

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

1518 ) 

1519 

1520 self.compilation_bindtemplate = _pyformat_template 

1521 else: 

1522 self.compilation_bindtemplate = BIND_TEMPLATES[dialect.paramstyle] 

1523 

1524 self.ctes = None 

1525 

1526 self.label_length = ( 

1527 dialect.label_length or dialect.max_identifier_length 

1528 ) 

1529 

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

1531 # the fly here 

1532 self.anon_map = prefix_anon_map() 

1533 

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

1535 # dialect.label_length or dialect.max_identifier_length 

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

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

1538 if not cache_key: 

1539 self._collect_params = True 

1540 self._collected_params = util.EMPTY_DICT 

1541 else: 

1542 self._collect_params = False # type: ignore[misc] 

1543 

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

1545 

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

1547 if TYPE_CHECKING: 

1548 assert isinstance(statement, UpdateBase) 

1549 

1550 if self.isinsert or self.isupdate: 

1551 if TYPE_CHECKING: 

1552 assert isinstance(statement, ValuesBase) 

1553 if statement._inline: 

1554 self.inline = True 

1555 elif self.for_executemany and ( 

1556 not self.isinsert 

1557 or ( 

1558 self.dialect.insert_executemany_returning 

1559 and statement._return_defaults 

1560 ) 

1561 ): 

1562 self.inline = True 

1563 

1564 self.bindtemplate = BIND_TEMPLATES[dialect.paramstyle] 

1565 

1566 if _supporting_against: 

1567 self.__dict__.update( 

1568 { 

1569 k: v 

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

1571 if k 

1572 not in { 

1573 "state", 

1574 "dialect", 

1575 "preparer", 

1576 "positional", 

1577 "_numeric_binds", 

1578 "compilation_bindtemplate", 

1579 "bindtemplate", 

1580 } 

1581 } 

1582 ) 

1583 

1584 if self.state is CompilerState.STRING_APPLIED: 

1585 if self.positional: 

1586 if self._numeric_binds: 

1587 self._process_numeric() 

1588 else: 

1589 self._process_positional() 

1590 

1591 if self._render_postcompile: 

1592 parameters = self.construct_params( 

1593 escape_names=False, 

1594 _no_postcompile=True, 

1595 ) 

1596 

1597 self._process_parameters_for_postcompile( 

1598 parameters, _populate_self=True 

1599 ) 

1600 

1601 @property 

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

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

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

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

1606 

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

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

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

1610 :class:`.SQLCompiler`. 

1611 

1612 """ 

1613 if self._insertmanyvalues is None: 

1614 return None 

1615 else: 

1616 return self._insertmanyvalues.single_values_expr 

1617 

1618 @util.ro_memoized_property 

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

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

1621 

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

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

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

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

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

1627 

1628 .. versionadded:: 2.0 

1629 

1630 """ 

1631 if self.implicit_returning: 

1632 return self.implicit_returning 

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

1634 return [ 

1635 c 

1636 for c in self.statement._all_selected_columns 

1637 if is_column_element(c) 

1638 ] 

1639 

1640 else: 

1641 return None 

1642 

1643 @property 

1644 def returning(self): 

1645 """backwards compatibility; returns the 

1646 effective_returning collection. 

1647 

1648 """ 

1649 return self.effective_returning 

1650 

1651 @property 

1652 def current_executable(self): 

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

1654 

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

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

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

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

1659 

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

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

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

1663 ultimately nestable, and this attribute should never be consulted 

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

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

1666 

1667 """ 

1668 try: 

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

1670 except IndexError as ie: 

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

1672 

1673 @property 

1674 def prefetch(self): 

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

1676 

1677 @util.memoized_property 

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

1679 return {} 

1680 

1681 def _add_to_params(self, item: ExecutableStatement) -> None: 

1682 # assumes that this is called before traversing the statement 

1683 # so the call happens outer to inner, meaning that existing params 

1684 # take precedence 

1685 if item._params: 

1686 self._collected_params = item._params | self._collected_params 

1687 

1688 @util.memoized_instancemethod 

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

1690 """Initialize collections related to CTEs only if 

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

1692 these collections otherwise. 

1693 

1694 """ 

1695 # collect CTEs to tack on top of a SELECT 

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

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

1698 self.ctes = ctes 

1699 

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

1701 # Level is required for supporting nesting 

1702 self.ctes_by_level_name = {} 

1703 

1704 # To retrieve key/level in ctes_by_level_name - 

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

1706 self.level_name_by_cte = {} 

1707 

1708 self.ctes_recursive = False 

1709 

1710 return ctes 

1711 

1712 @contextlib.contextmanager 

1713 def _nested_result(self): 

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

1715 result_columns, ordered_columns = ( 

1716 self._result_columns, 

1717 self._ordered_columns, 

1718 ) 

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

1720 

1721 try: 

1722 if self.stack: 

1723 entry = self.stack[-1] 

1724 entry["need_result_map_for_nested"] = True 

1725 else: 

1726 entry = None 

1727 yield self._result_columns, self._ordered_columns 

1728 finally: 

1729 if entry: 

1730 entry.pop("need_result_map_for_nested") 

1731 self._result_columns, self._ordered_columns = ( 

1732 result_columns, 

1733 ordered_columns, 

1734 ) 

1735 

1736 def _process_positional(self): 

1737 assert not self.positiontup 

1738 assert self.state is CompilerState.STRING_APPLIED 

1739 assert not self._numeric_binds 

1740 

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

1742 placeholder = "%s" 

1743 else: 

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

1745 placeholder = "?" 

1746 

1747 positions = [] 

1748 binds = self.binds 

1749 

1750 if self.escaped_bind_names: 

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

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

1753 else: 

1754 reverse_escape = {} 

1755 

1756 if self.preparer._double_percents: 

1757 positional_pattern = self._pct_escaped_positional_pattern 

1758 else: 

1759 positional_pattern = self._positional_pattern 

1760 

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

1762 normal_bind = m.group(1) 

1763 if normal_bind: 

1764 if reverse_escape.get(normal_bind, normal_bind) not in binds: 

1765 # literal text that looks like a bound parameter 

1766 return m.group(0) 

1767 positions.append(normal_bind) 

1768 return placeholder 

1769 elif m.group(2): 

1770 # this a post-compile bind 

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

1772 return m.group(0) 

1773 else: 

1774 # an escaped percent sign 

1775 return m.group(0) 

1776 

1777 self.string = re.sub(positional_pattern, find_position, self.string) 

1778 

1779 if reverse_escape: 

1780 self.positiontup = [ 

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

1782 ] 

1783 else: 

1784 self.positiontup = positions 

1785 

1786 if self._insertmanyvalues: 

1787 positions = [] 

1788 

1789 single_values_expr = re.sub( 

1790 positional_pattern, 

1791 find_position, 

1792 self._insertmanyvalues.single_values_expr, 

1793 ) 

1794 insert_crud_params = [ 

1795 ( 

1796 v[0], 

1797 v[1], 

1798 re.sub(positional_pattern, find_position, v[2]), 

1799 v[3], 

1800 ) 

1801 for v in self._insertmanyvalues.insert_crud_params 

1802 ] 

1803 

1804 self._insertmanyvalues = self._insertmanyvalues._replace( 

1805 single_values_expr=single_values_expr, 

1806 insert_crud_params=insert_crud_params, 

1807 ) 

1808 

1809 def _process_numeric(self): 

1810 assert self._numeric_binds 

1811 assert self.state is CompilerState.STRING_APPLIED 

1812 

1813 num = 1 

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

1815 order: Iterable[str] 

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

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

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

1819 # values () () 

1820 order = itertools.chain( 

1821 ( 

1822 name 

1823 for name in self.bind_names.values() 

1824 if name not in self._values_bindparam 

1825 ), 

1826 self.bind_names.values(), 

1827 ) 

1828 else: 

1829 order = self.bind_names.values() 

1830 

1831 for bind_name in order: 

1832 if bind_name in param_pos: 

1833 continue 

1834 bind = self.binds[bind_name] 

1835 if ( 

1836 bind in self.post_compile_params 

1837 or bind in self.literal_execute_params 

1838 ): 

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

1840 # be replaced below. 

1841 param_pos[bind_name] = None # type: ignore[assignment] 

1842 else: 

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

1844 num += 1 

1845 param_pos[bind_name] = ph 

1846 

1847 self.next_numeric_pos = num 

1848 

1849 self.positiontup = list(param_pos) 

1850 if self.escaped_bind_names: 

1851 len_before = len(param_pos) 

1852 param_pos = { 

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

1854 for name, pos in param_pos.items() 

1855 } 

1856 assert len(param_pos) == len_before 

1857 

1858 if self.preparer._double_percents: 

1859 pyformat_pattern = self._pct_escaped_pyformat_pattern 

1860 else: 

1861 pyformat_pattern = self._pyformat_pattern 

1862 

1863 # any match not in param_pos is an escaped percent sign or literal 

1864 # text that looks like a bound parameter 

1865 self.string = pyformat_pattern.sub( 

1866 lambda m: param_pos.get(m.group(1), m.group(0)), self.string 

1867 ) 

1868 

1869 if self._insertmanyvalues: 

1870 single_values_expr = ( 

1871 # format is ok here since single_values_expr includes only 

1872 # place-holders 

1873 self._insertmanyvalues.single_values_expr 

1874 % param_pos 

1875 ) 

1876 insert_crud_params = [ 

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

1878 for v in self._insertmanyvalues.insert_crud_params 

1879 ] 

1880 

1881 self._insertmanyvalues = self._insertmanyvalues._replace( 

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

1883 single_values_expr=single_values_expr, 

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

1885 insert_crud_params=insert_crud_params, 

1886 ) 

1887 

1888 @util.memoized_property 

1889 def _bind_processors( 

1890 self, 

1891 ) -> MutableMapping[ 

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

1893 ]: 

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

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

1896 return { 

1897 key: value # type: ignore[misc] 

1898 for key, value in ( 

1899 ( 

1900 self.bind_names[bindparam], 

1901 ( 

1902 bindparam.type._cached_bind_processor(self.dialect) 

1903 if not bindparam.type._is_tuple_type 

1904 else tuple( 

1905 elem_type._cached_bind_processor(self.dialect) 

1906 for elem_type in cast( 

1907 TupleType, bindparam.type 

1908 ).types 

1909 ) 

1910 ), 

1911 ) 

1912 for bindparam in self.bind_names 

1913 # literal_execute parameters are rendered into the SQL string 

1914 # via their literal_processor and never bound as values, so 

1915 # they do not need a bind processor. Skipping them also avoids 

1916 # invoking bind-processor construction that may require the 

1917 # DBAPI to be present (see asyncpg, psycopgcffi cases), 

1918 # facilitating testing. 

1919 if bindparam not in self.literal_execute_params 

1920 ) 

1921 if value is not None 

1922 } 

1923 

1924 @util.memoized_property 

1925 def _expanded_bind_names_reserved(self) -> FrozenSet[str]: 

1926 """Names that the individual elements of an expanding parameter 

1927 may not take when the parameter is expanded at execution time. 

1928 

1929 This is every bound parameter name in the statement, both in its 

1930 compiled and in its escaped form. See :ticket:`13695`. 

1931 

1932 """ 

1933 return frozenset(self.bind_names.values()).union( 

1934 self.escaped_bind_names, self.escaped_bind_names.values() 

1935 ) 

1936 

1937 def is_subquery(self): 

1938 return len(self.stack) > 1 

1939 

1940 @property 

1941 def sql_compiler(self) -> Self: 

1942 return self 

1943 

1944 def construct_expanded_state( 

1945 self, 

1946 params: Optional[_CoreSingleExecuteParams] = None, 

1947 escape_names: bool = True, 

1948 ) -> ExpandedState: 

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

1950 

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

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

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

1954 

1955 .. versionadded:: 2.0.0rc1 

1956 

1957 """ 

1958 parameters = self.construct_params( 

1959 params, 

1960 escape_names=escape_names, 

1961 _no_postcompile=True, 

1962 ) 

1963 return self._process_parameters_for_postcompile( 

1964 parameters, 

1965 ) 

1966 

1967 def construct_params( 

1968 self, 

1969 params: Optional[_CoreSingleExecuteParams] = None, 

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

1971 escape_names: bool = True, 

1972 _group_number: Optional[int] = None, 

1973 _check: bool = True, 

1974 _no_postcompile: bool = False, 

1975 _collected_params: _CoreSingleExecuteParams | None = None, 

1976 ) -> _MutableCoreSingleExecuteParams: 

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

1978 if _collected_params is not None: 

1979 assert not self._collect_params 

1980 elif self._collect_params: 

1981 _collected_params = self._collected_params 

1982 

1983 if _collected_params: 

1984 if not params: 

1985 params = _collected_params 

1986 else: 

1987 params = {**_collected_params, **params} 

1988 

1989 if self._render_postcompile and not _no_postcompile: 

1990 assert self._post_compile_expanded_state is not None 

1991 if not params: 

1992 return dict(self._post_compile_expanded_state.parameters) 

1993 else: 

1994 raise exc.InvalidRequestError( 

1995 "can't construct new parameters when render_postcompile " 

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

1997 "parameters. Use construct_expanded_state to generate a " 

1998 "new statement and parameters." 

1999 ) 

2000 

2001 has_escaped_names = escape_names and bool(self.escaped_bind_names) 

2002 

2003 if extracted_parameters: 

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

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

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

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

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

2009 # will be guaranteed to match. 

2010 if self.cache_key is None: 

2011 raise exc.CompileError( 

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

2013 "can't pass extracted_parameters to construct_params" 

2014 ) 

2015 else: 

2016 orig_extracted = self.cache_key[1] 

2017 

2018 ckbm_tuple = self._cache_key_bind_match 

2019 assert ckbm_tuple is not None 

2020 ckbm, _ = ckbm_tuple 

2021 resolved_extracted = { 

2022 bind: extracted 

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

2024 for bind in ckbm[b] 

2025 } 

2026 else: 

2027 resolved_extracted = None 

2028 

2029 if params: 

2030 pd = {} 

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

2032 escaped_name = ( 

2033 self.escaped_bind_names.get(name, name) 

2034 if has_escaped_names 

2035 else name 

2036 ) 

2037 

2038 if bindparam.key in params: 

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

2040 elif name in params: 

2041 pd[escaped_name] = params[name] 

2042 

2043 elif _check and bindparam.required: 

2044 if _group_number: 

2045 raise exc.InvalidRequestError( 

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

2047 "in parameter group %d" 

2048 % (bindparam.key, _group_number), 

2049 code="cd3x", 

2050 ) 

2051 else: 

2052 raise exc.InvalidRequestError( 

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

2054 % bindparam.key, 

2055 code="cd3x", 

2056 ) 

2057 else: 

2058 if resolved_extracted: 

2059 value_param = resolved_extracted.get( 

2060 bindparam, bindparam 

2061 ) 

2062 else: 

2063 value_param = bindparam 

2064 

2065 if bindparam.callable: 

2066 pd[escaped_name] = value_param.effective_value 

2067 else: 

2068 pd[escaped_name] = value_param.value 

2069 return pd 

2070 else: 

2071 pd = {} 

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

2073 escaped_name = ( 

2074 self.escaped_bind_names.get(name, name) 

2075 if has_escaped_names 

2076 else name 

2077 ) 

2078 

2079 if _check and bindparam.required: 

2080 if _group_number: 

2081 raise exc.InvalidRequestError( 

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

2083 "in parameter group %d" 

2084 % (bindparam.key, _group_number), 

2085 code="cd3x", 

2086 ) 

2087 else: 

2088 raise exc.InvalidRequestError( 

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

2090 % bindparam.key, 

2091 code="cd3x", 

2092 ) 

2093 

2094 if resolved_extracted: 

2095 value_param = resolved_extracted.get(bindparam, bindparam) 

2096 else: 

2097 value_param = bindparam 

2098 

2099 if bindparam.callable: 

2100 pd[escaped_name] = value_param.effective_value 

2101 else: 

2102 pd[escaped_name] = value_param.value 

2103 

2104 return pd 

2105 

2106 @util.memoized_instancemethod 

2107 def _get_set_input_sizes_lookup(self): 

2108 dialect = self.dialect 

2109 

2110 include_types = dialect.include_set_input_sizes 

2111 exclude_types = dialect.exclude_set_input_sizes 

2112 

2113 dbapi = dialect.dbapi 

2114 

2115 def lookup_type(typ): 

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

2117 

2118 if ( 

2119 dbtype is not None 

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

2121 and (include_types is None or dbtype in include_types) 

2122 ): 

2123 return dbtype 

2124 else: 

2125 return None 

2126 

2127 inputsizes = {} 

2128 

2129 literal_execute_params = self.literal_execute_params 

2130 

2131 for bindparam in self.bind_names: 

2132 if bindparam in literal_execute_params: 

2133 continue 

2134 

2135 if bindparam.type._is_tuple_type: 

2136 inputsizes[bindparam] = [ 

2137 lookup_type(typ) 

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

2139 ] 

2140 else: 

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

2142 

2143 return inputsizes 

2144 

2145 @property 

2146 def params(self): 

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

2148 compiled object, for those values that are present. 

2149 

2150 .. seealso:: 

2151 

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

2153 debugging use cases. 

2154 

2155 """ 

2156 return self.construct_params(_check=False) 

2157 

2158 def _process_parameters_for_postcompile( 

2159 self, 

2160 parameters: _MutableCoreSingleExecuteParams, 

2161 _populate_self: bool = False, 

2162 ) -> ExpandedState: 

2163 """handle special post compile parameters. 

2164 

2165 These include: 

2166 

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

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

2169 

2170 * literal_binds compiled with the literal_execute flag. Used for 

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

2172 N as a bound parameter. 

2173 

2174 """ 

2175 

2176 expanded_parameters = {} 

2177 new_positiontup: Optional[List[str]] 

2178 

2179 pre_expanded_string = self._pre_expanded_string 

2180 if pre_expanded_string is None: 

2181 pre_expanded_string = self.string 

2182 

2183 if self.positional: 

2184 new_positiontup = [] 

2185 

2186 pre_expanded_positiontup = self._pre_expanded_positiontup 

2187 if pre_expanded_positiontup is None: 

2188 pre_expanded_positiontup = self.positiontup 

2189 

2190 else: 

2191 new_positiontup = pre_expanded_positiontup = None 

2192 

2193 processors = self._bind_processors 

2194 single_processors = cast( 

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

2196 ) 

2197 tuple_processors = cast( 

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

2199 ) 

2200 

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

2202 

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

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

2205 

2206 # names that the elements of expanding parameters can't take, as 

2207 # they are in use either by other parameters in the statement or by 

2208 # elements of expanding parameters that were already expanded. 

2209 # copied into a mutable set, as it is added to within this method's 

2210 # scope to track the additional element names generated for this 

2211 # execution 

2212 expanded_taken_names: Set[str] = set( 

2213 self._expanded_bind_names_reserved 

2214 ) 

2215 

2216 # notes: 

2217 # *unescaped* parameter names in: 

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

2219 # 

2220 # *escaped* parameter names in: 

2221 # construct_params(), replacement_expressions 

2222 

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

2224 

2225 if self.positional and pre_expanded_positiontup is not None: 

2226 names: Iterable[str] = pre_expanded_positiontup 

2227 if self._numeric_binds: 

2228 numeric_positiontup = [] 

2229 else: 

2230 names = self.bind_names.values() 

2231 

2232 ebn = self.escaped_bind_names 

2233 for name in names: 

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

2235 parameter = self.binds[name] 

2236 

2237 if parameter in self.literal_execute_params: 

2238 if escaped_name not in replacement_expressions: 

2239 replacement_expressions[escaped_name] = ( 

2240 self.render_literal_bindparam( 

2241 parameter, 

2242 render_literal_value=parameters.pop(escaped_name), 

2243 ) 

2244 ) 

2245 continue 

2246 

2247 if parameter in self.post_compile_params: 

2248 if escaped_name in replacement_expressions: 

2249 to_update = to_update_sets[escaped_name] 

2250 values = None 

2251 else: 

2252 # we are removing the parameter from parameters 

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

2254 # TypeEngine objects that would otherwise be asked to 

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

2256 # individual numbered parameters for each value in the 

2257 # param. 

2258 # 

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

2260 # into the given dictionary. default dialect will 

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

2262 # in the escaped_bind_names dictionary. 

2263 values = parameters.pop(name) 

2264 

2265 leep_res = self._literal_execute_expanding_parameter( 

2266 escaped_name, 

2267 parameter, 

2268 values, 

2269 taken_names=expanded_taken_names, 

2270 ) 

2271 to_update, replacement_expr = leep_res 

2272 

2273 to_update_sets[escaped_name] = to_update 

2274 replacement_expressions[escaped_name] = replacement_expr 

2275 

2276 if not parameter.literal_execute: 

2277 parameters.update(to_update) 

2278 if parameter.type._is_tuple_type: 

2279 if name in tuple_processors: 

2280 # to_update is flattened row by row, one entry 

2281 # per element of each tuple 

2282 element_processors = tuple_processors[name] 

2283 num = len(element_processors) 

2284 new_processors.update( 

2285 (key, element_processors[idx % num]) 

2286 for idx, (key, _) in enumerate(to_update) 

2287 if element_processors[idx % num] is not None 

2288 ) 

2289 else: 

2290 new_processors.update( 

2291 (key, single_processors[name]) 

2292 for key, _ in to_update 

2293 if name in single_processors 

2294 ) 

2295 if numeric_positiontup is not None: 

2296 numeric_positiontup.extend( 

2297 name for name, _ in to_update 

2298 ) 

2299 elif new_positiontup is not None: 

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

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

2302 # escaped_bind_names dict. 

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

2304 expanded_parameters[name] = [ 

2305 expand_key for expand_key, _ in to_update 

2306 ] 

2307 elif new_positiontup is not None: 

2308 new_positiontup.append(name) 

2309 

2310 def process_expanding(m): 

2311 key = m.group(1) 

2312 expr = replacement_expressions[key] 

2313 

2314 # if POSTCOMPILE included a bind_expression, render that 

2315 # around each element 

2316 if m.group(2): 

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

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

2319 expr = ", ".join( 

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

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

2322 ) 

2323 return expr 

2324 

2325 statement = re.sub( 

2326 self._post_compile_pattern, process_expanding, pre_expanded_string 

2327 ) 

2328 

2329 if numeric_positiontup is not None: 

2330 assert new_positiontup is not None 

2331 param_pos = { 

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

2333 for num, key in enumerate( 

2334 numeric_positiontup, self.next_numeric_pos 

2335 ) 

2336 } 

2337 if self.preparer._double_percents: 

2338 pyformat_pattern = self._pct_escaped_pyformat_pattern 

2339 else: 

2340 pyformat_pattern = self._pyformat_pattern 

2341 

2342 # only the expanded parameters are replaced; any other 

2343 # match is an escaped percent sign or literal text that looks 

2344 # like a bound parameter 

2345 statement = pyformat_pattern.sub( 

2346 lambda m: param_pos.get(m.group(1), m.group(0)), statement 

2347 ) 

2348 new_positiontup.extend(numeric_positiontup) 

2349 

2350 expanded_state = ExpandedState( 

2351 statement, 

2352 parameters, 

2353 new_processors, 

2354 new_positiontup, 

2355 expanded_parameters, 

2356 ) 

2357 

2358 if _populate_self: 

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

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

2361 # special use cases. 

2362 self._pre_expanded_string = pre_expanded_string 

2363 self._pre_expanded_positiontup = pre_expanded_positiontup 

2364 self.string = expanded_state.statement 

2365 self.positiontup = ( 

2366 list(expanded_state.positiontup or ()) 

2367 if self.positional 

2368 else None 

2369 ) 

2370 self._post_compile_expanded_state = expanded_state 

2371 

2372 return expanded_state 

2373 

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

2375 def _create_result_map(self): 

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

2377 cursor = util.preloaded.engine_cursor 

2378 return cursor.CursorResultMetaData._create_description_match_map( 

2379 self._result_columns 

2380 ) 

2381 

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

2383 _get_bind_name_for_col: _BindNameForColProtocol 

2384 

2385 @util.memoized_property 

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

2387 getter = self._get_bind_name_for_col 

2388 return getter 

2389 

2390 @util.memoized_property 

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

2392 def _inserted_primary_key_from_lastrowid_getter(self): 

2393 result = util.preloaded.engine_result 

2394 

2395 param_key_getter = self._within_exec_param_key_getter 

2396 

2397 assert self.compile_state is not None 

2398 statement = self.compile_state.statement 

2399 

2400 if TYPE_CHECKING: 

2401 assert isinstance(statement, Insert) 

2402 

2403 table = statement.table 

2404 

2405 getters = [ 

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

2407 for col in table.primary_key 

2408 ] 

2409 

2410 autoinc_getter = None 

2411 autoinc_col = table._autoincrement_column 

2412 if autoinc_col is not None: 

2413 # apply type post processors to the lastrowid 

2414 lastrowid_processor = autoinc_col.type._cached_result_processor( 

2415 self.dialect, None 

2416 ) 

2417 autoinc_key = param_key_getter(autoinc_col) 

2418 

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

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

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

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

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

2424 if autoinc_key in self.binds: 

2425 

2426 def _autoinc_getter(lastrowid, parameters): 

2427 param_value = parameters.get(autoinc_key, lastrowid) 

2428 if param_value is not None: 

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

2430 # SQLite at least is observed to return the wrong 

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

2432 # can't be used in all cases 

2433 return param_value 

2434 else: 

2435 # use lastrowid 

2436 return lastrowid 

2437 

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

2439 autoinc_getter = _autoinc_getter 

2440 

2441 else: 

2442 lastrowid_processor = None 

2443 

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

2445 

2446 def get(lastrowid, parameters): 

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

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

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

2450 that were sent along with the INSERT. 

2451 

2452 """ 

2453 if lastrowid_processor is not None: 

2454 lastrowid = lastrowid_processor(lastrowid) 

2455 

2456 if lastrowid is None: 

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

2458 else: 

2459 return row_fn( 

2460 ( 

2461 ( 

2462 autoinc_getter(lastrowid, parameters) 

2463 if autoinc_getter is not None 

2464 else lastrowid 

2465 ) 

2466 if col is autoinc_col 

2467 else getter(parameters) 

2468 ) 

2469 for getter, col in getters 

2470 ) 

2471 

2472 return get 

2473 

2474 @util.memoized_property 

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

2476 def _inserted_primary_key_from_returning_getter(self): 

2477 result = util.preloaded.engine_result 

2478 

2479 assert self.compile_state is not None 

2480 statement = self.compile_state.statement 

2481 

2482 if TYPE_CHECKING: 

2483 assert isinstance(statement, Insert) 

2484 

2485 param_key_getter = self._within_exec_param_key_getter 

2486 table = statement.table 

2487 

2488 returning = self.implicit_returning 

2489 assert returning is not None 

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

2491 

2492 getters = cast( 

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

2494 [ 

2495 ( 

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

2497 if col in ret 

2498 else ( 

2499 operator.methodcaller( 

2500 "get", param_key_getter(col), None 

2501 ), 

2502 False, 

2503 ) 

2504 ) 

2505 for col in table.primary_key 

2506 ], 

2507 ) 

2508 

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

2510 

2511 def get(row, parameters): 

2512 return row_fn( 

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

2514 for getter, use_row in getters 

2515 ) 

2516 

2517 return get 

2518 

2519 def default_from(self) -> str: 

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

2521 to be appended. 

2522 

2523 Gives Oracle Database a chance to tack on a ``FROM DUAL`` to the string 

2524 output. 

2525 

2526 """ 

2527 return "" 

2528 

2529 def visit_override_binds( 

2530 self, 

2531 override_binds, 

2532 add_to_result_map=None, 

2533 result_map_targets=(), 

2534 **kw, 

2535 ): 

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

2537 bindparams swapped out. 

2538 

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

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

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

2542 be swapped in at execution time. 

2543 

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

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

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

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

2548 

2549 """ 

2550 

2551 if add_to_result_map is not None: 

2552 # the ORM looks up result columns using the objects it placed 

2553 # into the loader option, which for a cached statement is the 

2554 # _OverrideBinds wrapper rather than the element inside of it. 

2555 # make sure the wrapper is present in the result map so that 

2556 # lookup succeeds. See #13560 

2557 result_map_targets += (override_binds,) 

2558 kw["add_to_result_map"] = add_to_result_map 

2559 kw["result_map_targets"] = result_map_targets 

2560 

2561 # get SQL text first 

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

2563 

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

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

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

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

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

2569 # test_relationship_criteria.py -> 

2570 # test_selectinload_local_criteria_subquery 

2571 for k in override_binds.translate: 

2572 if k not in self.binds: 

2573 continue 

2574 bp = self.binds[k] 

2575 

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

2577 # but we dont want to mutate things outside. 

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

2579 # continue 

2580 

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

2582 # in all internal collections 

2583 new_bp = bp._with_value( 

2584 override_binds.translate[bp.key], 

2585 maintain_key=True, 

2586 required=False, 

2587 ) 

2588 

2589 name = self.bind_names[bp] 

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

2591 self.bind_names[new_bp] = name 

2592 self.bind_names.pop(bp, None) 

2593 

2594 if bp in self.post_compile_params: 

2595 self.post_compile_params |= {new_bp} 

2596 if bp in self.literal_execute_params: 

2597 self.literal_execute_params |= {new_bp} 

2598 

2599 ckbm_tuple = self._cache_key_bind_match 

2600 if ckbm_tuple: 

2601 ckbm, cksm = ckbm_tuple 

2602 for bp in bp._cloned_set: 

2603 if bp.key in cksm: 

2604 cb = cksm[bp.key] 

2605 ckbm[cb].append(new_bp) 

2606 

2607 return sqltext 

2608 

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

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

2611 

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

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

2614 

2615 def visit_label_reference( 

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

2617 ): 

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

2619 try: 

2620 compile_state = cast( 

2621 "Union[SelectState, CompoundSelectState]", 

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

2623 ) 

2624 except KeyError as ke: 

2625 raise exc.CompileError( 

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

2627 "GROUP BY / DISTINCT etc." 

2628 ) from ke 

2629 

2630 ( 

2631 with_cols, 

2632 only_froms, 

2633 only_cols, 

2634 ) = compile_state._label_resolve_dict 

2635 if within_columns_clause: 

2636 resolve_dict = only_froms 

2637 else: 

2638 resolve_dict = only_cols 

2639 

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

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

2642 # the replacement of the Label element may have changed 

2643 # to something else like a ColumnClause expression. 

2644 order_by_elem = element.element._order_by_label_element 

2645 

2646 if ( 

2647 order_by_elem is not None 

2648 and order_by_elem.name in resolve_dict 

2649 and order_by_elem.shares_lineage( 

2650 resolve_dict[order_by_elem.name] 

2651 ) 

2652 ): 

2653 kwargs["render_label_as_label"] = ( 

2654 element.element._order_by_label_element 

2655 ) 

2656 return self.process( 

2657 element.element, 

2658 within_columns_clause=within_columns_clause, 

2659 **kwargs, 

2660 ) 

2661 

2662 def visit_textual_label_reference( 

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

2664 ): 

2665 if not self.stack: 

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

2667 return self.process(element._text_clause) 

2668 

2669 try: 

2670 compile_state = cast( 

2671 "Union[SelectState, CompoundSelectState]", 

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

2673 ) 

2674 except KeyError as ke: 

2675 coercions._no_text_coercion( 

2676 element.element, 

2677 extra=( 

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

2679 "GROUP BY / DISTINCT etc." 

2680 ), 

2681 exc_cls=exc.CompileError, 

2682 err=ke, 

2683 ) 

2684 

2685 with_cols, only_froms, only_cols = compile_state._label_resolve_dict 

2686 try: 

2687 if within_columns_clause: 

2688 col = only_froms[element.element] 

2689 else: 

2690 col = with_cols[element.element] 

2691 except KeyError as err: 

2692 coercions._no_text_coercion( 

2693 element.element, 

2694 extra=( 

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

2696 "GROUP BY / DISTINCT etc." 

2697 ), 

2698 exc_cls=exc.CompileError, 

2699 err=err, 

2700 ) 

2701 else: 

2702 kwargs["render_label_as_label"] = col 

2703 return self.process( 

2704 col, within_columns_clause=within_columns_clause, **kwargs 

2705 ) 

2706 

2707 def visit_label( 

2708 self, 

2709 label, 

2710 add_to_result_map=None, 

2711 within_label_clause=False, 

2712 within_columns_clause=False, 

2713 render_label_as_label=None, 

2714 result_map_targets=(), 

2715 within_tstring=False, 

2716 **kw, 

2717 ): 

2718 if within_tstring: 

2719 raise exc.CompileError( 

2720 "Using label() directly inside tstring is not supported " 

2721 "as it is ambiguous how the label expression should be " 

2722 "rendered without knowledge of how it's being used in SQL" 

2723 ) 

2724 # only render labels within the columns clause 

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

2726 # can modify this behavior. 

2727 render_label_with_as = ( 

2728 within_columns_clause and not within_label_clause 

2729 ) 

2730 render_label_only = render_label_as_label is label 

2731 

2732 if render_label_only or render_label_with_as: 

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

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

2735 else: 

2736 labelname = label.name 

2737 

2738 if render_label_with_as: 

2739 if add_to_result_map is not None: 

2740 add_to_result_map( 

2741 labelname, 

2742 label.name, 

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

2744 label.type, 

2745 ) 

2746 return ( 

2747 label.element._compiler_dispatch( 

2748 self, 

2749 within_columns_clause=True, 

2750 within_label_clause=True, 

2751 **kw, 

2752 ) 

2753 + OPERATORS[operators.as_] 

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

2755 ) 

2756 elif render_label_only: 

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

2758 else: 

2759 return label.element._compiler_dispatch( 

2760 self, within_columns_clause=False, **kw 

2761 ) 

2762 

2763 def _fallback_column_name(self, column): 

2764 raise exc.CompileError( 

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

2766 ) 

2767 

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

2769 sql_element = element._resolved 

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

2771 

2772 def visit_column( 

2773 self, 

2774 column: ColumnClause[Any], 

2775 add_to_result_map: Optional[_ResultMapAppender] = None, 

2776 include_table: bool = True, 

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

2778 ambiguous_table_name_map: Optional[_AmbiguousTableNameMap] = None, 

2779 **kwargs: Any, 

2780 ) -> str: 

2781 name = orig_name = column.name 

2782 if name is None: 

2783 name = self._fallback_column_name(column) 

2784 

2785 is_literal = column.is_literal 

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

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

2788 

2789 if add_to_result_map is not None: 

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

2791 if column._tq_label: 

2792 targets += (column._tq_label,) 

2793 

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

2795 

2796 if is_literal: 

2797 # note we are not currently accommodating for 

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

2799 name = self.escape_literal_column(name) 

2800 else: 

2801 name = self.preparer.quote(name) 

2802 table = column.table 

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

2804 return name 

2805 else: 

2806 effective_schema = self.preparer.schema_for_object(table) 

2807 

2808 if effective_schema: 

2809 schema_prefix = ( 

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

2811 ) 

2812 else: 

2813 schema_prefix = "" 

2814 

2815 if TYPE_CHECKING: 

2816 assert isinstance(table, NamedFromClause) 

2817 tablename = table.name 

2818 

2819 if ( 

2820 not effective_schema 

2821 and ambiguous_table_name_map 

2822 and tablename in ambiguous_table_name_map 

2823 ): 

2824 tablename = ambiguous_table_name_map[tablename] 

2825 

2826 if isinstance(tablename, elements._truncated_label): 

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

2828 

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

2830 

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

2832 return self.preparer.format_collation( 

2833 element.collation, element.collation_schema 

2834 ) 

2835 

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

2837 return fromclause.name 

2838 

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

2840 return index.name 

2841 

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

2843 kw["type_expression"] = typeclause 

2844 kw["identifier_preparer"] = self.preparer 

2845 return self.dialect.type_compiler_instance.process( 

2846 typeclause.type, **kw 

2847 ) 

2848 

2849 def post_process_text(self, text): 

2850 if self.preparer._double_percents: 

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

2852 return text 

2853 

2854 def escape_literal_column(self, text): 

2855 if self.preparer._double_percents: 

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

2857 return text 

2858 

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

2860 if self._collect_params: 

2861 self._add_to_params(textclause) 

2862 

2863 def do_bindparam(m): 

2864 name = m.group(1) 

2865 if name in textclause._bindparams: 

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

2867 else: 

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

2869 

2870 if not self.stack: 

2871 self.isplaintext = True 

2872 

2873 if add_to_result_map: 

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

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

2876 # row[text()] produces a result 

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

2878 

2879 # un-escape any \:params 

2880 return BIND_PARAMS_ESC.sub( 

2881 lambda m: m.group(1), 

2882 BIND_PARAMS.sub( 

2883 do_bindparam, self.post_process_text(textclause.text) 

2884 ), 

2885 ) 

2886 

2887 def visit_tstring_text(self, element, **kw): 

2888 return self.post_process_text(element.text) 

2889 

2890 def visit_tstring(self, tstring, add_to_result_map=None, **kw): 

2891 if self._collect_params: 

2892 self._add_to_params(tstring) 

2893 

2894 if not self.stack: 

2895 self.isplaintext = True 

2896 

2897 if add_to_result_map: 

2898 # tstring() object is present in the columns clause of a 

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

2900 # row[tstring()] produces a result 

2901 add_to_result_map(None, None, (tstring,), sqltypes.NULLTYPE) 

2902 

2903 # Process each part and concatenate 

2904 kw["within_tstring"] = True 

2905 return "".join(self.process(part, **kw) for part in tstring.parts) 

2906 

2907 def visit_textual_select( 

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

2909 ): 

2910 if self._collect_params: 

2911 self._add_to_params(taf) 

2912 toplevel = not self.stack 

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

2914 

2915 new_entry: _CompilerStackEntry = { 

2916 "correlate_froms": set(), 

2917 "asfrom_froms": set(), 

2918 "selectable": taf, 

2919 } 

2920 self.stack.append(new_entry) 

2921 

2922 if taf._independent_ctes: 

2923 self._dispatch_independent_ctes(taf, kw) 

2924 

2925 populate_result_map = ( 

2926 toplevel 

2927 or ( 

2928 compound_index == 0 

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

2930 ) 

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

2932 ) 

2933 

2934 if populate_result_map: 

2935 self._ordered_columns = self._textual_ordered_columns = ( 

2936 taf.positional 

2937 ) 

2938 

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

2940 # Column objects by name only 

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

2942 taf.column_args 

2943 ) 

2944 

2945 for c in taf.column_args: 

2946 self.process( 

2947 c, 

2948 within_columns_clause=True, 

2949 add_to_result_map=self._add_to_result_map, 

2950 ) 

2951 

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

2953 if self.ctes: 

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

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

2956 

2957 self.stack.pop(-1) 

2958 

2959 return text 

2960 

2961 def visit_null(self, expr: Null, **kw: Any) -> str: 

2962 return "NULL" 

2963 

2964 def visit_true(self, expr: True_, **kw: Any) -> str: 

2965 if self.dialect.supports_native_boolean: 

2966 return "true" 

2967 else: 

2968 return "1" 

2969 

2970 def visit_false(self, expr: False_, **kw: Any) -> str: 

2971 if self.dialect.supports_native_boolean: 

2972 return "false" 

2973 else: 

2974 return "0" 

2975 

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

2977 return separator.join( 

2978 s 

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

2980 if s 

2981 ) 

2982 

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

2984 lcc, clauses = elements.BooleanClauseList._process_clauses_for_boolean( 

2985 operators.and_, 

2986 elements.True_._singleton, 

2987 elements.False_._singleton, 

2988 clauses, 

2989 ) 

2990 if lcc == 1: 

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

2992 else: 

2993 separator = OPERATORS[operators.and_] 

2994 return separator.join( 

2995 s 

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

2997 if s 

2998 ) 

2999 

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

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

3002 

3003 def visit_element_list(self, element, **kw): 

3004 return self._generate_delimited_list(element.clauses, " ", **kw) 

3005 

3006 def visit_order_by_list(self, element, **kw): 

3007 return self._generate_delimited_list(element.clauses, ", ", **kw) 

3008 

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

3010 sep = clauselist.operator 

3011 if sep is None: 

3012 sep = " " 

3013 else: 

3014 sep = OPERATORS[clauselist.operator] 

3015 

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

3017 

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

3019 operator_ = clauselist.operator 

3020 

3021 disp = self._get_operator_dispatch( 

3022 operator_, "expression_clauselist", None 

3023 ) 

3024 if disp: 

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

3026 

3027 try: 

3028 opstring = OPERATORS[operator_] 

3029 except KeyError as err: 

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

3031 else: 

3032 kw["_in_operator_expression"] = True 

3033 return self._generate_delimited_list( 

3034 clauselist.clauses, opstring, **kw 

3035 ) 

3036 

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

3038 x = "CASE " 

3039 if clause.value is not None: 

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

3041 for cond, result in clause.whens: 

3042 x += ( 

3043 "WHEN " 

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

3045 + " THEN " 

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

3047 + " " 

3048 ) 

3049 if clause.else_ is not None: 

3050 x += ( 

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

3052 ) 

3053 x += "END" 

3054 return x 

3055 

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

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

3058 

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

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

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

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

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

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

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

3066 ) 

3067 

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

3069 

3070 if frameclause.lower_type is elements.FrameClauseType.UNBOUNDED: 

3071 left = "UNBOUNDED PRECEDING" 

3072 elif frameclause.lower_type is elements.FrameClauseType.CURRENT: 

3073 left = "CURRENT ROW" 

3074 else: 

3075 val = self.process(frameclause.lower_bind, **kw) 

3076 if frameclause.lower_type is elements.FrameClauseType.PRECEDING: 

3077 left = f"{val} PRECEDING" 

3078 else: 

3079 left = f"{val} FOLLOWING" 

3080 

3081 if frameclause.upper_type is elements.FrameClauseType.UNBOUNDED: 

3082 right = "UNBOUNDED FOLLOWING" 

3083 elif frameclause.upper_type is elements.FrameClauseType.CURRENT: 

3084 right = "CURRENT ROW" 

3085 else: 

3086 val = self.process(frameclause.upper_bind, **kw) 

3087 if frameclause.upper_type is elements.FrameClauseType.PRECEDING: 

3088 right = f"{val} PRECEDING" 

3089 else: 

3090 right = f"{val} FOLLOWING" 

3091 

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

3093 

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

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

3096 if over.range_ is not None: 

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

3098 elif over.rows is not None: 

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

3100 elif over.groups is not None: 

3101 range_ = f"GROUPS BETWEEN {self.process(over.groups, **kwargs)}" 

3102 else: 

3103 range_ = None 

3104 

3105 if range_ is not None and over.exclude is not None: 

3106 range_ += " EXCLUDE " + self.preparer.validate_sql_phrase( 

3107 over.exclude, _WINDOW_EXCLUDE_RE 

3108 ) 

3109 

3110 return "%s OVER (%s)" % ( 

3111 text, 

3112 " ".join( 

3113 [ 

3114 "%s BY %s" 

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

3116 for word, clause in ( 

3117 ("PARTITION", over.partition_by), 

3118 ("ORDER", over.order_by), 

3119 ) 

3120 if clause is not None and len(clause) 

3121 ] 

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

3123 ), 

3124 ) 

3125 

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

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

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

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

3130 ) 

3131 

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

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

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

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

3136 ) 

3137 

3138 def visit_aggregateorderby(self, aggregateorderby, **kwargs): 

3139 if self.dialect.aggregate_order_by_style is AggregateOrderByStyle.NONE: 

3140 raise exc.CompileError( 

3141 "this dialect does not support " 

3142 "ORDER BY within an aggregate function" 

3143 ) 

3144 elif ( 

3145 self.dialect.aggregate_order_by_style 

3146 is AggregateOrderByStyle.INLINE 

3147 ): 

3148 new_fn = aggregateorderby.element._clone() 

3149 new_fn.clause_expr = elements.Grouping( 

3150 aggregate_orderby_inline( 

3151 new_fn.clause_expr.element, aggregateorderby.order_by 

3152 ) 

3153 ) 

3154 

3155 return new_fn._compiler_dispatch(self, **kwargs) 

3156 else: 

3157 return self.visit_withingroup(aggregateorderby, **kwargs) 

3158 

3159 def visit_aggregate_orderby_inline(self, element, **kw): 

3160 return "%s ORDER BY %s" % ( 

3161 self.process(element.element, **kw), 

3162 self.process(element.aggregate_order_by, **kw), 

3163 ) 

3164 

3165 def visit_aggregate_strings_func( 

3166 self, fn, *, use_function_name=None, **kw 

3167 ): 

3168 # aggreagate_order_by attribute is present if visit_function 

3169 # gave us a Function with aggregate_orderby_inline() as the inner 

3170 # contents 

3171 order_by = getattr(fn.clauses, "aggregate_order_by", None) 

3172 

3173 if use_function_name is None: 

3174 raise exc.CompileError( 

3175 "%s dialect does not support the aggregate_strings function" 

3176 % self.dialect.name 

3177 ) 

3178 

3179 literal_exec = dict(kw) 

3180 literal_exec["literal_execute"] = True 

3181 

3182 # break up the function into its components so we can apply 

3183 # literal_execute to the second argument (the delimiter) 

3184 cl = list(fn.clauses) 

3185 expr, delimiter = cl[0:2] 

3186 if ( 

3187 order_by is not None 

3188 and self.dialect.aggregate_order_by_style 

3189 is AggregateOrderByStyle.INLINE 

3190 ): 

3191 return ( 

3192 f"{use_function_name}({expr._compiler_dispatch(self, **kw)}, " 

3193 f"{delimiter._compiler_dispatch(self, **literal_exec)} " 

3194 f"ORDER BY {order_by._compiler_dispatch(self, **kw)})" 

3195 ) 

3196 else: 

3197 return ( 

3198 f"{use_function_name}({expr._compiler_dispatch(self, **kw)}, " 

3199 f"{delimiter._compiler_dispatch(self, **literal_exec)})" 

3200 ) 

3201 

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

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

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

3205 field, 

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

3207 ) 

3208 

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

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

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

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

3213 

3214 def visit_function( 

3215 self, 

3216 func: Function[Any], 

3217 add_to_result_map: Optional[_ResultMapAppender] = None, 

3218 **kwargs: Any, 

3219 ) -> str: 

3220 if self._collect_params: 

3221 self._add_to_params(func) 

3222 if add_to_result_map is not None: 

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

3224 

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

3226 

3227 text: str 

3228 

3229 kwargs["within_aggregate_function"] = True 

3230 

3231 if disp: 

3232 text = disp(func, **kwargs) 

3233 else: 

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

3235 if name: 

3236 if func._has_args: 

3237 name += "%(expr)s" 

3238 else: 

3239 name = func.name 

3240 name = ( 

3241 self.preparer.quote(name) 

3242 if self.preparer._requires_quotes_illegal_chars(name) 

3243 or isinstance(name, elements.quoted_name) 

3244 else name 

3245 ) 

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

3247 text = ".".join( 

3248 [ 

3249 ( 

3250 self.preparer.quote(tok) 

3251 if self.preparer._requires_quotes_illegal_chars(tok) 

3252 or isinstance(name, elements.quoted_name) 

3253 else tok 

3254 ) 

3255 for tok in func.packagenames 

3256 ] 

3257 + [name] 

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

3259 

3260 if func._with_ordinality: 

3261 text += " WITH ORDINALITY" 

3262 return text 

3263 

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

3265 return self.visit_sequence(next_value.sequence) 

3266 

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

3268 raise NotImplementedError( 

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

3270 % self.dialect.name 

3271 ) 

3272 

3273 def function_argspec(self, func: Function[Any], **kwargs: Any) -> str: 

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

3275 

3276 def visit_compound_select( 

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

3278 ): 

3279 if self._collect_params: 

3280 self._add_to_params(cs) 

3281 toplevel = not self.stack 

3282 

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

3284 

3285 if toplevel and not self.compile_state: 

3286 self.compile_state = compile_state 

3287 

3288 compound_stmt = compile_state.statement 

3289 

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

3291 need_result_map = toplevel or ( 

3292 not compound_index 

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

3294 ) 

3295 

3296 # indicates there is already a CompoundSelect in play 

3297 if compound_index == 0: 

3298 entry["select_0"] = cs 

3299 

3300 self.stack.append( 

3301 { 

3302 "correlate_froms": entry["correlate_froms"], 

3303 "asfrom_froms": entry["asfrom_froms"], 

3304 "selectable": cs, 

3305 "compile_state": compile_state, 

3306 "need_result_map_for_compound": need_result_map, 

3307 } 

3308 ) 

3309 

3310 if compound_stmt._independent_ctes: 

3311 self._dispatch_independent_ctes(compound_stmt, kwargs) 

3312 

3313 keyword = self.compound_keywords[cs.keyword] 

3314 

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

3316 ( 

3317 c._compiler_dispatch( 

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

3319 ) 

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

3321 ) 

3322 ) 

3323 

3324 kwargs["include_table"] = False 

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

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

3327 if cs._has_row_limiting_clause: 

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

3329 

3330 if self.ctes: 

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

3332 text = ( 

3333 self._render_cte_clause( 

3334 nesting_level=nesting_level, 

3335 include_following_stack=True, 

3336 ) 

3337 + text 

3338 ) 

3339 

3340 self.stack.pop(-1) 

3341 return text 

3342 

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

3344 if cs._fetch_clause is not None: 

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

3346 else: 

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

3348 

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

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

3351 operator_.__name__, 

3352 qualifier1, 

3353 "_" + qualifier2 if qualifier2 else "", 

3354 ) 

3355 return getattr(self, attrname, None) 

3356 

3357 def _get_custom_operator_dispatch(self, operator_, qualifier1): 

3358 attrname = "visit_%s_op_%s" % (operator_.visit_name, qualifier1) 

3359 return getattr(self, attrname, None) 

3360 

3361 def visit_unary( 

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

3363 ): 

3364 if add_to_result_map is not None: 

3365 result_map_targets += (unary,) 

3366 kw["add_to_result_map"] = add_to_result_map 

3367 kw["result_map_targets"] = result_map_targets 

3368 

3369 if unary.operator is operators.distinct_op and not kw.get( 

3370 "within_aggregate_function", False 

3371 ): 

3372 util.warn( 

3373 "Column-expression-level unary distinct() " 

3374 "should not be used outside of an aggregate " 

3375 "function. For general 'SELECT DISTINCT' support " 

3376 "use select().distinct()." 

3377 ) 

3378 

3379 if unary.operator: 

3380 if unary.modifier: 

3381 raise exc.CompileError( 

3382 "Unary expression does not support operator " 

3383 "and modifier simultaneously" 

3384 ) 

3385 disp = self._get_operator_dispatch( 

3386 unary.operator, "unary", "operator" 

3387 ) 

3388 if disp: 

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

3390 else: 

3391 return self._generate_generic_unary_operator( 

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

3393 ) 

3394 elif unary.modifier: 

3395 disp = self._get_operator_dispatch( 

3396 unary.modifier, "unary", "modifier" 

3397 ) 

3398 if disp: 

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

3400 else: 

3401 return self._generate_generic_unary_modifier( 

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

3403 ) 

3404 else: 

3405 raise exc.CompileError( 

3406 "Unary expression has no operator or modifier" 

3407 ) 

3408 

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

3410 if self.dialect.div_is_floordiv: 

3411 return ( 

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

3413 + " / " 

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

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

3416 + self.process( 

3417 elements.Cast( 

3418 binary.right, 

3419 ( 

3420 binary.right.type 

3421 if binary.right.type._type_affinity 

3422 in (sqltypes.Numeric, sqltypes.Float) 

3423 else sqltypes.Numeric() 

3424 ), 

3425 ), 

3426 **kw, 

3427 ) 

3428 ) 

3429 else: 

3430 return ( 

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

3432 + " / " 

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

3434 ) 

3435 

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

3437 if ( 

3438 self.dialect.div_is_floordiv 

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

3440 and binary.left.type._type_affinity is sqltypes.Integer 

3441 ): 

3442 return ( 

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

3444 + " / " 

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

3446 ) 

3447 else: 

3448 return "FLOOR(%s)" % ( 

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

3450 + " / " 

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

3452 ) 

3453 

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

3455 if ( 

3456 element._is_implicitly_boolean 

3457 or self.dialect.supports_native_boolean 

3458 ): 

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

3460 else: 

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

3462 

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

3464 if ( 

3465 element._is_implicitly_boolean 

3466 or self.dialect.supports_native_boolean 

3467 ): 

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

3469 else: 

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

3471 

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

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

3474 binary, override_operator=operators.match_op 

3475 ) 

3476 

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

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

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

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

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

3482 binary, OPERATORS[operator], **kw 

3483 ) 

3484 

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

3486 if expand_op is operators.not_in_op: 

3487 if len(type_) > 1: 

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

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

3490 ) 

3491 else: 

3492 return "NULL) OR (1 = 1" 

3493 elif expand_op is operators.in_op: 

3494 if len(type_) > 1: 

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

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

3497 ) 

3498 else: 

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

3500 else: 

3501 return self.visit_empty_set_expr(type_) 

3502 

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

3504 raise NotImplementedError( 

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

3506 % self.dialect.name 

3507 ) 

3508 

3509 def _literal_execute_expanding_parameter_literal_binds( 

3510 self, parameter, values, bind_expression_template=None 

3511 ): 

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

3513 

3514 if not values: 

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

3516 # bind_expression_template here because there are no 

3517 # expressions to render. 

3518 

3519 if typ_dialect_impl._is_tuple_type: 

3520 replacement_expression = ( 

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

3522 ) + self.visit_empty_set_op_expr( 

3523 parameter.type.types, parameter.expand_op 

3524 ) 

3525 

3526 else: 

3527 replacement_expression = self.visit_empty_set_op_expr( 

3528 [parameter.type], parameter.expand_op 

3529 ) 

3530 

3531 elif typ_dialect_impl._is_tuple_type or ( 

3532 typ_dialect_impl._isnull 

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

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

3535 ): 

3536 if typ_dialect_impl._has_bind_expression: 

3537 raise NotImplementedError( 

3538 "bind_expression() on TupleType not supported with " 

3539 "literal_binds" 

3540 ) 

3541 

3542 replacement_expression = ( 

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

3544 ) + ", ".join( 

3545 "(%s)" 

3546 % ( 

3547 ", ".join( 

3548 self.render_literal_value(value, param_type) 

3549 for value, param_type in zip( 

3550 tuple_element, parameter.type.types 

3551 ) 

3552 ) 

3553 ) 

3554 for i, tuple_element in enumerate(values) 

3555 ) 

3556 else: 

3557 if bind_expression_template: 

3558 post_compile_pattern = self._post_compile_pattern 

3559 m = post_compile_pattern.search(bind_expression_template) 

3560 assert m and m.group( 

3561 2 

3562 ), "unexpected format for expanding parameter" 

3563 

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

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

3566 replacement_expression = ", ".join( 

3567 "%s%s%s" 

3568 % ( 

3569 be_left, 

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

3571 be_right, 

3572 ) 

3573 for value in values 

3574 ) 

3575 else: 

3576 replacement_expression = ", ".join( 

3577 self.render_literal_value(value, parameter.type) 

3578 for value in values 

3579 ) 

3580 

3581 return (), replacement_expression 

3582 

3583 def _literal_execute_expanding_parameter( 

3584 self, name, parameter, values, *, taken_names 

3585 ): 

3586 """Render the per-element bound parameters of an expanding 

3587 parameter. 

3588 

3589 ``taken_names`` is a set of names the elements may not use; each 

3590 name generated is checked against it and added to it. The element 

3591 names are normally ``<name>_<n>``, or ``<name>_<n>_<m>`` for tuples, 

3592 which may be the name of another parameter in the same statement, 

3593 e.g. a parameter ``x_1`` alongside the expanding parameter ``x``, or 

3594 the element of another expanding parameter. A colliding name is 

3595 uniquified with a counter, rather than the element value replacing 

3596 the value of the other parameter. See :ticket:`13695`. 

3597 

3598 """ 

3599 if parameter.literal_execute: 

3600 return self._literal_execute_expanding_parameter_literal_binds( 

3601 parameter, values 

3602 ) 

3603 

3604 dialect = self.dialect 

3605 typ_dialect_impl = parameter.type._unwrapped_dialect_impl(dialect) 

3606 

3607 if self._numeric_binds: 

3608 bind_template = self.compilation_bindtemplate 

3609 else: 

3610 bind_template = self.bindtemplate 

3611 

3612 if ( 

3613 self.dialect._bind_typing_render_casts 

3614 and typ_dialect_impl.render_bind_cast 

3615 ): 

3616 

3617 def _render_bindtemplate(name): 

3618 return self.render_bind_cast( 

3619 parameter.type, 

3620 typ_dialect_impl, 

3621 bind_template % {"name": name}, 

3622 ) 

3623 

3624 else: 

3625 

3626 def _render_bindtemplate(name): 

3627 return bind_template % {"name": name} 

3628 

3629 def _unique_name(key): 

3630 if key in taken_names: 

3631 base = key 

3632 counter = 0 

3633 while key in taken_names: 

3634 counter += 1 

3635 # double underscore keeps the suffix out of the 

3636 # <name>_<n> form generated for anonymous names and 

3637 # for the elements themselves, so a renamed element 

3638 # won't take a name another element would generate 

3639 key = "%s__%d" % (base, counter) 

3640 taken_names.add(key) 

3641 return key 

3642 

3643 if not values: 

3644 to_update = [] 

3645 if typ_dialect_impl._is_tuple_type: 

3646 replacement_expression = self.visit_empty_set_op_expr( 

3647 parameter.type.types, parameter.expand_op 

3648 ) 

3649 else: 

3650 replacement_expression = self.visit_empty_set_op_expr( 

3651 [parameter.type], parameter.expand_op 

3652 ) 

3653 

3654 elif typ_dialect_impl._is_tuple_type or ( 

3655 typ_dialect_impl._isnull 

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

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

3658 ): 

3659 assert not typ_dialect_impl._is_array 

3660 to_update = [ 

3661 (_unique_name("%s_%s_%s" % (name, i, j)), value) 

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

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

3664 ] 

3665 

3666 replacement_expression = ( 

3667 "VALUES " if dialect.tuple_in_values else "" 

3668 ) + ", ".join( 

3669 "(%s)" 

3670 % ( 

3671 ", ".join( 

3672 _render_bindtemplate( 

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

3674 ) 

3675 for j, value in enumerate(tuple_element) 

3676 ) 

3677 ) 

3678 for i, tuple_element in enumerate(values) 

3679 ) 

3680 else: 

3681 to_update = [ 

3682 (_unique_name("%s_%s" % (name, i)), value) 

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

3684 ] 

3685 replacement_expression = ", ".join( 

3686 _render_bindtemplate(key) for key, value in to_update 

3687 ) 

3688 

3689 return to_update, replacement_expression 

3690 

3691 def visit_binary( 

3692 self, 

3693 binary, 

3694 override_operator=None, 

3695 eager_grouping=False, 

3696 from_linter=None, 

3697 lateral_from_linter=None, 

3698 **kw, 

3699 ): 

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

3701 if lateral_from_linter is not None: 

3702 enclosing_lateral = kw["enclosing_lateral"] 

3703 lateral_from_linter.edges.update( 

3704 itertools.product( 

3705 _de_clone( 

3706 binary.left._from_objects + [enclosing_lateral] 

3707 ), 

3708 _de_clone( 

3709 binary.right._from_objects + [enclosing_lateral] 

3710 ), 

3711 ) 

3712 ) 

3713 else: 

3714 from_linter.edges.update( 

3715 itertools.product( 

3716 _de_clone(binary.left._from_objects), 

3717 _de_clone(binary.right._from_objects), 

3718 ) 

3719 ) 

3720 

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

3722 if ( 

3723 self.ansi_bind_rules 

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

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

3726 ): 

3727 kw["literal_execute"] = True 

3728 

3729 operator_ = override_operator or binary.operator 

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

3731 if disp: 

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

3733 else: 

3734 try: 

3735 opstring = OPERATORS[operator_] 

3736 except KeyError as err: 

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

3738 else: 

3739 return self._generate_generic_binary( 

3740 binary, 

3741 opstring, 

3742 from_linter=from_linter, 

3743 lateral_from_linter=lateral_from_linter, 

3744 **kw, 

3745 ) 

3746 

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

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

3749 

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

3751 if self.preparer._double_percents: 

3752 return ( 

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

3754 + " %% " 

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

3756 ) 

3757 else: 

3758 return ( 

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

3760 + " % " 

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

3762 ) 

3763 

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

3765 if operator.visit_name: 

3766 disp = self._get_custom_operator_dispatch(operator, "binary") 

3767 if disp: 

3768 return disp(element, operator, **kw) 

3769 

3770 kw["eager_grouping"] = operator.eager_grouping 

3771 return self._generate_generic_binary( 

3772 element, 

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

3774 **kw, 

3775 ) 

3776 

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

3778 if operator.visit_name: 

3779 disp = self._get_custom_operator_dispatch(operator, "unary") 

3780 if disp: 

3781 return disp(element, operator, **kw) 

3782 

3783 return self._generate_generic_unary_operator( 

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

3785 ) 

3786 

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

3788 if operator.visit_name: 

3789 disp = self._get_custom_operator_dispatch(operator, "unary") 

3790 if disp: 

3791 return disp(element, operator, **kw) 

3792 

3793 return self._generate_generic_unary_modifier( 

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

3795 ) 

3796 

3797 def _generate_generic_binary( 

3798 self, 

3799 binary: BinaryExpression[Any], 

3800 opstring: str, 

3801 eager_grouping: bool = False, 

3802 **kw: Any, 

3803 ) -> str: 

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

3805 

3806 kw["_in_operator_expression"] = True 

3807 kw["_binary_op"] = binary.operator 

3808 text = ( 

3809 binary.left._compiler_dispatch( 

3810 self, eager_grouping=eager_grouping, **kw 

3811 ) 

3812 + opstring 

3813 + binary.right._compiler_dispatch( 

3814 self, eager_grouping=eager_grouping, **kw 

3815 ) 

3816 ) 

3817 

3818 if _in_operator_expression and eager_grouping: 

3819 text = "(%s)" % text 

3820 return text 

3821 

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

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

3824 

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

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

3827 

3828 @util.memoized_property 

3829 def _like_percent_literal(self): 

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

3831 

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

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

3834 

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

3836 binary = binary._clone() 

3837 percent = self._like_percent_literal 

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

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

3840 

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

3842 binary = binary._clone() 

3843 percent = self._like_percent_literal 

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

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

3846 

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

3848 binary = binary._clone() 

3849 percent = self._like_percent_literal 

3850 binary.left = ilike_case_insensitive(binary.left) 

3851 binary.right = percent.concat( 

3852 ilike_case_insensitive(binary.right) 

3853 ).concat(percent) 

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

3855 

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

3857 binary = binary._clone() 

3858 percent = self._like_percent_literal 

3859 binary.left = ilike_case_insensitive(binary.left) 

3860 binary.right = percent.concat( 

3861 ilike_case_insensitive(binary.right) 

3862 ).concat(percent) 

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

3864 

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

3866 binary = binary._clone() 

3867 percent = self._like_percent_literal 

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

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

3870 

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

3872 binary = binary._clone() 

3873 percent = self._like_percent_literal 

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

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

3876 

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

3878 binary = binary._clone() 

3879 percent = self._like_percent_literal 

3880 binary.left = ilike_case_insensitive(binary.left) 

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

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

3883 

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

3885 binary = binary._clone() 

3886 percent = self._like_percent_literal 

3887 binary.left = ilike_case_insensitive(binary.left) 

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

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

3890 

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

3892 binary = binary._clone() 

3893 percent = self._like_percent_literal 

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

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

3896 

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

3898 binary = binary._clone() 

3899 percent = self._like_percent_literal 

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

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

3902 

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

3904 binary = binary._clone() 

3905 percent = self._like_percent_literal 

3906 binary.left = ilike_case_insensitive(binary.left) 

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

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

3909 

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

3911 binary = binary._clone() 

3912 percent = self._like_percent_literal 

3913 binary.left = ilike_case_insensitive(binary.left) 

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

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

3916 

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

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

3919 

3920 return "%s LIKE %s" % ( 

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

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

3923 ) + ( 

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

3925 if escape is not None 

3926 else "" 

3927 ) 

3928 

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

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

3931 return "%s NOT LIKE %s" % ( 

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

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

3934 ) + ( 

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

3936 if escape is not None 

3937 else "" 

3938 ) 

3939 

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

3941 if operator is operators.ilike_op: 

3942 binary = binary._clone() 

3943 binary.left = ilike_case_insensitive(binary.left) 

3944 binary.right = ilike_case_insensitive(binary.right) 

3945 # else we assume ilower() has been applied 

3946 

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

3948 

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

3950 if operator is operators.not_ilike_op: 

3951 binary = binary._clone() 

3952 binary.left = ilike_case_insensitive(binary.left) 

3953 binary.right = ilike_case_insensitive(binary.right) 

3954 # else we assume ilower() has been applied 

3955 

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

3957 

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

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

3960 return self._generate_generic_binary( 

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

3962 ) 

3963 

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

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

3966 return self._generate_generic_binary( 

3967 binary, 

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

3969 **kw, 

3970 ) 

3971 

3972 def visit_regexp_match_op_binary( 

3973 self, binary: BinaryExpression[Any], operator: Any, **kw: Any 

3974 ) -> str: 

3975 raise exc.CompileError( 

3976 "%s dialect does not support regular expressions" 

3977 % self.dialect.name 

3978 ) 

3979 

3980 def visit_not_regexp_match_op_binary( 

3981 self, binary: BinaryExpression[Any], operator: Any, **kw: Any 

3982 ) -> str: 

3983 raise exc.CompileError( 

3984 "%s dialect does not support regular expressions" 

3985 % self.dialect.name 

3986 ) 

3987 

3988 def visit_regexp_replace_op_binary( 

3989 self, binary: BinaryExpression[Any], operator: Any, **kw: Any 

3990 ) -> str: 

3991 raise exc.CompileError( 

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

3993 % self.dialect.name 

3994 ) 

3995 

3996 def visit_dmltargetcopy(self, element, *, bindmarkers=None, **kw): 

3997 if bindmarkers is None: 

3998 raise exc.CompileError( 

3999 "DML target objects may only be used with " 

4000 "compiled INSERT or UPDATE statements" 

4001 ) 

4002 

4003 bindmarkers[element.column.key] = element 

4004 return f"__BINDMARKER_~~{element.column.key}~~" 

4005 

4006 def visit_bindparam( 

4007 self, 

4008 bindparam, 

4009 within_columns_clause=False, 

4010 literal_binds=False, 

4011 skip_bind_expression=False, 

4012 literal_execute=False, 

4013 render_postcompile=False, 

4014 is_upsert_set=False, 

4015 **kwargs, 

4016 ): 

4017 # Detect parametrized bindparams in upsert SET clause for issue #13130 

4018 if ( 

4019 is_upsert_set 

4020 and bindparam.value is None 

4021 and bindparam.callable is None 

4022 and self._insertmanyvalues is not None 

4023 ): 

4024 self._insertmanyvalues = self._insertmanyvalues._replace( 

4025 has_upsert_bound_parameters=True 

4026 ) 

4027 

4028 if not skip_bind_expression: 

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

4030 if impl._has_bind_expression: 

4031 bind_expression = impl.bind_expression(bindparam) 

4032 wrapped = self.process( 

4033 bind_expression, 

4034 skip_bind_expression=True, 

4035 within_columns_clause=within_columns_clause, 

4036 literal_binds=literal_binds and not bindparam.expanding, 

4037 literal_execute=literal_execute, 

4038 render_postcompile=render_postcompile, 

4039 **kwargs, 

4040 ) 

4041 if bindparam.expanding: 

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

4043 # of this into the inside 

4044 

4045 m = re.match( 

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

4047 ) 

4048 assert m, "unexpected format for expanding parameter" 

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

4050 m.group(2), 

4051 m.group(1), 

4052 m.group(3), 

4053 ) 

4054 

4055 if literal_binds: 

4056 ret = self.render_literal_bindparam( 

4057 bindparam, 

4058 within_columns_clause=True, 

4059 bind_expression_template=wrapped, 

4060 **kwargs, 

4061 ) 

4062 return f"({ret})" 

4063 

4064 return wrapped 

4065 

4066 if not literal_binds: 

4067 literal_execute = ( 

4068 literal_execute 

4069 or bindparam.literal_execute 

4070 or (within_columns_clause and self.ansi_bind_rules) 

4071 ) 

4072 post_compile = literal_execute or bindparam.expanding 

4073 else: 

4074 post_compile = False 

4075 

4076 if literal_binds: 

4077 ret = self.render_literal_bindparam( 

4078 bindparam, within_columns_clause=True, **kwargs 

4079 ) 

4080 if bindparam.expanding: 

4081 ret = f"({ret})" 

4082 return ret 

4083 

4084 name = self._truncate_bindparam(bindparam) 

4085 

4086 if name in self.binds: 

4087 existing = self.binds[name] 

4088 if existing is not bindparam: 

4089 if ( 

4090 (existing.unique or bindparam.unique) 

4091 and not existing.proxy_set.intersection( 

4092 bindparam.proxy_set 

4093 ) 

4094 and not existing._cloned_set.intersection( 

4095 bindparam._cloned_set 

4096 ) 

4097 ): 

4098 raise exc.CompileError( 

4099 "Bind parameter '%s' conflicts with " 

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

4101 ) 

4102 elif existing.expanding != bindparam.expanding: 

4103 raise exc.CompileError( 

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

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

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

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

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

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

4110 ) 

4111 elif existing._is_crud or bindparam._is_crud: 

4112 if existing._is_crud and bindparam._is_crud: 

4113 # TODO: this condition is not well understood. 

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

4115 raise exc.CompileError( 

4116 "Encountered unsupported case when compiling an " 

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

4118 "multi-table " 

4119 "UPDATE statement, please provide string-named " 

4120 "arguments to the " 

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

4122 "multi-table UPDATE statements that " 

4123 "target multiple tables for UPDATE is very " 

4124 "limited", 

4125 ) 

4126 else: 

4127 raise exc.CompileError( 

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

4129 "for automatic usage in the VALUES or SET " 

4130 "clause of this " 

4131 "insert/update statement. Please use a " 

4132 "name other than column name when using " 

4133 "bindparam() " 

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

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

4136 ) 

4137 

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

4139 

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

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

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

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

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

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

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

4147 # key was been generated. 

4148 ckbm_tuple = self._cache_key_bind_match 

4149 

4150 if ckbm_tuple: 

4151 ckbm, cksm = ckbm_tuple 

4152 for bp in bindparam._cloned_set: 

4153 if bp.key in cksm: 

4154 cb = cksm[bp.key] 

4155 ckbm[cb].append(bindparam) 

4156 

4157 if bindparam.isoutparam: 

4158 self.has_out_parameters = True 

4159 

4160 if post_compile: 

4161 if render_postcompile: 

4162 self._render_postcompile = True 

4163 

4164 if literal_execute: 

4165 self.literal_execute_params |= {bindparam} 

4166 else: 

4167 self.post_compile_params |= {bindparam} 

4168 

4169 ret = self.bindparam_string( 

4170 name, 

4171 post_compile=post_compile, 

4172 expanding=bindparam.expanding, 

4173 bindparam_type=bindparam.type, 

4174 **kwargs, 

4175 ) 

4176 

4177 if bindparam.expanding: 

4178 ret = f"({ret})" 

4179 

4180 return ret 

4181 

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

4183 raise NotImplementedError() 

4184 

4185 def render_literal_bindparam( 

4186 self, 

4187 bindparam, 

4188 render_literal_value=NO_ARG, 

4189 bind_expression_template=None, 

4190 **kw, 

4191 ): 

4192 if render_literal_value is not NO_ARG: 

4193 value = render_literal_value 

4194 elif self._collect_params and bindparam.key in self._collected_params: 

4195 # values passed to ExecutableStatement.params() are not 

4196 # present on the bindparam itself 

4197 value = self._collected_params[bindparam.key] 

4198 else: 

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

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

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

4202 util.warn_limited( 

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

4204 "expression; comparisons to NULL should not use " 

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

4206 (bindparam.key,), 

4207 ) 

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

4209 value = bindparam.effective_value 

4210 

4211 if bindparam.expanding: 

4212 leep = self._literal_execute_expanding_parameter_literal_binds 

4213 to_update, replacement_expr = leep( 

4214 bindparam, 

4215 value, 

4216 bind_expression_template=bind_expression_template, 

4217 ) 

4218 return replacement_expr 

4219 else: 

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

4221 

4222 def render_literal_value( 

4223 self, value: Any, type_: sqltypes.TypeEngine[Any] 

4224 ) -> str: 

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

4226 

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

4228 on the target driver/database. 

4229 

4230 This should be implemented by subclasses using the quoting services 

4231 of the DBAPI. 

4232 

4233 """ 

4234 

4235 if value is None and not type_.should_evaluate_none: 

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

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

4238 # (e.g. JSON) 

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

4240 

4241 processor = type_._cached_literal_processor(self.dialect) 

4242 if processor: 

4243 try: 

4244 return processor(value) 

4245 except Exception as e: 

4246 raise exc.CompileError( 

4247 f"Could not render literal value " 

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

4249 f"with datatype " 

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

4251 "more detail." 

4252 ) from e 

4253 

4254 else: 

4255 raise exc.CompileError( 

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

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

4258 f"with datatype {type_}" 

4259 ) 

4260 

4261 def _truncate_bindparam(self, bindparam): 

4262 if bindparam in self.bind_names: 

4263 return self.bind_names[bindparam] 

4264 

4265 bind_name = bindparam.key 

4266 if isinstance(bind_name, elements._truncated_label): 

4267 bind_name = self._truncated_identifier("bindparam", bind_name) 

4268 

4269 # add to bind_names for translation 

4270 self.bind_names[bindparam] = bind_name 

4271 

4272 return bind_name 

4273 

4274 def _truncated_identifier( 

4275 self, ident_class: str, name: _truncated_label 

4276 ) -> str: 

4277 if (ident_class, name) in self.truncated_names: 

4278 return self.truncated_names[(ident_class, name)] 

4279 

4280 anonname = name.apply_map(self.anon_map) 

4281 

4282 if len(anonname) > self.label_length - 6: 

4283 counter = self._truncated_counters.get(ident_class, 1) 

4284 truncname = ( 

4285 anonname[0 : max(self.label_length - 6, 0)] 

4286 + "_" 

4287 + hex(counter)[2:] 

4288 ) 

4289 self._truncated_counters[ident_class] = counter + 1 

4290 else: 

4291 truncname = anonname 

4292 self.truncated_names[(ident_class, name)] = truncname 

4293 return truncname 

4294 

4295 def _anonymize(self, name: str) -> str: 

4296 return name % self.anon_map 

4297 

4298 def _escaped_bind_name(self, escaped_from: str, name: str) -> str: 

4299 """Record the escaped form of a bind name, uniquifying it against 

4300 the names already in use. 

4301 

4302 Distinct bind names can escape to the same string, e.g. parameters 

4303 named ``"a.b"`` and ``"a_b"`` both escape to ``a_b``. As the escaped 

4304 name is what keys the parameter dictionary handed to the driver, the 

4305 second parameter would otherwise overwrite the first and its value be 

4306 silently discarded. A counter is appended so that the two remain 

4307 distinct. The ``.key`` of each :class:`.BindParameter` is untouched, 

4308 so parameter dictionaries passed by the caller are unaffected. 

4309 

4310 See :ticket:`13534`. 

4311 

4312 """ 

4313 try: 

4314 return self.escaped_bind_names[escaped_from] 

4315 except KeyError: 

4316 pass 

4317 

4318 if self.escaped_bind_names: 

4319 used = self._escaped_bind_names_used 

4320 else: 

4321 self._escaped_bind_names_used = used = set() 

4322 

4323 if name in used or (name != escaped_from and name in self.binds): 

4324 base = name 

4325 counter = 0 

4326 while name in used or name in self.binds: 

4327 counter += 1 

4328 name = "%s__%d" % (base, counter) 

4329 

4330 used.add(name) 

4331 self.escaped_bind_names = self.escaped_bind_names.union( 

4332 {escaped_from: name} 

4333 ) 

4334 return name 

4335 

4336 def bindparam_string( 

4337 self, 

4338 name: str, 

4339 post_compile: bool = False, 

4340 expanding: bool = False, 

4341 escaped_from: Optional[str] = None, 

4342 bindparam_type: Optional[TypeEngine[Any]] = None, 

4343 accumulate_bind_names: Optional[Set[str]] = None, 

4344 visited_bindparam: Optional[List[str]] = None, 

4345 **kw: Any, 

4346 ) -> str: 

4347 # TODO: accumulate_bind_names is passed by crud.py to gather 

4348 # names on a per-value basis, visited_bindparam is passed by 

4349 # visit_insert() to collect all parameters in the statement. 

4350 # see if this gathering can be simplified somehow 

4351 if accumulate_bind_names is not None: 

4352 accumulate_bind_names.add(name) 

4353 if visited_bindparam is not None: 

4354 visited_bindparam.append(name) 

4355 

4356 if not escaped_from: 

4357 if self._bind_translate_re.search(name): 

4358 # not quite the translate use case as we want to 

4359 # also get a quick boolean if we even found 

4360 # unusual characters in the name 

4361 new_name = self._bind_translate_re.sub( 

4362 lambda m: self._bind_translate_chars[m.group(0)], 

4363 name, 

4364 ) 

4365 escaped_from = name 

4366 name = new_name 

4367 elif ( 

4368 self.escaped_bind_names 

4369 and name in self._escaped_bind_names_used 

4370 ): 

4371 # this name needs no escaping of its own, but another 

4372 # parameter has already escaped to it; #13534 

4373 escaped_from = name 

4374 

4375 if escaped_from: 

4376 name = self._escaped_bind_name(escaped_from, name) 

4377 if post_compile: 

4378 ret = "__[POSTCOMPILE_%s]" % name 

4379 if expanding: 

4380 # for expanding, bound parameters or literal values will be 

4381 # rendered per item 

4382 return ret 

4383 

4384 # otherwise, for non-expanding "literal execute", apply 

4385 # bind casts as determined by the datatype 

4386 if bindparam_type is not None: 

4387 type_impl = bindparam_type._unwrapped_dialect_impl( 

4388 self.dialect 

4389 ) 

4390 if type_impl.render_literal_cast: 

4391 ret = self.render_bind_cast(bindparam_type, type_impl, ret) 

4392 return ret 

4393 elif self.state is CompilerState.COMPILING: 

4394 ret = self.compilation_bindtemplate % {"name": name} 

4395 else: 

4396 ret = self.bindtemplate % {"name": name} 

4397 

4398 if ( 

4399 bindparam_type is not None 

4400 and self.dialect._bind_typing_render_casts 

4401 ): 

4402 type_impl = bindparam_type._unwrapped_dialect_impl(self.dialect) 

4403 if type_impl.render_bind_cast: 

4404 ret = self.render_bind_cast(bindparam_type, type_impl, ret) 

4405 

4406 return ret 

4407 

4408 def _dispatch_independent_ctes(self, stmt, kw): 

4409 local_kw = kw.copy() 

4410 local_kw.pop("cte_opts", None) 

4411 for cte, opt in zip( 

4412 stmt._independent_ctes, stmt._independent_ctes_opts 

4413 ): 

4414 cte._compiler_dispatch(self, cte_opts=opt, **local_kw) 

4415 

4416 def visit_cte( 

4417 self, 

4418 cte: CTE, 

4419 asfrom: bool = False, 

4420 ashint: bool = False, 

4421 fromhints: Optional[_FromHintsType] = None, 

4422 visiting_cte: Optional[CTE] = None, 

4423 from_linter: Optional[FromLinter] = None, 

4424 cte_opts: selectable._CTEOpts = selectable._CTEOpts(False), 

4425 **kwargs: Any, 

4426 ) -> Optional[str]: 

4427 self_ctes = self._init_cte_state() 

4428 assert self_ctes is self.ctes 

4429 

4430 kwargs["visiting_cte"] = cte 

4431 

4432 cte_name = cte.name 

4433 

4434 if isinstance(cte_name, elements._truncated_label): 

4435 cte_name = self._truncated_identifier("alias", cte_name) 

4436 

4437 is_new_cte = True 

4438 embedded_in_current_named_cte = False 

4439 

4440 _reference_cte = cte._get_reference_cte() 

4441 

4442 nesting = cte.nesting or cte_opts.nesting 

4443 

4444 # check for CTE already encountered 

4445 if _reference_cte in self.level_name_by_cte: 

4446 cte_level, _, existing_cte_opts = self.level_name_by_cte[ 

4447 _reference_cte 

4448 ] 

4449 assert _ == cte_name 

4450 

4451 cte_level_name = (cte_level, cte_name) 

4452 existing_cte = self.ctes_by_level_name[cte_level_name] 

4453 

4454 # check if we are receiving it here with a specific 

4455 # "nest_here" location; if so, move it to this location 

4456 

4457 if cte_opts.nesting: 

4458 if existing_cte_opts.nesting: 

4459 raise exc.CompileError( 

4460 "CTE is stated as 'nest_here' in " 

4461 "more than one location" 

4462 ) 

4463 

4464 old_level_name = (cte_level, cte_name) 

4465 cte_level = len(self.stack) if nesting else 1 

4466 cte_level_name = new_level_name = (cte_level, cte_name) 

4467 

4468 del self.ctes_by_level_name[old_level_name] 

4469 self.ctes_by_level_name[new_level_name] = existing_cte 

4470 self.level_name_by_cte[_reference_cte] = new_level_name + ( 

4471 cte_opts, 

4472 ) 

4473 

4474 else: 

4475 cte_level = len(self.stack) if nesting else 1 

4476 cte_level_name = (cte_level, cte_name) 

4477 

4478 if cte_level_name in self.ctes_by_level_name: 

4479 existing_cte = self.ctes_by_level_name[cte_level_name] 

4480 else: 

4481 existing_cte = None 

4482 

4483 if existing_cte is not None: 

4484 embedded_in_current_named_cte = visiting_cte is existing_cte 

4485 

4486 # we've generated a same-named CTE that we are enclosed in, 

4487 # or this is the same CTE. just return the name. 

4488 if cte is existing_cte._restates or cte is existing_cte: 

4489 is_new_cte = False 

4490 elif existing_cte is cte._restates: 

4491 # we've generated a same-named CTE that is 

4492 # enclosed in us - we take precedence, so 

4493 # discard the text for the "inner". 

4494 del self_ctes[existing_cte] 

4495 

4496 existing_cte_reference_cte = existing_cte._get_reference_cte() 

4497 

4498 assert existing_cte_reference_cte is _reference_cte 

4499 assert existing_cte_reference_cte is existing_cte 

4500 

4501 del self.level_name_by_cte[existing_cte_reference_cte] 

4502 else: 

4503 if ( 

4504 # if the two CTEs have the same hash, which we expect 

4505 # here means that one/both is an annotated of the other 

4506 (hash(cte) == hash(existing_cte)) 

4507 # or... 

4508 or ( 

4509 ( 

4510 # if they are clones, i.e. they came from the ORM 

4511 # or some other visit method 

4512 cte._is_clone_of is not None 

4513 or existing_cte._is_clone_of is not None 

4514 ) 

4515 # and are deep-copy identical 

4516 and cte.compare(existing_cte) 

4517 ) 

4518 ): 

4519 # then consider these two CTEs the same 

4520 is_new_cte = False 

4521 else: 

4522 # otherwise these are two CTEs that either will render 

4523 # differently, or were indicated separately by the user, 

4524 # with the same name 

4525 raise exc.CompileError( 

4526 "Multiple, unrelated CTEs found with " 

4527 "the same name: %r" % cte_name 

4528 ) 

4529 

4530 if not asfrom and not is_new_cte: 

4531 return None 

4532 

4533 if cte._cte_alias is not None: 

4534 pre_alias_cte = cte._cte_alias 

4535 cte_pre_alias_name = cte._cte_alias.name 

4536 if isinstance(cte_pre_alias_name, elements._truncated_label): 

4537 cte_pre_alias_name = self._truncated_identifier( 

4538 "alias", cte_pre_alias_name 

4539 ) 

4540 else: 

4541 pre_alias_cte = cte 

4542 cte_pre_alias_name = None 

4543 

4544 if is_new_cte: 

4545 self.ctes_by_level_name[cte_level_name] = cte 

4546 self.level_name_by_cte[_reference_cte] = cte_level_name + ( 

4547 cte_opts, 

4548 ) 

4549 

4550 if pre_alias_cte not in self.ctes: 

4551 self.visit_cte(pre_alias_cte, **kwargs) 

4552 

4553 if not cte_pre_alias_name and cte not in self_ctes: 

4554 if cte.recursive: 

4555 self.ctes_recursive = True 

4556 text = self.preparer.format_alias(cte, cte_name) 

4557 if cte.recursive or cte.element.name_cte_columns: 

4558 col_source = cte.element 

4559 

4560 # TODO: can we get at the .columns_plus_names collection 

4561 # that is already (or will be?) generated for the SELECT 

4562 # rather than calling twice? 

4563 recur_cols = [ 

4564 # TODO: proxy_name is not technically safe, 

4565 # see test_cte-> 

4566 # test_with_recursive_no_name_currently_buggy. not 

4567 # clear what should be done with such a case 

4568 fallback_label_name or proxy_name 

4569 for ( 

4570 _, 

4571 proxy_name, 

4572 fallback_label_name, 

4573 c, 

4574 repeated, 

4575 ) in (col_source._generate_columns_plus_names(True)) 

4576 if not repeated 

4577 ] 

4578 

4579 text += "(%s)" % ( 

4580 ", ".join( 

4581 self.preparer.format_label_name( 

4582 ident, anon_map=self.anon_map 

4583 ) 

4584 for ident in recur_cols 

4585 ) 

4586 ) 

4587 

4588 assert kwargs.get("subquery", False) is False 

4589 

4590 if not self.stack: 

4591 # toplevel, this is a stringify of the 

4592 # cte directly. just compile the inner 

4593 # the way alias() does. 

4594 return cte.element._compiler_dispatch( 

4595 self, asfrom=asfrom, **kwargs 

4596 ) 

4597 else: 

4598 prefixes = self._generate_prefixes( 

4599 cte, cte._prefixes, **kwargs 

4600 ) 

4601 inner = cte.element._compiler_dispatch( 

4602 self, asfrom=True, **kwargs 

4603 ) 

4604 

4605 text += " AS %s\n(%s)" % (prefixes, inner) 

4606 

4607 if cte._suffixes: 

4608 text += " " + self._generate_prefixes( 

4609 cte, cte._suffixes, **kwargs 

4610 ) 

4611 

4612 self_ctes[cte] = text 

4613 

4614 if asfrom: 

4615 if from_linter: 

4616 from_linter.froms[cte._de_clone()] = cte_name 

4617 

4618 if not is_new_cte and embedded_in_current_named_cte: 

4619 return self.preparer.format_alias(cte, cte_name) 

4620 

4621 if cte_pre_alias_name: 

4622 text = self.preparer.format_alias(cte, cte_pre_alias_name) 

4623 if self.preparer._requires_quotes(cte_name): 

4624 cte_name = self.preparer.quote(cte_name) 

4625 text += self.get_render_as_alias_suffix(cte_name) 

4626 return text # type: ignore[no-any-return] 

4627 else: 

4628 return self.preparer.format_alias(cte, cte_name) 

4629 

4630 return None 

4631 

4632 def visit_table_valued_alias(self, element, **kw): 

4633 if element.joins_implicitly: 

4634 kw["from_linter"] = None 

4635 if element._is_lateral: 

4636 return self.visit_lateral(element, **kw) 

4637 else: 

4638 return self.visit_alias(element, **kw) 

4639 

4640 def visit_table_valued_column(self, element, **kw): 

4641 return self.visit_column(element, **kw) 

4642 

4643 def visit_alias( 

4644 self, 

4645 alias, 

4646 asfrom=False, 

4647 ashint=False, 

4648 iscrud=False, 

4649 fromhints=None, 

4650 subquery=False, 

4651 lateral=False, 

4652 enclosing_alias=None, 

4653 from_linter=None, 

4654 within_tstring=False, 

4655 **kwargs, 

4656 ): 

4657 if lateral: 

4658 if "enclosing_lateral" not in kwargs: 

4659 # if lateral is set and enclosing_lateral is not 

4660 # present, we assume we are being called directly 

4661 # from visit_lateral() and we need to set enclosing_lateral. 

4662 assert alias._is_lateral 

4663 kwargs["enclosing_lateral"] = alias 

4664 

4665 # for lateral objects, we track a second from_linter that is... 

4666 # lateral! to the level above us. 

4667 if ( 

4668 from_linter 

4669 and "lateral_from_linter" not in kwargs 

4670 and "enclosing_lateral" in kwargs 

4671 ): 

4672 kwargs["lateral_from_linter"] = from_linter 

4673 

4674 if enclosing_alias is not None and enclosing_alias.element is alias: 

4675 inner = alias.element._compiler_dispatch( 

4676 self, 

4677 asfrom=asfrom, 

4678 ashint=ashint, 

4679 iscrud=iscrud, 

4680 fromhints=fromhints, 

4681 lateral=lateral, 

4682 enclosing_alias=alias, 

4683 **kwargs, 

4684 ) 

4685 if subquery and (asfrom or lateral): 

4686 inner = "(%s)" % (inner,) 

4687 return inner 

4688 else: 

4689 kwargs["enclosing_alias"] = alias 

4690 

4691 if asfrom or ashint or within_tstring: 

4692 if isinstance(alias.name, elements._truncated_label): 

4693 alias_name = self._truncated_identifier("alias", alias.name) 

4694 else: 

4695 alias_name = alias.name 

4696 

4697 if ashint: 

4698 return self.preparer.format_alias(alias, alias_name) 

4699 elif asfrom or within_tstring: 

4700 if from_linter: 

4701 from_linter.froms[alias._de_clone()] = alias_name 

4702 

4703 inner = alias.element._compiler_dispatch( 

4704 self, asfrom=True, lateral=lateral, **kwargs 

4705 ) 

4706 if subquery: 

4707 inner = "(%s)" % (inner,) 

4708 

4709 ret = inner + self.get_render_as_alias_suffix( 

4710 self.preparer.format_alias(alias, alias_name) 

4711 ) 

4712 

4713 if alias._supports_derived_columns and alias._render_derived: 

4714 ret += "(%s)" % ( 

4715 ", ".join( 

4716 "%s%s" 

4717 % ( 

4718 self.preparer.quote(col.name), 

4719 ( 

4720 " %s" 

4721 % self.dialect.type_compiler_instance.process( 

4722 col.type, **kwargs 

4723 ) 

4724 if alias._render_derived_w_types 

4725 else "" 

4726 ), 

4727 ) 

4728 for col in alias.c 

4729 ) 

4730 ) 

4731 

4732 if fromhints and alias in fromhints: 

4733 ret = self.format_from_hint_text( 

4734 ret, alias, fromhints[alias], iscrud 

4735 ) 

4736 

4737 return ret 

4738 else: 

4739 # note we cancel the "subquery" flag here as well 

4740 return alias.element._compiler_dispatch( 

4741 self, lateral=lateral, **kwargs 

4742 ) 

4743 

4744 def visit_subquery(self, subquery, **kw): 

4745 kw["subquery"] = True 

4746 return self.visit_alias(subquery, **kw) 

4747 

4748 def visit_lateral(self, lateral_, **kw): 

4749 kw["lateral"] = True 

4750 return "LATERAL %s" % self.visit_alias(lateral_, **kw) 

4751 

4752 def visit_tablesample(self, tablesample, asfrom=False, **kw): 

4753 text = "%s TABLESAMPLE %s" % ( 

4754 self.visit_alias(tablesample, asfrom=True, **kw), 

4755 tablesample._get_method()._compiler_dispatch(self, **kw), 

4756 ) 

4757 

4758 if tablesample.seed is not None: 

4759 text += " REPEATABLE (%s)" % ( 

4760 tablesample.seed._compiler_dispatch(self, **kw) 

4761 ) 

4762 

4763 return text 

4764 

4765 def _render_values(self, element, **kw): 

4766 kw.setdefault("literal_binds", element.literal_binds) 

4767 tuples = ", ".join( 

4768 self.process( 

4769 elements.Tuple( 

4770 types=element._column_types, *elem 

4771 ).self_group(), 

4772 **kw, 

4773 ) 

4774 for chunk in element._data 

4775 for elem in chunk 

4776 ) 

4777 return f"VALUES {tuples}" 

4778 

4779 def visit_values( 

4780 self, element, asfrom=False, from_linter=None, visiting_cte=None, **kw 

4781 ): 

4782 

4783 if element._independent_ctes: 

4784 self._dispatch_independent_ctes(element, kw) 

4785 

4786 v = self._render_values(element, **kw) 

4787 

4788 if element._unnamed: 

4789 name = None 

4790 elif isinstance(element.name, elements._truncated_label): 

4791 name = self._truncated_identifier("values", element.name) 

4792 else: 

4793 name = element.name 

4794 

4795 if element._is_lateral: 

4796 lateral = "LATERAL " 

4797 else: 

4798 lateral = "" 

4799 

4800 if asfrom: 

4801 if from_linter: 

4802 from_linter.froms[element._de_clone()] = ( 

4803 name if name is not None else "(unnamed VALUES element)" 

4804 ) 

4805 

4806 if visiting_cte is not None and visiting_cte.element is element: 

4807 if element._is_lateral: 

4808 raise exc.CompileError( 

4809 "Can't use a LATERAL VALUES expression inside of a CTE" 

4810 ) 

4811 elif name: 

4812 kw["include_table"] = False 

4813 v = "%s(%s)%s (%s)" % ( 

4814 lateral, 

4815 v, 

4816 self.get_render_as_alias_suffix(self.preparer.quote(name)), 

4817 ( 

4818 ", ".join( 

4819 c._compiler_dispatch(self, **kw) 

4820 for c in element.columns 

4821 ) 

4822 ), 

4823 ) 

4824 else: 

4825 v = "%s(%s)" % (lateral, v) 

4826 return v 

4827 

4828 def visit_scalar_values(self, element, **kw): 

4829 return f"({self._render_values(element, **kw)})" 

4830 

4831 def get_render_as_alias_suffix(self, alias_name_text): 

4832 return " AS " + alias_name_text 

4833 

4834 def _add_to_result_map( 

4835 self, 

4836 keyname: str, 

4837 name: str, 

4838 objects: Tuple[Any, ...], 

4839 type_: TypeEngine[Any], 

4840 ) -> None: 

4841 

4842 # note objects must be non-empty for cursor.py to handle the 

4843 # collection properly 

4844 assert objects 

4845 

4846 if keyname is None or keyname == "*": 

4847 self._ordered_columns = False 

4848 self._ad_hoc_textual = True 

4849 if type_._is_tuple_type: 

4850 raise exc.CompileError( 

4851 "Most backends don't support SELECTing " 

4852 "from a tuple() object. If this is an ORM query, " 

4853 "consider using the Bundle object." 

4854 ) 

4855 self._result_columns.append( 

4856 ResultColumnsEntry(keyname, name, objects, type_) 

4857 ) 

4858 

4859 def _label_returning_column( 

4860 self, stmt, column, populate_result_map, column_clause_args=None, **kw 

4861 ): 

4862 """Render a column with necessary labels inside of a RETURNING clause. 

4863 

4864 This method is provided for individual dialects in place of calling 

4865 the _label_select_column method directly, so that the two use cases 

4866 of RETURNING vs. SELECT can be disambiguated going forward. 

4867 

4868 .. versionadded:: 1.4.21 

4869 

4870 """ 

4871 return self._label_select_column( 

4872 None, 

4873 column, 

4874 populate_result_map, 

4875 False, 

4876 {} if column_clause_args is None else column_clause_args, 

4877 **kw, 

4878 ) 

4879 

4880 def _label_select_column( 

4881 self, 

4882 select, 

4883 column, 

4884 populate_result_map, 

4885 asfrom, 

4886 column_clause_args, 

4887 name=None, 

4888 proxy_name=None, 

4889 fallback_label_name=None, 

4890 within_columns_clause=True, 

4891 column_is_repeated=False, 

4892 need_column_expressions=False, 

4893 include_table=True, 

4894 ): 

4895 """produce labeled columns present in a select().""" 

4896 impl = column.type.dialect_impl(self.dialect) 

4897 

4898 if impl._has_column_expression and ( 

4899 need_column_expressions or populate_result_map 

4900 ): 

4901 col_expr = impl.column_expression(column) 

4902 else: 

4903 col_expr = column 

4904 

4905 if populate_result_map: 

4906 # pass an "add_to_result_map" callable into the compilation 

4907 # of embedded columns. this collects information about the 

4908 # column as it will be fetched in the result and is coordinated 

4909 # with cursor.description when the query is executed. 

4910 add_to_result_map = self._add_to_result_map 

4911 

4912 # if the SELECT statement told us this column is a repeat, 

4913 # wrap the callable with one that prevents the addition of the 

4914 # targets 

4915 if column_is_repeated: 

4916 _add_to_result_map = add_to_result_map 

4917 

4918 def add_to_result_map(keyname, name, objects, type_): 

4919 _add_to_result_map(keyname, name, (keyname,), type_) 

4920 

4921 # if we redefined col_expr for type expressions, wrap the 

4922 # callable with one that adds the original column to the targets 

4923 elif col_expr is not column: 

4924 _add_to_result_map = add_to_result_map 

4925 

4926 def add_to_result_map(keyname, name, objects, type_): 

4927 _add_to_result_map( 

4928 keyname, name, (column,) + objects, type_ 

4929 ) 

4930 

4931 else: 

4932 add_to_result_map = None 

4933 

4934 # this method is used by some of the dialects for RETURNING, 

4935 # which has different inputs. _label_returning_column was added 

4936 # as the better target for this now however for 1.4 we will keep 

4937 # _label_select_column directly compatible with this use case. 

4938 # these assertions right now set up the current expected inputs 

4939 assert within_columns_clause, ( 

4940 "_label_select_column is only relevant within " 

4941 "the columns clause of a SELECT or RETURNING" 

4942 ) 

4943 result_expr: elements.Label[Any] | _CompileLabel 

4944 

4945 if isinstance(column, elements.Label): 

4946 if col_expr is not column: 

4947 result_expr = _CompileLabel( 

4948 col_expr, column.name, alt_names=(column.element,) 

4949 ) 

4950 else: 

4951 result_expr = col_expr 

4952 

4953 elif name: 

4954 # here, _columns_plus_names has determined there's an explicit 

4955 # label name we need to use. this is the default for 

4956 # tablenames_plus_columnnames as well as when columns are being 

4957 # deduplicated on name 

4958 

4959 assert ( 

4960 proxy_name is not None 

4961 ), "proxy_name is required if 'name' is passed" 

4962 

4963 result_expr = _CompileLabel( 

4964 col_expr, 

4965 name, 

4966 alt_names=( 

4967 proxy_name, 

4968 # this is a hack to allow legacy result column lookups 

4969 # to work as they did before; this goes away in 2.0. 

4970 # TODO: this only seems to be tested indirectly 

4971 # via test/orm/test_deprecations.py. should be a 

4972 # resultset test for this 

4973 column._tq_label, 

4974 ), 

4975 ) 

4976 else: 

4977 # determine here whether this column should be rendered in 

4978 # a labelled context or not, as we were given no required label 

4979 # name from the caller. Here we apply heuristics based on the kind 

4980 # of SQL expression involved. 

4981 

4982 if col_expr is not column: 

4983 # type-specific expression wrapping the given column, 

4984 # so we render a label 

4985 render_with_label = True 

4986 elif isinstance(column, elements.ColumnClause): 

4987 # table-bound column, we render its name as a label if we are 

4988 # inside of a subquery only 

4989 render_with_label = ( 

4990 asfrom 

4991 and not column.is_literal 

4992 and column.table is not None 

4993 ) 

4994 elif isinstance(column, elements.TextClause): 

4995 render_with_label = False 

4996 elif isinstance(column, elements.UnaryExpression): 

4997 # unary expression. notes added as of #12681 

4998 # 

4999 # By convention, the visit_unary() method 

5000 # itself does not add an entry to the result map, and relies 

5001 # upon either the inner expression creating a result map 

5002 # entry, or if not, by creating a label here that produces 

5003 # the result map entry. Where that happens is based on whether 

5004 # or not the element immediately inside the unary is a 

5005 # NamedColumn subclass or not. 

5006 # 

5007 # Now, this also impacts how the SELECT is written; if 

5008 # we decide to generate a label here, we get the usual 

5009 # "~(x+y) AS anon_1" thing in the columns clause. If we 

5010 # don't, we don't get an AS at all, we get like 

5011 # "~table.column". 

5012 # 

5013 # But here is the important thing as of modernish (like 1.4) 

5014 # versions of SQLAlchemy - **whether or not the AS <label> 

5015 # is present in the statement is not actually important**. 

5016 # We target result columns **positionally** for a fully 

5017 # compiled ``Select()`` object; before 1.4 we needed those 

5018 # labels to match in cursor.description etc etc but now it 

5019 # really doesn't matter. 

5020 # So really, we could set render_with_label True in all cases. 

5021 # Or we could just have visit_unary() populate the result map 

5022 # in all cases. 

5023 # 

5024 # What we're doing here is strictly trying to not rock the 

5025 # boat too much with when we do/don't render "AS label"; 

5026 # labels being present helps in the edge cases that we 

5027 # "fall back" to named cursor.description matching, labels 

5028 # not being present for columns keeps us from having awkward 

5029 # phrases like "SELECT DISTINCT table.x AS x". 

5030 render_with_label = ( 

5031 ( 

5032 # exception case to detect if we render "not boolean" 

5033 # as "not <col>" for native boolean or "<col> = 1" 

5034 # for non-native boolean. this is controlled by 

5035 # visit_is_<true|false>_unary_operator 

5036 column.operator 

5037 in (operators.is_false, operators.is_true) 

5038 and not self.dialect.supports_native_boolean 

5039 ) 

5040 or column._wraps_unnamed_column() 

5041 or asfrom 

5042 ) 

5043 elif ( 

5044 # general class of expressions that don't have a SQL-column 

5045 # addressable name. includes scalar selects, bind parameters, 

5046 # SQL functions, others 

5047 not isinstance(column, elements.NamedColumn) 

5048 # deeper check that indicates there's no natural "name" to 

5049 # this element, which accommodates for custom SQL constructs 

5050 # that might have a ".name" attribute (but aren't SQL 

5051 # functions) but are not implementing this more recently added 

5052 # base class. in theory the "NamedColumn" check should be 

5053 # enough, however here we seek to maintain legacy behaviors 

5054 # as well. 

5055 and column._non_anon_label is None 

5056 ): 

5057 render_with_label = True 

5058 else: 

5059 render_with_label = False 

5060 

5061 if render_with_label: 

5062 if not fallback_label_name: 

5063 # used by the RETURNING case right now. we generate it 

5064 # here as 3rd party dialects may be referring to 

5065 # _label_select_column method directly instead of the 

5066 # just-added _label_returning_column method 

5067 assert not column_is_repeated 

5068 fallback_label_name = column._anon_name_label 

5069 

5070 fallback_label_name = ( 

5071 elements._truncated_label(fallback_label_name) 

5072 if not isinstance( 

5073 fallback_label_name, elements._truncated_label 

5074 ) 

5075 else fallback_label_name 

5076 ) 

5077 

5078 result_expr = _CompileLabel( 

5079 col_expr, fallback_label_name, alt_names=(proxy_name,) 

5080 ) 

5081 else: 

5082 result_expr = col_expr 

5083 

5084 column_clause_args.update( 

5085 within_columns_clause=within_columns_clause, 

5086 add_to_result_map=add_to_result_map, 

5087 include_table=include_table, 

5088 within_tstring=False, 

5089 ) 

5090 return result_expr._compiler_dispatch(self, **column_clause_args) 

5091 

5092 def format_from_hint_text(self, sqltext, table, hint, iscrud): 

5093 hinttext = self.get_from_hint_text(table, hint) 

5094 if hinttext: 

5095 sqltext += " " + hinttext 

5096 return sqltext 

5097 

5098 def get_select_hint_text(self, byfroms): 

5099 return None 

5100 

5101 def get_from_hint_text( 

5102 self, table: FromClause, text: Optional[str] 

5103 ) -> Optional[str]: 

5104 return None 

5105 

5106 def get_crud_hint_text(self, table, text): 

5107 return None 

5108 

5109 def get_statement_hint_text(self, hint_texts): 

5110 return " ".join(hint_texts) 

5111 

5112 _default_stack_entry: _CompilerStackEntry 

5113 

5114 if not typing.TYPE_CHECKING: 

5115 _default_stack_entry = util.immutabledict( 

5116 [("correlate_froms", frozenset()), ("asfrom_froms", frozenset())] 

5117 ) 

5118 

5119 def _display_froms_for_select( 

5120 self, select_stmt, asfrom, lateral=False, **kw 

5121 ): 

5122 # utility method to help external dialects 

5123 # get the correct from list for a select. 

5124 # specifically the oracle dialect needs this feature 

5125 # right now. 

5126 toplevel = not self.stack 

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

5128 

5129 compile_state = select_stmt._compile_state_factory( 

5130 select_stmt, self, asfrom=asfrom, lateral=lateral 

5131 ) 

5132 

5133 explicit, implicit = self._enclosing_correlate_froms( 

5134 entry, asfrom, lateral 

5135 ) 

5136 return compile_state._get_display_froms( 

5137 explicit_correlate_froms=explicit, 

5138 implicit_correlate_froms=implicit, 

5139 ) 

5140 

5141 def _enclosing_correlate_froms( 

5142 self, entry: _CompilerStackEntry, asfrom: bool, lateral: bool 

5143 ) -> Tuple[AbstractSet[FromClause], AbstractSet[FromClause]]: 

5144 """Given the stack entry of an enclosing statement, return the 

5145 explicit and implicit correlate FROMs for a SELECT that's embedded 

5146 within it, as passed to :meth:`.SelectState._get_display_froms`. 

5147 

5148 explicit_correlate_froms: FROMs of enclosing SELECTs that this SELECT 

5149 may correlate to when explicit correlation is used, i.e. 

5150 :meth:`.Select.correlate` or :meth:`.Select.correlate_except`. 

5151 Includes the FROMs of all enclosing SELECTs. 

5152 

5153 implicit_correlate_froms: FROMs of enclosing SELECTs that this SELECT 

5154 will correlate to automatically, assuming auto-correlation is in 

5155 effect. Includes only the FROMs of the immediately enclosing SELECT, 

5156 and is empty if this SELECT is a subquery in a FROM clause that's not 

5157 LATERAL. Is always a subset of explicit_correlate_froms. 

5158 

5159 """ 

5160 correlate_froms = entry["correlate_froms"] 

5161 asfrom_froms = entry["asfrom_froms"] 

5162 

5163 if asfrom and not lateral: 

5164 return correlate_froms.difference(asfrom_froms), util.EMPTY_SET 

5165 else: 

5166 return correlate_froms, asfrom_froms 

5167 

5168 translate_select_structure: Any = None 

5169 """if not ``None``, should be a callable which accepts ``(select_stmt, 

5170 **kw)`` and returns a select object. this is used for structural changes 

5171 mostly to accommodate for LIMIT/OFFSET schemes 

5172 

5173 """ 

5174 

5175 def visit_select( 

5176 self, 

5177 select_stmt, 

5178 asfrom=False, 

5179 insert_into=False, 

5180 fromhints=None, 

5181 compound_index=None, 

5182 select_wraps_for=None, 

5183 lateral=False, 

5184 from_linter=None, 

5185 **kwargs, 

5186 ): 

5187 assert select_wraps_for is None, ( 

5188 "SQLAlchemy 1.4 requires use of " 

5189 "the translate_select_structure hook for structural " 

5190 "translations of SELECT objects" 

5191 ) 

5192 if self._collect_params: 

5193 self._add_to_params(select_stmt) 

5194 

5195 # initial setup of SELECT. the compile_state_factory may now 

5196 # be creating a totally different SELECT from the one that was 

5197 # passed in. for ORM use this will convert from an ORM-state 

5198 # SELECT to a regular "Core" SELECT. other composed operations 

5199 # such as computation of joins will be performed. 

5200 

5201 kwargs["within_columns_clause"] = False 

5202 

5203 compile_state = select_stmt._compile_state_factory( 

5204 select_stmt, self, asfrom=asfrom, lateral=lateral, **kwargs 

5205 ) 

5206 kwargs["ambiguous_table_name_map"] = ( 

5207 compile_state._ambiguous_table_name_map 

5208 ) 

5209 

5210 select_stmt = compile_state.statement 

5211 

5212 toplevel = not self.stack 

5213 

5214 if toplevel and not self.compile_state: 

5215 self.compile_state = compile_state 

5216 

5217 is_embedded_select = compound_index is not None or insert_into 

5218 

5219 # translate step for Oracle, SQL Server which often need to 

5220 # restructure the SELECT to allow for LIMIT/OFFSET and possibly 

5221 # other conditions 

5222 if self.translate_select_structure: 

5223 new_select_stmt = self.translate_select_structure( 

5224 select_stmt, asfrom=asfrom, **kwargs 

5225 ) 

5226 

5227 # if SELECT was restructured, maintain a link to the originals 

5228 # and assemble a new compile state 

5229 if new_select_stmt is not select_stmt: 

5230 compile_state_wraps_for = compile_state 

5231 select_wraps_for = select_stmt 

5232 select_stmt = new_select_stmt 

5233 

5234 compile_state = select_stmt._compile_state_factory( 

5235 select_stmt, 

5236 self, 

5237 asfrom=asfrom, 

5238 lateral=lateral, 

5239 **kwargs, 

5240 ) 

5241 select_stmt = compile_state.statement 

5242 

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

5244 

5245 populate_result_map = need_column_expressions = ( 

5246 toplevel 

5247 or entry.get("need_result_map_for_compound", False) 

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

5249 ) 

5250 

5251 # indicates there is a CompoundSelect in play and we are not the 

5252 # first select 

5253 if compound_index: 

5254 populate_result_map = False 

5255 

5256 # this was first proposed as part of #3372; however, it is not 

5257 # reached in current tests and could possibly be an assertion 

5258 # instead. 

5259 if not populate_result_map and "add_to_result_map" in kwargs: 

5260 del kwargs["add_to_result_map"] 

5261 

5262 froms = self._setup_select_stack( 

5263 select_stmt, compile_state, entry, asfrom, lateral, compound_index 

5264 ) 

5265 

5266 column_clause_args = kwargs.copy() 

5267 column_clause_args.update( 

5268 {"within_label_clause": False, "within_columns_clause": False} 

5269 ) 

5270 

5271 text = "SELECT " # we're off to a good start ! 

5272 

5273 if select_stmt._post_select_clause is not None: 

5274 psc = self.process(select_stmt._post_select_clause, **kwargs) 

5275 if psc is not None: 

5276 text += psc + " " 

5277 

5278 if select_stmt._hints: 

5279 hint_text, byfrom = self._setup_select_hints(select_stmt) 

5280 if hint_text: 

5281 text += hint_text + " " 

5282 else: 

5283 byfrom = None 

5284 

5285 if select_stmt._independent_ctes: 

5286 self._dispatch_independent_ctes(select_stmt, kwargs) 

5287 

5288 if select_stmt._prefixes: 

5289 text += self._generate_prefixes( 

5290 select_stmt, select_stmt._prefixes, **kwargs 

5291 ) 

5292 

5293 text += self.get_select_precolumns(select_stmt, **kwargs) 

5294 

5295 if select_stmt._pre_columns_clause is not None: 

5296 pcc = self.process(select_stmt._pre_columns_clause, **kwargs) 

5297 if pcc is not None: 

5298 text += pcc + " " 

5299 

5300 # the actual list of columns to print in the SELECT column list. 

5301 inner_columns = [ 

5302 c 

5303 for c in [ 

5304 self._label_select_column( 

5305 select_stmt, 

5306 column, 

5307 populate_result_map, 

5308 asfrom, 

5309 column_clause_args, 

5310 name=name, 

5311 proxy_name=proxy_name, 

5312 fallback_label_name=fallback_label_name, 

5313 column_is_repeated=repeated, 

5314 need_column_expressions=need_column_expressions, 

5315 ) 

5316 for ( 

5317 name, 

5318 proxy_name, 

5319 fallback_label_name, 

5320 column, 

5321 repeated, 

5322 ) in compile_state.columns_plus_names 

5323 ] 

5324 if c is not None 

5325 ] 

5326 

5327 if populate_result_map and select_wraps_for is not None: 

5328 # if this select was generated from translate_select, 

5329 # rewrite the targeted columns in the result map 

5330 

5331 translate = dict( 

5332 zip( 

5333 [ 

5334 name 

5335 for ( 

5336 key, 

5337 proxy_name, 

5338 fallback_label_name, 

5339 name, 

5340 repeated, 

5341 ) in compile_state.columns_plus_names 

5342 ], 

5343 [ 

5344 name 

5345 for ( 

5346 key, 

5347 proxy_name, 

5348 fallback_label_name, 

5349 name, 

5350 repeated, 

5351 ) in compile_state_wraps_for.columns_plus_names 

5352 ], 

5353 ) 

5354 ) 

5355 

5356 self._result_columns = [ 

5357 ResultColumnsEntry( 

5358 key, name, tuple(translate.get(o, o) for o in obj), type_ 

5359 ) 

5360 for key, name, obj, type_ in self._result_columns 

5361 ] 

5362 

5363 text = self._compose_select_body( 

5364 text, 

5365 select_stmt, 

5366 compile_state, 

5367 inner_columns, 

5368 froms, 

5369 byfrom, 

5370 toplevel, 

5371 kwargs, 

5372 ) 

5373 

5374 if select_stmt._post_body_clause is not None: 

5375 pbc = self.process(select_stmt._post_body_clause, **kwargs) 

5376 if pbc: 

5377 text += " " + pbc 

5378 

5379 if select_stmt._statement_hints: 

5380 per_dialect = [ 

5381 ht 

5382 for (dialect_name, ht) in select_stmt._statement_hints 

5383 if dialect_name in ("*", self.dialect.name) 

5384 ] 

5385 if per_dialect: 

5386 text += " " + self.get_statement_hint_text(per_dialect) 

5387 

5388 # In compound query, CTEs are shared at the compound level 

5389 if self.ctes and (not is_embedded_select or toplevel): 

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

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

5392 

5393 if select_stmt._suffixes: 

5394 text += " " + self._generate_prefixes( 

5395 select_stmt, select_stmt._suffixes, **kwargs 

5396 ) 

5397 

5398 self.stack.pop(-1) 

5399 

5400 return text 

5401 

5402 def _setup_select_hints( 

5403 self, select: Select[Unpack[TupleAny]] 

5404 ) -> Tuple[str, _FromHintsType]: 

5405 byfrom = { 

5406 from_: hinttext 

5407 % {"name": from_._compiler_dispatch(self, ashint=True)} 

5408 for (from_, dialect), hinttext in select._hints.items() 

5409 if dialect in ("*", self.dialect.name) 

5410 } 

5411 hint_text = self.get_select_hint_text(byfrom) 

5412 return hint_text, byfrom 

5413 

5414 def _setup_select_stack( 

5415 self, select, compile_state, entry, asfrom, lateral, compound_index 

5416 ): 

5417 if compound_index == 0: 

5418 entry["select_0"] = select 

5419 elif compound_index: 

5420 select_0 = entry["select_0"] 

5421 numcols = len(select_0._all_selected_columns) 

5422 

5423 if len(compile_state.columns_plus_names) != numcols: 

5424 raise exc.CompileError( 

5425 "All selectables passed to " 

5426 "CompoundSelect must have identical numbers of " 

5427 "columns; select #%d has %d columns, select " 

5428 "#%d has %d" 

5429 % ( 

5430 1, 

5431 numcols, 

5432 compound_index + 1, 

5433 len(select._all_selected_columns), 

5434 ) 

5435 ) 

5436 

5437 explicit, implicit = self._enclosing_correlate_froms( 

5438 entry, asfrom, lateral 

5439 ) 

5440 froms = compile_state._get_display_froms( 

5441 explicit_correlate_froms=explicit, 

5442 implicit_correlate_froms=implicit, 

5443 ) 

5444 

5445 new_correlate_froms = set(_from_objects(*froms)) 

5446 all_correlate_froms = new_correlate_froms.union( 

5447 entry["correlate_froms"] 

5448 ) 

5449 

5450 new_entry: _CompilerStackEntry = { 

5451 "asfrom_froms": new_correlate_froms, 

5452 "correlate_froms": all_correlate_froms, 

5453 "selectable": select, 

5454 "compile_state": compile_state, 

5455 } 

5456 self.stack.append(new_entry) 

5457 

5458 return froms 

5459 

5460 def _compose_select_body( 

5461 self, 

5462 text, 

5463 select, 

5464 compile_state, 

5465 inner_columns, 

5466 froms, 

5467 byfrom, 

5468 toplevel, 

5469 kwargs, 

5470 ): 

5471 text += ", ".join(inner_columns) 

5472 

5473 if self.linting & COLLECT_CARTESIAN_PRODUCTS: 

5474 from_linter = FromLinter({}, set()) 

5475 warn_linting = self.linting & WARN_LINTING 

5476 if toplevel: 

5477 self.from_linter = from_linter 

5478 else: 

5479 from_linter = None 

5480 warn_linting = False 

5481 

5482 # adjust the whitespace for no inner columns, part of #9440, 

5483 # so that a no-col SELECT comes out as "SELECT WHERE..." or 

5484 # "SELECT FROM ...". 

5485 # while it would be better to have built the SELECT starting string 

5486 # without trailing whitespace first, then add whitespace only if inner 

5487 # cols were present, this breaks compatibility with various custom 

5488 # compilation schemes that are currently being tested. 

5489 if not inner_columns: 

5490 text = text.rstrip() 

5491 

5492 if froms: 

5493 text += " \nFROM " 

5494 

5495 if select._hints: 

5496 text += ", ".join( 

5497 [ 

5498 f._compiler_dispatch( 

5499 self, 

5500 asfrom=True, 

5501 fromhints=byfrom, 

5502 from_linter=from_linter, 

5503 **kwargs, 

5504 ) 

5505 for f in froms 

5506 ] 

5507 ) 

5508 else: 

5509 text += ", ".join( 

5510 [ 

5511 f._compiler_dispatch( 

5512 self, 

5513 asfrom=True, 

5514 from_linter=from_linter, 

5515 **kwargs, 

5516 ) 

5517 for f in froms 

5518 ] 

5519 ) 

5520 else: 

5521 text += self.default_from() 

5522 

5523 if select._where_criteria: 

5524 t = self._generate_delimited_and_list( 

5525 select._where_criteria, from_linter=from_linter, **kwargs 

5526 ) 

5527 if t: 

5528 text += " \nWHERE " + t 

5529 

5530 if warn_linting: 

5531 assert from_linter is not None 

5532 from_linter.warn() 

5533 

5534 if select._group_by_clauses: 

5535 text += self.group_by_clause(select, **kwargs) 

5536 

5537 if select._having_criteria: 

5538 t = self._generate_delimited_and_list( 

5539 select._having_criteria, **kwargs 

5540 ) 

5541 if t: 

5542 text += " \nHAVING " + t 

5543 

5544 if select._post_criteria_clause is not None: 

5545 pcc = self.process(select._post_criteria_clause, **kwargs) 

5546 if pcc is not None: 

5547 text += " \n" + pcc 

5548 

5549 if select._order_by_clauses: 

5550 text += self.order_by_clause(select, **kwargs) 

5551 

5552 if select._has_row_limiting_clause: 

5553 text += self._row_limit_clause(select, **kwargs) 

5554 

5555 if select._for_update_arg is not None: 

5556 text += self.for_update_clause(select, **kwargs) 

5557 

5558 return text 

5559 

5560 def _generate_prefixes(self, stmt, prefixes, **kw): 

5561 clause = " ".join( 

5562 prefix._compiler_dispatch(self, **kw) 

5563 for prefix, dialect_name in prefixes 

5564 if dialect_name in (None, "*") or dialect_name == self.dialect.name 

5565 ) 

5566 if clause: 

5567 clause += " " 

5568 return clause 

5569 

5570 def _render_cte_clause( 

5571 self, 

5572 nesting_level=None, 

5573 include_following_stack=False, 

5574 ): 

5575 """ 

5576 include_following_stack 

5577 Also render the nesting CTEs on the next stack. Useful for 

5578 SQL structures like UNION or INSERT that can wrap SELECT 

5579 statements containing nesting CTEs. 

5580 """ 

5581 if not self.ctes: 

5582 return "" 

5583 

5584 ctes: MutableMapping[CTE, str] 

5585 

5586 if nesting_level and nesting_level > 1: 

5587 ctes = util.OrderedDict() 

5588 for cte in list(self.ctes.keys()): 

5589 cte_level, cte_name, cte_opts = self.level_name_by_cte[ 

5590 cte._get_reference_cte() 

5591 ] 

5592 nesting = cte.nesting or cte_opts.nesting 

5593 is_rendered_level = cte_level == nesting_level or ( 

5594 include_following_stack and cte_level == nesting_level + 1 

5595 ) 

5596 if not (nesting and is_rendered_level): 

5597 continue 

5598 

5599 ctes[cte] = self.ctes[cte] 

5600 

5601 else: 

5602 ctes = self.ctes 

5603 

5604 if not ctes: 

5605 return "" 

5606 ctes_recursive = any([cte.recursive for cte in ctes]) 

5607 

5608 cte_text = self.get_cte_preamble(ctes_recursive) + " " 

5609 cte_text += ", \n".join([txt for txt in ctes.values()]) 

5610 cte_text += "\n " 

5611 

5612 if nesting_level and nesting_level > 1: 

5613 for cte in list(ctes.keys()): 

5614 cte_level, cte_name, cte_opts = self.level_name_by_cte[ 

5615 cte._get_reference_cte() 

5616 ] 

5617 del self.ctes[cte] 

5618 del self.ctes_by_level_name[(cte_level, cte_name)] 

5619 del self.level_name_by_cte[cte._get_reference_cte()] 

5620 

5621 return cte_text 

5622 

5623 def get_cte_preamble(self, recursive): 

5624 if recursive: 

5625 return "WITH RECURSIVE" 

5626 else: 

5627 return "WITH" 

5628 

5629 def get_select_precolumns(self, select: Select[Any], **kw: Any) -> str: 

5630 """Called when building a ``SELECT`` statement, position is just 

5631 before column list. 

5632 

5633 """ 

5634 if select._distinct_on: 

5635 util.warn_deprecated( 

5636 "DISTINCT ON is currently supported only by the PostgreSQL " 

5637 "dialect. Use of DISTINCT ON for other backends is currently " 

5638 "silently ignored, however this usage is deprecated, and will " 

5639 "raise CompileError in a future release for all backends " 

5640 "that do not support this syntax.", 

5641 version="1.4", 

5642 ) 

5643 return "DISTINCT " if select._distinct else "" 

5644 

5645 def group_by_clause(self, select, **kw): 

5646 """allow dialects to customize how GROUP BY is rendered.""" 

5647 

5648 group_by = self._generate_delimited_list( 

5649 select._group_by_clauses, OPERATORS[operators.comma_op], **kw 

5650 ) 

5651 if group_by: 

5652 return " GROUP BY " + group_by 

5653 else: 

5654 return "" 

5655 

5656 def order_by_clause(self, select, **kw): 

5657 """allow dialects to customize how ORDER BY is rendered.""" 

5658 

5659 order_by = self._generate_delimited_list( 

5660 select._order_by_clauses, OPERATORS[operators.comma_op], **kw 

5661 ) 

5662 

5663 if order_by: 

5664 return " ORDER BY " + order_by 

5665 else: 

5666 return "" 

5667 

5668 def for_update_clause(self, select, **kw): 

5669 return " FOR UPDATE" 

5670 

5671 def returning_clause( 

5672 self, 

5673 stmt: UpdateBase, 

5674 returning_cols: Sequence[_ColumnsClauseElement], 

5675 *, 

5676 populate_result_map: bool, 

5677 **kw: Any, 

5678 ) -> str: 

5679 columns = [ 

5680 self._label_returning_column( 

5681 stmt, 

5682 column, 

5683 populate_result_map, 

5684 fallback_label_name=fallback_label_name, 

5685 column_is_repeated=repeated, 

5686 name=name, 

5687 proxy_name=proxy_name, 

5688 **kw, 

5689 ) 

5690 for ( 

5691 name, 

5692 proxy_name, 

5693 fallback_label_name, 

5694 column, 

5695 repeated, 

5696 ) in stmt._generate_columns_plus_names( 

5697 True, cols=base._select_iterables(returning_cols) 

5698 ) 

5699 ] 

5700 

5701 return "RETURNING " + ", ".join(columns) 

5702 

5703 def limit_clause(self, select, **kw): 

5704 text = "" 

5705 if select._limit_clause is not None: 

5706 text += "\n LIMIT " + self.process(select._limit_clause, **kw) 

5707 if select._offset_clause is not None: 

5708 if select._limit_clause is None: 

5709 text += "\n LIMIT -1" 

5710 text += " OFFSET " + self.process(select._offset_clause, **kw) 

5711 return text 

5712 

5713 def fetch_clause( 

5714 self, 

5715 select, 

5716 fetch_clause=None, 

5717 require_offset=False, 

5718 use_literal_execute_for_simple_int=False, 

5719 **kw, 

5720 ): 

5721 if fetch_clause is None: 

5722 fetch_clause = select._fetch_clause 

5723 fetch_clause_options = select._fetch_clause_options 

5724 else: 

5725 fetch_clause_options = {"percent": False, "with_ties": False} 

5726 

5727 text = "" 

5728 

5729 if select._offset_clause is not None: 

5730 offset_clause = select._offset_clause 

5731 if ( 

5732 use_literal_execute_for_simple_int 

5733 and select._simple_int_clause(offset_clause) 

5734 ): 

5735 offset_clause = offset_clause.render_literal_execute() 

5736 offset_str = self.process(offset_clause, **kw) 

5737 text += "\n OFFSET %s ROWS" % offset_str 

5738 elif require_offset: 

5739 text += "\n OFFSET 0 ROWS" 

5740 

5741 if fetch_clause is not None: 

5742 if ( 

5743 use_literal_execute_for_simple_int 

5744 and select._simple_int_clause(fetch_clause) 

5745 ): 

5746 fetch_clause = fetch_clause.render_literal_execute() 

5747 text += "\n FETCH FIRST %s%s ROWS %s" % ( 

5748 self.process(fetch_clause, **kw), 

5749 " PERCENT" if fetch_clause_options["percent"] else "", 

5750 "WITH TIES" if fetch_clause_options["with_ties"] else "ONLY", 

5751 ) 

5752 return text 

5753 

5754 def visit_table( 

5755 self, 

5756 table, 

5757 asfrom=False, 

5758 iscrud=False, 

5759 ashint=False, 

5760 fromhints=None, 

5761 use_schema=True, 

5762 from_linter=None, 

5763 ambiguous_table_name_map=None, 

5764 enclosing_alias=None, 

5765 within_tstring=False, 

5766 **kwargs, 

5767 ): 

5768 if from_linter: 

5769 from_linter.froms[table] = table.fullname 

5770 

5771 if asfrom or ashint or within_tstring: 

5772 effective_schema = self.preparer.schema_for_object(table) 

5773 

5774 if use_schema and effective_schema: 

5775 ret = ( 

5776 self.preparer.quote_schema(effective_schema) 

5777 + "." 

5778 + self.preparer.quote(table.name) 

5779 ) 

5780 else: 

5781 ret = self.preparer.quote(table.name) 

5782 

5783 if ( 

5784 ( 

5785 enclosing_alias is None 

5786 or enclosing_alias.element is not table 

5787 ) 

5788 and not effective_schema 

5789 and ambiguous_table_name_map 

5790 and table.name in ambiguous_table_name_map 

5791 ): 

5792 anon_name = self._truncated_identifier( 

5793 "alias", ambiguous_table_name_map[table.name] 

5794 ) 

5795 

5796 ret = ret + self.get_render_as_alias_suffix( 

5797 self.preparer.format_alias(None, anon_name) 

5798 ) 

5799 

5800 if fromhints and table in fromhints: 

5801 ret = self.format_from_hint_text( 

5802 ret, table, fromhints[table], iscrud 

5803 ) 

5804 return ret 

5805 else: 

5806 return "" 

5807 

5808 def visit_join(self, join, asfrom=False, from_linter=None, **kwargs): 

5809 if from_linter: 

5810 from_linter.edges.update( 

5811 itertools.product( 

5812 _de_clone(join.left._from_objects), 

5813 _de_clone(join.right._from_objects), 

5814 ) 

5815 ) 

5816 

5817 if join.full: 

5818 join_type = " FULL OUTER JOIN " 

5819 elif join.isouter: 

5820 join_type = " LEFT OUTER JOIN " 

5821 else: 

5822 join_type = " JOIN " 

5823 return ( 

5824 join.left._compiler_dispatch( 

5825 self, asfrom=True, from_linter=from_linter, **kwargs 

5826 ) 

5827 + join_type 

5828 + join.right._compiler_dispatch( 

5829 self, asfrom=True, from_linter=from_linter, **kwargs 

5830 ) 

5831 + " ON " 

5832 # TODO: likely need asfrom=True here? 

5833 + join.onclause._compiler_dispatch( 

5834 self, from_linter=from_linter, **kwargs 

5835 ) 

5836 ) 

5837 

5838 def _setup_crud_hints(self, stmt, table_text): 

5839 dialect_hints = { 

5840 table: hint_text 

5841 for (table, dialect), hint_text in stmt._hints.items() 

5842 if dialect in ("*", self.dialect.name) 

5843 } 

5844 if stmt.table in dialect_hints: 

5845 table_text = self.format_from_hint_text( 

5846 table_text, stmt.table, dialect_hints[stmt.table], True 

5847 ) 

5848 return dialect_hints, table_text 

5849 

5850 # within the realm of "insertmanyvalues sentinel columns", 

5851 # these lookups match different kinds of Column() configurations 

5852 # to specific backend capabilities. they are broken into two 

5853 # lookups, one for autoincrement columns and the other for non 

5854 # autoincrement columns 

5855 _sentinel_col_non_autoinc_lookup = util.immutabledict( 

5856 { 

5857 _SentinelDefaultCharacterization.CLIENTSIDE: ( 

5858 InsertmanyvaluesSentinelOpts._SUPPORTED_OR_NOT 

5859 ), 

5860 _SentinelDefaultCharacterization.SENTINEL_DEFAULT: ( 

5861 InsertmanyvaluesSentinelOpts._SUPPORTED_OR_NOT 

5862 ), 

5863 _SentinelDefaultCharacterization.NONE: ( 

5864 InsertmanyvaluesSentinelOpts._SUPPORTED_OR_NOT 

5865 ), 

5866 _SentinelDefaultCharacterization.IDENTITY: ( 

5867 InsertmanyvaluesSentinelOpts.IDENTITY 

5868 ), 

5869 _SentinelDefaultCharacterization.SEQUENCE: ( 

5870 InsertmanyvaluesSentinelOpts.SEQUENCE 

5871 ), 

5872 _SentinelDefaultCharacterization.MONOTONIC_FUNCTION: ( 

5873 InsertmanyvaluesSentinelOpts.MONOTONIC_FUNCTION 

5874 ), 

5875 } 

5876 ) 

5877 _sentinel_col_autoinc_lookup = _sentinel_col_non_autoinc_lookup.union( 

5878 { 

5879 _SentinelDefaultCharacterization.NONE: ( 

5880 InsertmanyvaluesSentinelOpts.AUTOINCREMENT 

5881 ), 

5882 } 

5883 ) 

5884 

5885 def _get_sentinel_column_for_table( 

5886 self, table: Table 

5887 ) -> Optional[Sequence[Column[Any]]]: 

5888 """given a :class:`.Table`, return a usable sentinel column or 

5889 columns for this dialect if any. 

5890 

5891 Return None if no sentinel columns could be identified, or raise an 

5892 error if a column was marked as a sentinel explicitly but isn't 

5893 compatible with this dialect. 

5894 

5895 """ 

5896 

5897 sentinel_opts = self.dialect.insertmanyvalues_implicit_sentinel 

5898 sentinel_characteristics = table._sentinel_column_characteristics 

5899 

5900 sent_cols = sentinel_characteristics.columns 

5901 

5902 if sent_cols is None: 

5903 return None 

5904 

5905 if sentinel_characteristics.is_autoinc: 

5906 bitmask = self._sentinel_col_autoinc_lookup.get( 

5907 sentinel_characteristics.default_characterization, 0 

5908 ) 

5909 else: 

5910 bitmask = self._sentinel_col_non_autoinc_lookup.get( 

5911 sentinel_characteristics.default_characterization, 0 

5912 ) 

5913 

5914 if sentinel_opts & bitmask: 

5915 return sent_cols 

5916 

5917 if sentinel_characteristics.is_explicit: 

5918 # a column was explicitly marked as insert_sentinel=True, 

5919 # however it is not compatible with this dialect. they should 

5920 # not indicate this column as a sentinel if they need to include 

5921 # this dialect. 

5922 

5923 # TODO: do we want non-primary key explicit sentinel cols 

5924 # that can gracefully degrade for some backends? 

5925 # insert_sentinel="degrade" perhaps. not for the initial release. 

5926 # I am hoping people are generally not dealing with this sentinel 

5927 # business at all. 

5928 

5929 # if is_explicit is True, there will be only one sentinel column. 

5930 

5931 raise exc.InvalidRequestError( 

5932 f"Column {sent_cols[0]} can't be explicitly " 

5933 "marked as a sentinel column when using the " 

5934 f"{self.dialect.name} dialect, as the " 

5935 "particular type of default generation on this column is " 

5936 "not currently compatible with this dialect's specific " 

5937 f"INSERT..RETURNING syntax which can receive the " 

5938 "server-generated value in " 

5939 "a deterministic way. To remove this error, remove " 

5940 "insert_sentinel=True from primary key autoincrement " 

5941 "columns; these columns are automatically used as " 

5942 "sentinels for supported dialects in any case." 

5943 ) 

5944 

5945 return None 

5946 

5947 def _deliver_insertmanyvalues_batches( 

5948 self, 

5949 statement: str, 

5950 parameters: _DBAPIMultiExecuteParams, 

5951 compiled_parameters: List[_MutableCoreSingleExecuteParams], 

5952 generic_setinputsizes: Optional[_GenericSetInputSizesType], 

5953 batch_size: int, 

5954 sort_by_parameter_order: bool, 

5955 schema_translate_map: Optional[SchemaTranslateMapType], 

5956 ) -> Iterator[_InsertManyValuesBatch]: 

5957 imv = self._insertmanyvalues 

5958 assert imv is not None 

5959 

5960 if not imv.sentinel_param_keys: 

5961 _sentinel_from_params = None 

5962 else: 

5963 _sentinel_from_params = operator.itemgetter( 

5964 *imv.sentinel_param_keys 

5965 ) 

5966 

5967 lenparams = len(parameters) 

5968 if imv.is_default_expr and not self.dialect.supports_default_metavalue: 

5969 # backend doesn't support 

5970 # INSERT INTO table (pk_col) VALUES (DEFAULT), (DEFAULT), ... 

5971 # at the moment this is basically SQL Server due to 

5972 # not being able to use DEFAULT for identity column 

5973 # just yield out that many single statements! still 

5974 # faster than a whole connection.execute() call ;) 

5975 # 

5976 # note we still are taking advantage of the fact that we know 

5977 # we are using RETURNING. The generalized approach of fetching 

5978 # cursor.lastrowid etc. still goes through the more heavyweight 

5979 # "ExecutionContext per statement" system as it isn't usable 

5980 # as a generic "RETURNING" approach 

5981 use_row_at_a_time = True 

5982 downgraded = False 

5983 elif not self.dialect.supports_multivalues_insert or ( 

5984 sort_by_parameter_order 

5985 and self._result_columns 

5986 and ( 

5987 imv.sentinel_columns is None 

5988 or ( 

5989 imv.includes_upsert_behaviors 

5990 and not imv.embed_values_counter 

5991 ) 

5992 ) 

5993 ): 

5994 # deterministic order was requested and the compiler could 

5995 # not organize sentinel columns for this dialect/statement. 

5996 # use row at a time. Note: if embed_values_counter is True, 

5997 # the counter itself provides the ordering capability we need, 

5998 # so we can use batch mode even with upsert behaviors. 

5999 use_row_at_a_time = True 

6000 downgraded = True 

6001 elif ( 

6002 imv.has_upsert_bound_parameters 

6003 and not imv.embed_values_counter 

6004 and self._result_columns 

6005 ): 

6006 # For upsert behaviors (ON CONFLICT DO UPDATE, etc.) with RETURNING 

6007 # and parametrized bindparams in the SET clause, we must use 

6008 # row-at-a-time. Batching multiple rows in a single statement 

6009 # doesn't work when the SET clause contains bound parameters that 

6010 # will receive different values per row, as there's only one SET 

6011 # clause per statement. See issue #13130. 

6012 use_row_at_a_time = True 

6013 downgraded = True 

6014 else: 

6015 use_row_at_a_time = False 

6016 downgraded = False 

6017 

6018 if use_row_at_a_time: 

6019 for batchnum, (param, compiled_param) in enumerate( 

6020 cast( 

6021 "Sequence[Tuple[_DBAPISingleExecuteParams, _MutableCoreSingleExecuteParams]]", # noqa: E501 

6022 zip(parameters, compiled_parameters), 

6023 ), 

6024 1, 

6025 ): 

6026 yield _InsertManyValuesBatch( 

6027 statement, 

6028 param, 

6029 generic_setinputsizes, 

6030 [param], 

6031 ( 

6032 [_sentinel_from_params(compiled_param)] 

6033 if _sentinel_from_params 

6034 else [] 

6035 ), 

6036 1, 

6037 batchnum, 

6038 lenparams, 

6039 sort_by_parameter_order, 

6040 downgraded, 

6041 ) 

6042 return 

6043 

6044 if schema_translate_map: 

6045 rst = functools.partial( 

6046 self.preparer._render_schema_translates, 

6047 schema_translate_map=schema_translate_map, 

6048 ) 

6049 else: 

6050 rst = None 

6051 

6052 imv_single_values_expr = imv.single_values_expr 

6053 if rst: 

6054 imv_single_values_expr = rst(imv_single_values_expr) 

6055 

6056 executemany_values = f"({imv_single_values_expr})" 

6057 statement = statement.replace(executemany_values, "__EXECMANY_TOKEN__") 

6058 

6059 # Use optional insertmanyvalues_max_parameters 

6060 # to further shrink the batch size so that there are no more than 

6061 # insertmanyvalues_max_parameters params. 

6062 # Currently used by SQL Server, which limits statements to 2100 bound 

6063 # parameters (actually 2099). 

6064 max_params = self.dialect.insertmanyvalues_max_parameters 

6065 if max_params: 

6066 total_num_of_params = len(self.bind_names) 

6067 num_params_per_batch = len(imv.insert_crud_params) 

6068 num_params_outside_of_batch = ( 

6069 total_num_of_params - num_params_per_batch 

6070 ) 

6071 batch_size = min( 

6072 batch_size, 

6073 ( 

6074 (max_params - num_params_outside_of_batch) 

6075 // num_params_per_batch 

6076 ), 

6077 ) 

6078 

6079 batches = cast("List[Sequence[Any]]", list(parameters)) 

6080 compiled_batches = cast( 

6081 "List[Sequence[Any]]", list(compiled_parameters) 

6082 ) 

6083 

6084 processed_setinputsizes: Optional[_GenericSetInputSizesType] = None 

6085 batchnum = 1 

6086 total_batches = lenparams // batch_size + ( 

6087 1 if lenparams % batch_size else 0 

6088 ) 

6089 

6090 insert_crud_params = imv.insert_crud_params 

6091 assert insert_crud_params is not None 

6092 

6093 if rst: 

6094 insert_crud_params = [ 

6095 (col, key, rst(expr), st) 

6096 for col, key, expr, st in insert_crud_params 

6097 ] 

6098 

6099 escaped_bind_names: Mapping[str, str] 

6100 expand_pos_lower_index = expand_pos_upper_index = 0 

6101 

6102 if not self.positional: 

6103 if self.escaped_bind_names: 

6104 escaped_bind_names = self.escaped_bind_names 

6105 else: 

6106 escaped_bind_names = {} 

6107 

6108 all_keys = set(parameters[0]) 

6109 

6110 def apply_placeholders(keys, formatted): 

6111 for key in keys: 

6112 key = escaped_bind_names.get(key, key) 

6113 formatted = formatted.replace( 

6114 self.bindtemplate % {"name": key}, 

6115 self.bindtemplate 

6116 % {"name": f"{key}__EXECMANY_INDEX__"}, 

6117 ) 

6118 return formatted 

6119 

6120 if imv.embed_values_counter: 

6121 imv_values_counter = ", _IMV_VALUES_COUNTER" 

6122 else: 

6123 imv_values_counter = "" 

6124 formatted_values_clause = f"""({', '.join( 

6125 apply_placeholders(bind_keys, formatted) 

6126 for _, _, formatted, bind_keys in insert_crud_params 

6127 )}{imv_values_counter})""" 

6128 

6129 keys_to_replace = all_keys.intersection( 

6130 escaped_bind_names.get(key, key) 

6131 for _, _, _, bind_keys in insert_crud_params 

6132 for key in bind_keys 

6133 ) 

6134 base_parameters = { 

6135 key: parameters[0][key] 

6136 for key in all_keys.difference(keys_to_replace) 

6137 } 

6138 

6139 executemany_values_w_comma = "" 

6140 else: 

6141 formatted_values_clause = "" 

6142 keys_to_replace = set() 

6143 base_parameters = {} 

6144 

6145 if imv.embed_values_counter: 

6146 executemany_values_w_comma = ( 

6147 f"({imv_single_values_expr}, _IMV_VALUES_COUNTER), " 

6148 ) 

6149 else: 

6150 executemany_values_w_comma = f"({imv_single_values_expr}), " 

6151 

6152 all_names_we_will_expand: Set[str] = set() 

6153 for elem in imv.insert_crud_params: 

6154 all_names_we_will_expand.update(elem[3]) 

6155 

6156 # get the start and end position in a particular list 

6157 # of parameters where we will be doing the "expanding". 

6158 # statements can have params on either side or both sides, 

6159 # given RETURNING and CTEs 

6160 if all_names_we_will_expand: 

6161 positiontup = self.positiontup 

6162 assert positiontup is not None 

6163 

6164 all_expand_positions = { 

6165 idx 

6166 for idx, name in enumerate(positiontup) 

6167 if name in all_names_we_will_expand 

6168 } 

6169 expand_pos_lower_index = min(all_expand_positions) 

6170 expand_pos_upper_index = max(all_expand_positions) + 1 

6171 assert ( 

6172 len(all_expand_positions) 

6173 == expand_pos_upper_index - expand_pos_lower_index 

6174 ) 

6175 

6176 if self._numeric_binds: 

6177 escaped = re.escape(self._numeric_binds_identifier_char) 

6178 executemany_values_w_comma = re.sub( 

6179 rf"{escaped}\d+", "%s", executemany_values_w_comma 

6180 ) 

6181 

6182 while batches: 

6183 batch = batches[0:batch_size] 

6184 compiled_batch = compiled_batches[0:batch_size] 

6185 

6186 batches[0:batch_size] = [] 

6187 compiled_batches[0:batch_size] = [] 

6188 

6189 if batches: 

6190 current_batch_size = batch_size 

6191 else: 

6192 current_batch_size = len(batch) 

6193 

6194 if generic_setinputsizes: 

6195 # if setinputsizes is present, expand this collection to 

6196 # suit the batch length as well 

6197 # currently this will be mssql+pyodbc for internal dialects 

6198 processed_setinputsizes = [ 

6199 (new_key, len_, typ) 

6200 for new_key, len_, typ in ( 

6201 (f"{key}_{index}", len_, typ) 

6202 for index in range(current_batch_size) 

6203 for key, len_, typ in generic_setinputsizes 

6204 ) 

6205 ] 

6206 

6207 replaced_parameters: Any 

6208 if self.positional: 

6209 num_ins_params = imv.num_positional_params_counted 

6210 

6211 batch_iterator: Iterable[Sequence[Any]] 

6212 extra_params_left: Sequence[Any] 

6213 extra_params_right: Sequence[Any] 

6214 

6215 if num_ins_params == len(batch[0]): 

6216 extra_params_left = extra_params_right = () 

6217 batch_iterator = batch 

6218 else: 

6219 extra_params_left = batch[0][:expand_pos_lower_index] 

6220 extra_params_right = batch[0][expand_pos_upper_index:] 

6221 batch_iterator = ( 

6222 b[expand_pos_lower_index:expand_pos_upper_index] 

6223 for b in batch 

6224 ) 

6225 

6226 if imv.embed_values_counter: 

6227 expanded_values_string = ( 

6228 "".join( 

6229 executemany_values_w_comma.replace( 

6230 "_IMV_VALUES_COUNTER", str(i) 

6231 ) 

6232 for i, _ in enumerate(batch) 

6233 ) 

6234 )[:-2] 

6235 else: 

6236 expanded_values_string = ( 

6237 (executemany_values_w_comma * current_batch_size) 

6238 )[:-2] 

6239 

6240 if self._numeric_binds and num_ins_params > 0: 

6241 # numeric will always number the parameters inside of 

6242 # VALUES (and thus order self.positiontup) to be higher 

6243 # than non-VALUES parameters, no matter where in the 

6244 # statement those non-VALUES parameters appear (this is 

6245 # ensured in _process_numeric by numbering first all 

6246 # params that are not in _values_bindparam) 

6247 # therefore all extra params are always 

6248 # on the left side and numbered lower than the VALUES 

6249 # parameters 

6250 assert not extra_params_right 

6251 

6252 start = expand_pos_lower_index + 1 

6253 end = num_ins_params * (current_batch_size) + start 

6254 

6255 # need to format here, since statement may contain 

6256 # unescaped %, while values_string contains just (%s, %s) 

6257 positions = tuple( 

6258 f"{self._numeric_binds_identifier_char}{i}" 

6259 for i in range(start, end) 

6260 ) 

6261 expanded_values_string = expanded_values_string % positions 

6262 

6263 replaced_statement = statement.replace( 

6264 "__EXECMANY_TOKEN__", expanded_values_string 

6265 ) 

6266 

6267 replaced_parameters = tuple( 

6268 itertools.chain.from_iterable(batch_iterator) 

6269 ) 

6270 

6271 replaced_parameters = ( 

6272 extra_params_left 

6273 + replaced_parameters 

6274 + extra_params_right 

6275 ) 

6276 

6277 else: 

6278 replaced_values_clauses = [] 

6279 replaced_parameters = base_parameters.copy() 

6280 

6281 for i, param in enumerate(batch): 

6282 fmv = formatted_values_clause.replace( 

6283 "EXECMANY_INDEX__", str(i) 

6284 ) 

6285 if imv.embed_values_counter: 

6286 fmv = fmv.replace("_IMV_VALUES_COUNTER", str(i)) 

6287 

6288 replaced_values_clauses.append(fmv) 

6289 replaced_parameters.update( 

6290 {f"{key}__{i}": param[key] for key in keys_to_replace} 

6291 ) 

6292 

6293 replaced_statement = statement.replace( 

6294 "__EXECMANY_TOKEN__", 

6295 ", ".join(replaced_values_clauses), 

6296 ) 

6297 

6298 yield _InsertManyValuesBatch( 

6299 replaced_statement, 

6300 replaced_parameters, 

6301 processed_setinputsizes, 

6302 batch, 

6303 ( 

6304 [_sentinel_from_params(cb) for cb in compiled_batch] 

6305 if _sentinel_from_params 

6306 else [] 

6307 ), 

6308 current_batch_size, 

6309 batchnum, 

6310 total_batches, 

6311 sort_by_parameter_order, 

6312 False, 

6313 ) 

6314 batchnum += 1 

6315 

6316 def visit_insert( 

6317 self, insert_stmt, visited_bindparam=None, visiting_cte=None, **kw 

6318 ): 

6319 compile_state = insert_stmt._compile_state_factory( 

6320 insert_stmt, self, **kw 

6321 ) 

6322 insert_stmt = compile_state.statement 

6323 

6324 if visiting_cte is not None: 

6325 kw["visiting_cte"] = visiting_cte 

6326 toplevel = False 

6327 else: 

6328 toplevel = not self.stack 

6329 

6330 if toplevel: 

6331 self.isinsert = True 

6332 if not self.dml_compile_state: 

6333 self.dml_compile_state = compile_state 

6334 if not self.compile_state: 

6335 self.compile_state = compile_state 

6336 

6337 self.stack.append( 

6338 { 

6339 "correlate_froms": set(), 

6340 "asfrom_froms": set(), 

6341 "selectable": insert_stmt, 

6342 } 

6343 ) 

6344 

6345 counted_bindparam = 0 

6346 

6347 # reset any incoming "visited_bindparam" collection 

6348 visited_bindparam = None 

6349 

6350 # for positional, insertmanyvalues needs to know how many 

6351 # bound parameters are in the VALUES sequence; there's no simple 

6352 # rule because default expressions etc. can have zero or more 

6353 # params inside them. After multiple attempts to figure this out, 

6354 # this very simplistic "count after" works and is 

6355 # likely the least amount of callcounts, though looks clumsy 

6356 if self.positional and visiting_cte is None: 

6357 # if we are inside a CTE, don't count parameters 

6358 # here since they won't be for insertmanyvalues. keep 

6359 # visited_bindparam at None so no counting happens. 

6360 # see #9173 

6361 visited_bindparam = [] 

6362 

6363 crud_params_struct = crud._get_crud_params( 

6364 self, 

6365 insert_stmt, 

6366 compile_state, 

6367 toplevel, 

6368 visited_bindparam=visited_bindparam, 

6369 **kw, 

6370 ) 

6371 

6372 if self.positional and visited_bindparam is not None: 

6373 counted_bindparam = len(visited_bindparam) 

6374 if self._numeric_binds: 

6375 if self._values_bindparam is not None: 

6376 self._values_bindparam += visited_bindparam 

6377 else: 

6378 self._values_bindparam = visited_bindparam 

6379 

6380 crud_params_single = crud_params_struct.single_params 

6381 

6382 if ( 

6383 not crud_params_single 

6384 and not self.dialect.supports_default_values 

6385 and not self.dialect.supports_default_metavalue 

6386 and not self.dialect.supports_empty_insert 

6387 ): 

6388 raise exc.CompileError( 

6389 "The '%s' dialect with current database " 

6390 "version settings does not support empty " 

6391 "inserts." % self.dialect.name 

6392 ) 

6393 

6394 if compile_state._has_multi_parameters: 

6395 if not self.dialect.supports_multivalues_insert: 

6396 raise exc.CompileError( 

6397 "The '%s' dialect with current database " 

6398 "version settings does not support " 

6399 "in-place multirow inserts." % self.dialect.name 

6400 ) 

6401 elif ( 

6402 self.implicit_returning or insert_stmt._returning 

6403 ) and insert_stmt._sort_by_parameter_order: 

6404 raise exc.CompileError( 

6405 "RETURNING cannot be deterministically sorted when " 

6406 "using an INSERT which includes multi-row values()." 

6407 ) 

6408 crud_params_single = crud_params_struct.single_params 

6409 else: 

6410 crud_params_single = crud_params_struct.single_params 

6411 

6412 preparer = self.preparer 

6413 supports_default_values = self.dialect.supports_default_values 

6414 

6415 text = "INSERT " 

6416 

6417 if insert_stmt._prefixes: 

6418 text += self._generate_prefixes( 

6419 insert_stmt, insert_stmt._prefixes, **kw 

6420 ) 

6421 

6422 text += "INTO " 

6423 table_text = preparer.format_table(insert_stmt.table) 

6424 

6425 if insert_stmt._hints: 

6426 _, table_text = self._setup_crud_hints(insert_stmt, table_text) 

6427 

6428 if insert_stmt._independent_ctes: 

6429 self._dispatch_independent_ctes(insert_stmt, kw) 

6430 

6431 text += table_text 

6432 

6433 if crud_params_single or not supports_default_values: 

6434 text += " (%s)" % ", ".join( 

6435 [expr for _, expr, _, _ in crud_params_single] 

6436 ) 

6437 

6438 # look for insertmanyvalues attributes that would have been configured 

6439 # by crud.py as it scanned through the columns to be part of the 

6440 # INSERT 

6441 use_insertmanyvalues = crud_params_struct.use_insertmanyvalues 

6442 named_sentinel_params: Optional[Sequence[str]] = None 

6443 add_sentinel_cols = None 

6444 implicit_sentinel = False 

6445 

6446 returning_cols = self.implicit_returning or insert_stmt._returning 

6447 if returning_cols: 

6448 add_sentinel_cols = crud_params_struct.use_sentinel_columns 

6449 if add_sentinel_cols is not None: 

6450 assert use_insertmanyvalues 

6451 

6452 # search for the sentinel column explicitly present 

6453 # in the INSERT columns list, and additionally check that 

6454 # this column has a bound parameter name set up that's in the 

6455 # parameter list. If both of these cases are present, it means 

6456 # we will have a client side value for the sentinel in each 

6457 # parameter set. 

6458 

6459 _params_by_col = { 

6460 col: param_names 

6461 for col, _, _, param_names in crud_params_single 

6462 } 

6463 named_sentinel_params = [] 

6464 for _add_sentinel_col in add_sentinel_cols: 

6465 if _add_sentinel_col not in _params_by_col: 

6466 named_sentinel_params = None 

6467 break 

6468 param_name = self._within_exec_param_key_getter( 

6469 _add_sentinel_col 

6470 ) 

6471 if param_name not in _params_by_col[_add_sentinel_col]: 

6472 named_sentinel_params = None 

6473 break 

6474 named_sentinel_params.append(param_name) 

6475 

6476 if named_sentinel_params is None: 

6477 # if we are not going to have a client side value for 

6478 # the sentinel in the parameter set, that means it's 

6479 # an autoincrement, an IDENTITY, or a server-side SQL 

6480 # expression like nextval('seqname'). So this is 

6481 # an "implicit" sentinel; we will look for it in 

6482 # RETURNING 

6483 # only, and then sort on it. For this case on PG, 

6484 # SQL Server we have to use a special INSERT form 

6485 # that guarantees the server side function lines up with 

6486 # the entries in the VALUES. 

6487 if ( 

6488 self.dialect.insertmanyvalues_implicit_sentinel 

6489 & InsertmanyvaluesSentinelOpts.ANY_AUTOINCREMENT 

6490 ): 

6491 implicit_sentinel = True 

6492 else: 

6493 # here, we are not using a sentinel at all 

6494 # and we are likely the SQLite dialect. 

6495 # The first add_sentinel_col that we have should not 

6496 # be marked as "insert_sentinel=True". if it was, 

6497 # an error should have been raised in 

6498 # _get_sentinel_column_for_table. 

6499 assert not add_sentinel_cols[0]._insert_sentinel, ( 

6500 "sentinel selection rules should have prevented " 

6501 "us from getting here for this dialect" 

6502 ) 

6503 

6504 # always put the sentinel columns last. even if they are 

6505 # in the returning list already, they will be there twice 

6506 # then. 

6507 returning_cols = list(returning_cols) + list(add_sentinel_cols) 

6508 

6509 returning_clause = self.returning_clause( 

6510 insert_stmt, 

6511 returning_cols, 

6512 populate_result_map=toplevel, 

6513 ) 

6514 

6515 if self.returning_precedes_values: 

6516 text += " " + returning_clause 

6517 

6518 else: 

6519 returning_clause = None 

6520 

6521 if insert_stmt.select is not None: 

6522 # placed here by crud.py 

6523 select_text = self.process( 

6524 self.stack[-1]["insert_from_select"], insert_into=True, **kw 

6525 ) 

6526 

6527 if self.ctes and self.dialect.cte_follows_insert: 

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

6529 text += " %s%s" % ( 

6530 self._render_cte_clause( 

6531 nesting_level=nesting_level, 

6532 include_following_stack=True, 

6533 ), 

6534 select_text, 

6535 ) 

6536 else: 

6537 text += " %s" % select_text 

6538 elif not crud_params_single and supports_default_values: 

6539 text += " DEFAULT VALUES" 

6540 if use_insertmanyvalues: 

6541 self._insertmanyvalues = _InsertManyValues( 

6542 True, 

6543 self.dialect.default_metavalue_token, 

6544 crud_params_single, 

6545 counted_bindparam, 

6546 sort_by_parameter_order=( 

6547 insert_stmt._sort_by_parameter_order 

6548 ), 

6549 includes_upsert_behaviors=( 

6550 insert_stmt._post_values_clause is not None 

6551 ), 

6552 sentinel_columns=add_sentinel_cols, 

6553 num_sentinel_columns=( 

6554 len(add_sentinel_cols) if add_sentinel_cols else 0 

6555 ), 

6556 implicit_sentinel=implicit_sentinel, 

6557 ) 

6558 elif compile_state._has_multi_parameters: 

6559 text += " VALUES %s" % ( 

6560 ", ".join( 

6561 "(%s)" 

6562 % (", ".join(value for _, _, value, _ in crud_param_set)) 

6563 for crud_param_set in crud_params_struct.all_multi_params 

6564 ), 

6565 ) 

6566 elif use_insertmanyvalues: 

6567 if ( 

6568 implicit_sentinel 

6569 and ( 

6570 self.dialect.insertmanyvalues_implicit_sentinel 

6571 & InsertmanyvaluesSentinelOpts.USE_INSERT_FROM_SELECT 

6572 ) 

6573 # this is checking if we have 

6574 # INSERT INTO table (id) VALUES (DEFAULT). 

6575 and not (crud_params_struct.is_default_metavalue_only) 

6576 ): 

6577 # if we have a sentinel column that is server generated, 

6578 # then for selected backends render the VALUES list as a 

6579 # subquery. This is the orderable form supported by 

6580 # PostgreSQL and in fewer cases SQL Server 

6581 embed_sentinel_value = True 

6582 

6583 render_bind_casts = ( 

6584 self.dialect.insertmanyvalues_implicit_sentinel 

6585 & InsertmanyvaluesSentinelOpts.RENDER_SELECT_COL_CASTS 

6586 ) 

6587 

6588 add_sentinel_set = add_sentinel_cols or () 

6589 

6590 insert_single_values_expr = ", ".join( 

6591 [ 

6592 value 

6593 for col, _, value, _ in crud_params_single 

6594 if col not in add_sentinel_set 

6595 ] 

6596 ) 

6597 

6598 colnames = ", ".join( 

6599 f"p{i}" 

6600 for i, cp in enumerate(crud_params_single) 

6601 if cp[0] not in add_sentinel_set 

6602 ) 

6603 

6604 if render_bind_casts: 

6605 # render casts for the SELECT list. For PG, we are 

6606 # already rendering bind casts in the parameter list, 

6607 # selectively for the more "tricky" types like ARRAY. 

6608 # however, even for the "easy" types, if the parameter 

6609 # is NULL for every entry, PG gives up and says 

6610 # "it must be TEXT", which fails for other easy types 

6611 # like ints. So we cast on this side too. 

6612 colnames_w_cast = ", ".join( 

6613 ( 

6614 self.render_bind_cast( 

6615 col.type, 

6616 col.type._unwrapped_dialect_impl(self.dialect), 

6617 f"p{i}", 

6618 ) 

6619 if col not in add_sentinel_set 

6620 else expr 

6621 ) 

6622 for i, (col, _, expr, _) in enumerate( 

6623 crud_params_single 

6624 ) 

6625 ) 

6626 else: 

6627 colnames_w_cast = ", ".join( 

6628 (f"p{i}" if col not in add_sentinel_set else expr) 

6629 for i, (col, _, expr, _) in enumerate( 

6630 crud_params_single 

6631 ) 

6632 ) 

6633 

6634 insert_crud_params = [ 

6635 elem 

6636 for elem in crud_params_single 

6637 if elem[0] not in add_sentinel_set 

6638 ] 

6639 

6640 text += ( 

6641 f" SELECT {colnames_w_cast} FROM " 

6642 f"(VALUES ({insert_single_values_expr})) " 

6643 f"AS imp_sen({colnames}, sen_counter) " 

6644 "ORDER BY sen_counter" 

6645 ) 

6646 

6647 else: 

6648 # otherwise, if no sentinel or backend doesn't support 

6649 # orderable subquery form, use a plain VALUES list 

6650 embed_sentinel_value = False 

6651 insert_crud_params = crud_params_single 

6652 insert_single_values_expr = ", ".join( 

6653 [value for _, _, value, _ in crud_params_single] 

6654 ) 

6655 

6656 text += f" VALUES ({insert_single_values_expr})" 

6657 

6658 self._insertmanyvalues = _InsertManyValues( 

6659 is_default_expr=False, 

6660 single_values_expr=insert_single_values_expr, 

6661 insert_crud_params=insert_crud_params, 

6662 num_positional_params_counted=counted_bindparam, 

6663 sort_by_parameter_order=(insert_stmt._sort_by_parameter_order), 

6664 includes_upsert_behaviors=( 

6665 insert_stmt._post_values_clause is not None 

6666 ), 

6667 sentinel_columns=add_sentinel_cols, 

6668 num_sentinel_columns=( 

6669 len(add_sentinel_cols) if add_sentinel_cols else 0 

6670 ), 

6671 sentinel_param_keys=named_sentinel_params, 

6672 implicit_sentinel=implicit_sentinel, 

6673 embed_values_counter=embed_sentinel_value, 

6674 ) 

6675 

6676 else: 

6677 insert_single_values_expr = ", ".join( 

6678 [value for _, _, value, _ in crud_params_single] 

6679 ) 

6680 

6681 text += f" VALUES ({insert_single_values_expr})" 

6682 

6683 if insert_stmt._post_values_clause is not None: 

6684 post_values_clause = self.process( 

6685 insert_stmt._post_values_clause, **kw 

6686 ) 

6687 if post_values_clause: 

6688 text += " " + post_values_clause 

6689 

6690 if returning_clause and not self.returning_precedes_values: 

6691 text += " " + returning_clause 

6692 

6693 if self.ctes and not self.dialect.cte_follows_insert: 

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

6695 text = ( 

6696 self._render_cte_clause( 

6697 nesting_level=nesting_level, 

6698 include_following_stack=True, 

6699 ) 

6700 + text 

6701 ) 

6702 

6703 self.stack.pop(-1) 

6704 

6705 return text 

6706 

6707 def update_tables_clause(self, update_stmt, from_table, extra_froms, **kw): 

6708 """Provide a hook to override the initial table clause 

6709 in an UPDATE statement. 

6710 

6711 MySQL overrides this. 

6712 

6713 """ 

6714 kw["asfrom"] = True 

6715 return from_table._compiler_dispatch(self, iscrud=True, **kw) 

6716 

6717 def update_from_clause( 

6718 self, update_stmt, from_table, extra_froms, from_hints, **kw 

6719 ): 

6720 """Provide a hook to override the generation of an 

6721 UPDATE..FROM clause. 

6722 MySQL and MSSQL override this. 

6723 """ 

6724 raise NotImplementedError( 

6725 "This backend does not support multiple-table " 

6726 "criteria within UPDATE" 

6727 ) 

6728 

6729 def update_post_criteria_clause( 

6730 self, update_stmt: Update, **kw: Any 

6731 ) -> Optional[str]: 

6732 """provide a hook to override generation after the WHERE criteria 

6733 in an UPDATE statement 

6734 

6735 .. versionadded:: 2.1 

6736 

6737 """ 

6738 if update_stmt._post_criteria_clause is not None: 

6739 return self.process( 

6740 update_stmt._post_criteria_clause, 

6741 **kw, 

6742 ) 

6743 else: 

6744 return None 

6745 

6746 def delete_post_criteria_clause( 

6747 self, delete_stmt: Delete, **kw: Any 

6748 ) -> Optional[str]: 

6749 """provide a hook to override generation after the WHERE criteria 

6750 in a DELETE statement 

6751 

6752 .. versionadded:: 2.1 

6753 

6754 """ 

6755 if delete_stmt._post_criteria_clause is not None: 

6756 return self.process( 

6757 delete_stmt._post_criteria_clause, 

6758 **kw, 

6759 ) 

6760 else: 

6761 return None 

6762 

6763 def visit_update( 

6764 self, 

6765 update_stmt: Update, 

6766 visiting_cte: Optional[CTE] = None, 

6767 **kw: Any, 

6768 ) -> str: 

6769 compile_state = update_stmt._compile_state_factory( 

6770 update_stmt, self, **kw 

6771 ) 

6772 if TYPE_CHECKING: 

6773 assert isinstance(compile_state, UpdateDMLState) 

6774 update_stmt = compile_state.statement # type: ignore[assignment] 

6775 

6776 if visiting_cte is not None: 

6777 kw["visiting_cte"] = visiting_cte 

6778 toplevel = False 

6779 else: 

6780 toplevel = not self.stack 

6781 

6782 if toplevel: 

6783 self.isupdate = True 

6784 if not self.dml_compile_state: 

6785 self.dml_compile_state = compile_state 

6786 if not self.compile_state: 

6787 self.compile_state = compile_state 

6788 

6789 if self.linting & COLLECT_CARTESIAN_PRODUCTS: 

6790 from_linter = FromLinter({}, set()) 

6791 warn_linting = self.linting & WARN_LINTING 

6792 if toplevel: 

6793 self.from_linter = from_linter 

6794 else: 

6795 from_linter = None 

6796 warn_linting = False 

6797 

6798 extra_froms = compile_state._extra_froms 

6799 is_multitable = bool(extra_froms) 

6800 

6801 if is_multitable: 

6802 # main table might be a JOIN 

6803 main_froms = set(_from_objects(update_stmt.table)) 

6804 render_extra_froms = [ 

6805 f for f in extra_froms if f not in main_froms 

6806 ] 

6807 correlate_froms = main_froms.union(extra_froms) 

6808 else: 

6809 render_extra_froms = [] 

6810 correlate_froms = {update_stmt.table} 

6811 

6812 self.stack.append( 

6813 { 

6814 "correlate_froms": correlate_froms, 

6815 "asfrom_froms": correlate_froms, 

6816 "selectable": update_stmt, 

6817 } 

6818 ) 

6819 

6820 text = "UPDATE " 

6821 

6822 if update_stmt._prefixes: 

6823 text += self._generate_prefixes( 

6824 update_stmt, update_stmt._prefixes, **kw 

6825 ) 

6826 

6827 table_text = self.update_tables_clause( 

6828 update_stmt, 

6829 update_stmt.table, 

6830 render_extra_froms, 

6831 from_linter=from_linter, 

6832 **kw, 

6833 ) 

6834 crud_params_struct = crud._get_crud_params( 

6835 self, update_stmt, compile_state, toplevel, **kw 

6836 ) 

6837 crud_params = crud_params_struct.single_params 

6838 

6839 if update_stmt._hints: 

6840 dialect_hints, table_text = self._setup_crud_hints( 

6841 update_stmt, table_text 

6842 ) 

6843 else: 

6844 dialect_hints = None 

6845 

6846 if update_stmt._independent_ctes: 

6847 self._dispatch_independent_ctes(update_stmt, kw) 

6848 

6849 text += table_text 

6850 

6851 text += " SET " 

6852 text += ", ".join( 

6853 expr + "=" + value 

6854 for _, expr, value, _ in cast( 

6855 "List[Tuple[Any, str, str, Any]]", crud_params 

6856 ) 

6857 ) 

6858 

6859 if self.implicit_returning or update_stmt._returning: 

6860 if self.returning_precedes_values: 

6861 text += " " + self.returning_clause( 

6862 update_stmt, 

6863 self.implicit_returning or update_stmt._returning, 

6864 populate_result_map=toplevel, 

6865 ) 

6866 

6867 if extra_froms: 

6868 extra_from_text = self.update_from_clause( 

6869 update_stmt, 

6870 update_stmt.table, 

6871 render_extra_froms, 

6872 dialect_hints, 

6873 from_linter=from_linter, 

6874 **kw, 

6875 ) 

6876 if extra_from_text: 

6877 text += " " + extra_from_text 

6878 

6879 if update_stmt._where_criteria: 

6880 t = self._generate_delimited_and_list( 

6881 update_stmt._where_criteria, from_linter=from_linter, **kw 

6882 ) 

6883 if t: 

6884 text += " WHERE " + t 

6885 

6886 ulc = self.update_post_criteria_clause( 

6887 update_stmt, from_linter=from_linter, **kw 

6888 ) 

6889 if ulc: 

6890 text += " " + ulc 

6891 

6892 if ( 

6893 self.implicit_returning or update_stmt._returning 

6894 ) and not self.returning_precedes_values: 

6895 text += " " + self.returning_clause( 

6896 update_stmt, 

6897 self.implicit_returning or update_stmt._returning, 

6898 populate_result_map=toplevel, 

6899 ) 

6900 

6901 if self.ctes: 

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

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

6904 

6905 if warn_linting: 

6906 assert from_linter is not None 

6907 from_linter.warn(stmt_type="UPDATE") 

6908 

6909 self.stack.pop(-1) 

6910 

6911 return text # type: ignore[no-any-return] 

6912 

6913 def delete_extra_from_clause( 

6914 self, delete_stmt, from_table, extra_froms, from_hints, **kw 

6915 ): 

6916 """Provide a hook to override the generation of an 

6917 DELETE..FROM clause. 

6918 

6919 This can be used to implement DELETE..USING for example. 

6920 

6921 MySQL and MSSQL override this. 

6922 

6923 """ 

6924 raise NotImplementedError( 

6925 "This backend does not support multiple-table " 

6926 "criteria within DELETE" 

6927 ) 

6928 

6929 def delete_table_clause(self, delete_stmt, from_table, extra_froms, **kw): 

6930 return from_table._compiler_dispatch( 

6931 self, asfrom=True, iscrud=True, **kw 

6932 ) 

6933 

6934 def visit_delete(self, delete_stmt, visiting_cte=None, **kw): 

6935 compile_state = delete_stmt._compile_state_factory( 

6936 delete_stmt, self, **kw 

6937 ) 

6938 delete_stmt = compile_state.statement 

6939 

6940 if visiting_cte is not None: 

6941 kw["visiting_cte"] = visiting_cte 

6942 toplevel = False 

6943 else: 

6944 toplevel = not self.stack 

6945 

6946 if toplevel: 

6947 self.isdelete = True 

6948 if not self.dml_compile_state: 

6949 self.dml_compile_state = compile_state 

6950 if not self.compile_state: 

6951 self.compile_state = compile_state 

6952 

6953 if self.linting & COLLECT_CARTESIAN_PRODUCTS: 

6954 from_linter = FromLinter({}, set()) 

6955 warn_linting = self.linting & WARN_LINTING 

6956 if toplevel: 

6957 self.from_linter = from_linter 

6958 else: 

6959 from_linter = None 

6960 warn_linting = False 

6961 

6962 extra_froms = compile_state._extra_froms 

6963 

6964 correlate_froms = {delete_stmt.table}.union(extra_froms) 

6965 self.stack.append( 

6966 { 

6967 "correlate_froms": correlate_froms, 

6968 "asfrom_froms": correlate_froms, 

6969 "selectable": delete_stmt, 

6970 } 

6971 ) 

6972 

6973 text = "DELETE " 

6974 

6975 if delete_stmt._prefixes: 

6976 text += self._generate_prefixes( 

6977 delete_stmt, delete_stmt._prefixes, **kw 

6978 ) 

6979 

6980 text += "FROM " 

6981 

6982 try: 

6983 table_text = self.delete_table_clause( 

6984 delete_stmt, 

6985 delete_stmt.table, 

6986 extra_froms, 

6987 from_linter=from_linter, 

6988 ) 

6989 except TypeError: 

6990 # anticipate 3rd party dialects that don't include **kw 

6991 # TODO: remove in 2.1 

6992 table_text = self.delete_table_clause( 

6993 delete_stmt, delete_stmt.table, extra_froms 

6994 ) 

6995 if from_linter: 

6996 _ = self.process(delete_stmt.table, from_linter=from_linter) 

6997 

6998 crud._get_crud_params(self, delete_stmt, compile_state, toplevel, **kw) 

6999 

7000 if delete_stmt._hints: 

7001 dialect_hints, table_text = self._setup_crud_hints( 

7002 delete_stmt, table_text 

7003 ) 

7004 else: 

7005 dialect_hints = None 

7006 

7007 if delete_stmt._independent_ctes: 

7008 self._dispatch_independent_ctes(delete_stmt, kw) 

7009 

7010 text += table_text 

7011 

7012 if ( 

7013 self.implicit_returning or delete_stmt._returning 

7014 ) and self.returning_precedes_values: 

7015 text += " " + self.returning_clause( 

7016 delete_stmt, 

7017 self.implicit_returning or delete_stmt._returning, 

7018 populate_result_map=toplevel, 

7019 ) 

7020 

7021 if extra_froms: 

7022 extra_from_text = self.delete_extra_from_clause( 

7023 delete_stmt, 

7024 delete_stmt.table, 

7025 extra_froms, 

7026 dialect_hints, 

7027 from_linter=from_linter, 

7028 **kw, 

7029 ) 

7030 if extra_from_text: 

7031 text += " " + extra_from_text 

7032 

7033 if delete_stmt._where_criteria: 

7034 t = self._generate_delimited_and_list( 

7035 delete_stmt._where_criteria, from_linter=from_linter, **kw 

7036 ) 

7037 if t: 

7038 text += " WHERE " + t 

7039 

7040 dlc = self.delete_post_criteria_clause( 

7041 delete_stmt, from_linter=from_linter, **kw 

7042 ) 

7043 if dlc: 

7044 text += " " + dlc 

7045 

7046 if ( 

7047 self.implicit_returning or delete_stmt._returning 

7048 ) and not self.returning_precedes_values: 

7049 text += " " + self.returning_clause( 

7050 delete_stmt, 

7051 self.implicit_returning or delete_stmt._returning, 

7052 populate_result_map=toplevel, 

7053 ) 

7054 

7055 if self.ctes: 

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

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

7058 

7059 if warn_linting: 

7060 assert from_linter is not None 

7061 from_linter.warn(stmt_type="DELETE") 

7062 

7063 self.stack.pop(-1) 

7064 

7065 return text 

7066 

7067 def visit_savepoint(self, savepoint_stmt, **kw): 

7068 return "SAVEPOINT %s" % self.preparer.format_savepoint(savepoint_stmt) 

7069 

7070 def visit_rollback_to_savepoint(self, savepoint_stmt, **kw): 

7071 return "ROLLBACK TO SAVEPOINT %s" % self.preparer.format_savepoint( 

7072 savepoint_stmt 

7073 ) 

7074 

7075 def visit_release_savepoint(self, savepoint_stmt, **kw): 

7076 return "RELEASE SAVEPOINT %s" % self.preparer.format_savepoint( 

7077 savepoint_stmt 

7078 ) 

7079 

7080 

7081class StrSQLCompiler(SQLCompiler): 

7082 """A :class:`.SQLCompiler` subclass which allows a small selection 

7083 of non-standard SQL features to render into a string value. 

7084 

7085 The :class:`.StrSQLCompiler` is invoked whenever a Core expression 

7086 element is directly stringified without calling upon the 

7087 :meth:`_expression.ClauseElement.compile` method. 

7088 It can render a limited set 

7089 of non-standard SQL constructs to assist in basic stringification, 

7090 however for more substantial custom or dialect-specific SQL constructs, 

7091 it will be necessary to make use of 

7092 :meth:`_expression.ClauseElement.compile` 

7093 directly. 

7094 

7095 .. seealso:: 

7096 

7097 :ref:`faq_sql_expression_string` 

7098 

7099 """ 

7100 

7101 def get_select_precolumns(self, select: Select[Any], **kw: Any) -> str: 

7102 return "DISTINCT " if select._distinct else "" 

7103 

7104 def _fallback_column_name(self, column): 

7105 return "<name unknown>" 

7106 

7107 @util.preload_module("sqlalchemy.engine.url") 

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

7109 if element.stringify_dialect != "default": 

7110 url = util.preloaded.engine_url 

7111 dialect = url.URL.create(element.stringify_dialect).get_dialect()() 

7112 

7113 compiler = dialect.statement_compiler( 

7114 dialect, None, _supporting_against=self 

7115 ) 

7116 if not isinstance(compiler, StrSQLCompiler): 

7117 return compiler.process(element, **kw) 

7118 

7119 return super().visit_unsupported_compilation(element, err) 

7120 

7121 def visit_getitem_binary(self, binary, operator, **kw): 

7122 return "%s[%s]" % ( 

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

7124 self.process(binary.right, **kw), 

7125 ) 

7126 

7127 def visit_json_getitem_op_binary(self, binary, operator, **kw): 

7128 return self.visit_getitem_binary(binary, operator, **kw) 

7129 

7130 def visit_json_path_getitem_op_binary(self, binary, operator, **kw): 

7131 return self.visit_getitem_binary(binary, operator, **kw) 

7132 

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

7134 return ( 

7135 f"<next sequence value: {self.preparer.format_sequence(sequence)}>" 

7136 ) 

7137 

7138 def returning_clause( 

7139 self, 

7140 stmt: UpdateBase, 

7141 returning_cols: Sequence[_ColumnsClauseElement], 

7142 *, 

7143 populate_result_map: bool, 

7144 **kw: Any, 

7145 ) -> str: 

7146 columns = [ 

7147 self._label_select_column(None, c, True, False, {}) 

7148 for c in base._select_iterables(returning_cols) 

7149 ] 

7150 return "RETURNING " + ", ".join(columns) 

7151 

7152 def update_from_clause( 

7153 self, update_stmt, from_table, extra_froms, from_hints, **kw 

7154 ): 

7155 kw["asfrom"] = True 

7156 return "FROM " + ", ".join( 

7157 t._compiler_dispatch(self, fromhints=from_hints, **kw) 

7158 for t in extra_froms 

7159 ) 

7160 

7161 def delete_extra_from_clause( 

7162 self, delete_stmt, from_table, extra_froms, from_hints, **kw 

7163 ): 

7164 kw["asfrom"] = True 

7165 return ", " + ", ".join( 

7166 t._compiler_dispatch(self, fromhints=from_hints, **kw) 

7167 for t in extra_froms 

7168 ) 

7169 

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

7171 return "SELECT 1 WHERE 1!=1" 

7172 

7173 def get_from_hint_text(self, table, text): 

7174 return "[%s]" % text 

7175 

7176 def visit_aggregate_strings_func(self, fn, **kw): 

7177 return super().visit_aggregate_strings_func( 

7178 fn, use_function_name=fn.name, **kw 

7179 ) 

7180 

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

7182 return self._generate_generic_binary(binary, " <regexp> ", **kw) 

7183 

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

7185 return self._generate_generic_binary(binary, " <not regexp> ", **kw) 

7186 

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

7188 return "<regexp replace>(%s, %s)" % ( 

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

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

7191 ) 

7192 

7193 def visit_try_cast(self, cast, **kwargs): 

7194 return "TRY_CAST(%s AS %s)" % ( 

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

7196 cast.typeclause._compiler_dispatch(self, **kwargs), 

7197 ) 

7198 

7199 

7200class DDLCompiler(Compiled): 

7201 is_ddl = True 

7202 

7203 if TYPE_CHECKING: 

7204 

7205 def __init__( 

7206 self, 

7207 dialect: Dialect, 

7208 statement: ExecutableDDLElement, 

7209 schema_translate_map: Optional[SchemaTranslateMapType] = ..., 

7210 render_schema_translate: bool = ..., 

7211 compile_kwargs: Mapping[str, Any] = ..., 

7212 ): ... 

7213 

7214 @util.ro_memoized_property 

7215 def sql_compiler(self) -> SQLCompiler: 

7216 return self.dialect.statement_compiler( 

7217 self.dialect, None, schema_translate_map=self.schema_translate_map 

7218 ) 

7219 

7220 @util.memoized_property 

7221 def type_compiler(self): 

7222 return self.dialect.type_compiler_instance 

7223 

7224 def construct_params( 

7225 self, 

7226 params: Optional[_CoreSingleExecuteParams] = None, 

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

7228 escape_names: bool = True, 

7229 ) -> Optional[_MutableCoreSingleExecuteParams]: 

7230 return None 

7231 

7232 def visit_ddl(self, ddl, **kwargs): 

7233 # table events can substitute table and schema name 

7234 context = ddl.context 

7235 if isinstance(ddl.target, schema.Table): 

7236 context = context.copy() 

7237 

7238 preparer = self.preparer 

7239 path = preparer.format_table_seq(ddl.target) 

7240 if len(path) == 1: 

7241 table, sch = path[0], "" 

7242 else: 

7243 table, sch = path[-1], path[0] 

7244 

7245 context.setdefault("table", table) 

7246 context.setdefault("schema", sch) 

7247 context.setdefault("fullname", preparer.format_table(ddl.target)) 

7248 

7249 return self.sql_compiler.post_process_text(ddl.statement % context) 

7250 

7251 def visit_create_schema(self, create, **kw): 

7252 text = "CREATE SCHEMA " 

7253 if create.if_not_exists: 

7254 text += "IF NOT EXISTS " 

7255 return text + self.preparer.format_schema(create.element) 

7256 

7257 def visit_drop_schema(self, drop, **kw): 

7258 text = "DROP SCHEMA " 

7259 if drop.if_exists: 

7260 text += "IF EXISTS " 

7261 text += self.preparer.format_schema(drop.element) 

7262 if drop.cascade: 

7263 text += " CASCADE" 

7264 return text 

7265 

7266 def visit_create_table(self, create, **kw): 

7267 table = create.element 

7268 preparer = self.preparer 

7269 

7270 text = "\nCREATE " 

7271 if table._prefixes: 

7272 text += " ".join(table._prefixes) + " " 

7273 

7274 text += "TABLE " 

7275 if create.if_not_exists: 

7276 text += "IF NOT EXISTS " 

7277 

7278 text += preparer.format_table(table) + " " 

7279 

7280 create_table_suffix = self.create_table_suffix(table) 

7281 if create_table_suffix: 

7282 text += create_table_suffix + " " 

7283 

7284 text += "(" 

7285 

7286 separator = "\n" 

7287 

7288 # if only one primary key, specify it along with the column 

7289 first_pk = False 

7290 for create_column in create.columns: 

7291 column = create_column.element 

7292 try: 

7293 processed = self.process( 

7294 create_column, first_pk=column.primary_key and not first_pk 

7295 ) 

7296 if processed is not None: 

7297 text += separator 

7298 separator = ", \n" 

7299 text += "\t" + processed 

7300 if column.primary_key: 

7301 first_pk = True 

7302 except exc.CompileError as ce: 

7303 raise exc.CompileError( 

7304 "(in table '%s', column '%s'): %s" 

7305 % (table.description, column.name, ce.args[0]) 

7306 ) from ce 

7307 

7308 const = self.create_table_constraints( 

7309 table, 

7310 _include_foreign_key_constraints=create.include_foreign_key_constraints, # noqa 

7311 ) 

7312 if const: 

7313 text += separator + "\t" + const 

7314 

7315 text += "\n)%s\n\n" % self.post_create_table(table) 

7316 return text 

7317 

7318 def visit_create_view(self, element: CreateView, **kw: Any) -> str: 

7319 return self._generate_table_select(element, "view", **kw) 

7320 

7321 def visit_create_table_as(self, element: CreateTableAs, **kw: Any) -> str: 

7322 return self._generate_table_select(element, "create_table_as", **kw) 

7323 

7324 def create_table_select_suffixes( 

7325 self, 

7326 element: _TableViaSelect, 

7327 type_: str, 

7328 **kw: Any, 

7329 ) -> str: 

7330 return "" 

7331 

7332 def _generate_table_select( 

7333 self, 

7334 element: _TableViaSelect, 

7335 type_: str, 

7336 if_not_exists: Optional[bool] = None, 

7337 **kw: Any, 

7338 ) -> str: 

7339 prep = self.preparer 

7340 

7341 inner_kw = dict(kw) 

7342 inner_kw["literal_binds"] = True 

7343 select_sql = self.sql_compiler.process(element.selectable, **inner_kw) 

7344 

7345 # Use if_not_exists parameter if provided, otherwise use element's 

7346 use_if_not_exists = ( 

7347 if_not_exists 

7348 if if_not_exists is not None 

7349 else element.if_not_exists 

7350 ) 

7351 

7352 parts: List[Optional[str]] = [ 

7353 "CREATE", 

7354 "OR REPLACE" if getattr(element, "or_replace", False) else None, 

7355 "TEMPORARY" if element.temporary else None, 

7356 ( 

7357 "MATERIALIZED VIEW" 

7358 if type_ == "view" and getattr(element, "materialized", False) 

7359 else "TABLE" if type_ == "create_table_as" else "VIEW" 

7360 ), 

7361 "IF NOT EXISTS" if use_if_not_exists else None, 

7362 prep.format_table(element.table), 

7363 ] 

7364 suffixes = self.create_table_select_suffixes(element, type_, **kw) 

7365 if suffixes: 

7366 parts.append(suffixes) 

7367 parts += ["AS", select_sql] 

7368 return " ".join(p for p in parts if p) 

7369 

7370 def visit_create_column(self, create, first_pk=False, **kw): 

7371 column = create.element 

7372 

7373 if column.system: 

7374 return None 

7375 

7376 text = self.get_column_specification(column, first_pk=first_pk) 

7377 const = " ".join( 

7378 self.process(constraint) for constraint in column.constraints 

7379 ) 

7380 if const: 

7381 text += " " + const 

7382 

7383 return text 

7384 

7385 def create_table_constraints( 

7386 self, table, _include_foreign_key_constraints=None, **kw 

7387 ): 

7388 # On some DB order is significant: visit PK first, then the 

7389 # other constraints (engine.ReflectionTest.testbasic failed on FB2) 

7390 constraints = [] 

7391 if table.primary_key: 

7392 constraints.append(table.primary_key) 

7393 

7394 all_fkcs = table.foreign_key_constraints 

7395 if _include_foreign_key_constraints is not None: 

7396 omit_fkcs = all_fkcs.difference(_include_foreign_key_constraints) 

7397 else: 

7398 omit_fkcs = set() 

7399 

7400 constraints.extend( 

7401 [ 

7402 c 

7403 for c in table._sorted_constraints 

7404 if c is not table.primary_key and c not in omit_fkcs 

7405 ] 

7406 ) 

7407 

7408 return ", \n\t".join( 

7409 p 

7410 for p in ( 

7411 self.process(constraint) 

7412 for constraint in constraints 

7413 if (constraint._should_create_for_compiler(self)) 

7414 and ( 

7415 not self.dialect.supports_alter 

7416 or not getattr(constraint, "use_alter", False) 

7417 ) 

7418 ) 

7419 if p is not None 

7420 ) 

7421 

7422 def visit_drop_table(self, drop, **kw): 

7423 text = "\nDROP TABLE " 

7424 if drop.if_exists: 

7425 text += "IF EXISTS " 

7426 return text + self.preparer.format_table(drop.element) 

7427 

7428 def visit_drop_view(self, drop, **kw): 

7429 text = "\nDROP " 

7430 if drop.materialized: 

7431 text += "MATERIALIZED VIEW " 

7432 else: 

7433 text += "VIEW " 

7434 if drop.if_exists: 

7435 text += "IF EXISTS " 

7436 return text + self.preparer.format_table(drop.element) 

7437 

7438 def _verify_index_table(self, index: Index) -> None: 

7439 if index.table is None: 

7440 raise exc.CompileError( 

7441 "Index '%s' is not associated with any table." % index.name 

7442 ) 

7443 

7444 def visit_create_index( 

7445 self, create, include_schema=False, include_table_schema=True, **kw 

7446 ): 

7447 index = create.element 

7448 self._verify_index_table(index) 

7449 preparer = self.preparer 

7450 text = "CREATE " 

7451 if index.unique: 

7452 text += "UNIQUE " 

7453 if index.name is None: 

7454 raise exc.CompileError( 

7455 "CREATE INDEX requires that the index have a name" 

7456 ) 

7457 

7458 text += "INDEX " 

7459 if create.if_not_exists: 

7460 text += "IF NOT EXISTS " 

7461 

7462 text += "%s ON %s (%s)" % ( 

7463 self._prepared_index_name(index, include_schema=include_schema), 

7464 preparer.format_table( 

7465 index.table, use_schema=include_table_schema 

7466 ), 

7467 ", ".join( 

7468 self.sql_compiler.process( 

7469 expr, include_table=False, literal_binds=True 

7470 ) 

7471 for expr in index.expressions 

7472 ), 

7473 ) 

7474 return text 

7475 

7476 def visit_drop_index(self, drop, **kw): 

7477 index = drop.element 

7478 

7479 if index.name is None: 

7480 raise exc.CompileError( 

7481 "DROP INDEX requires that the index have a name" 

7482 ) 

7483 text = "\nDROP INDEX " 

7484 if drop.if_exists: 

7485 text += "IF EXISTS " 

7486 

7487 return text + self._prepared_index_name(index, include_schema=True) 

7488 

7489 def _prepared_index_name( 

7490 self, index: Index, include_schema: bool = False 

7491 ) -> str: 

7492 if index.table is not None: 

7493 effective_schema = self.preparer.schema_for_object(index.table) 

7494 else: 

7495 effective_schema = None 

7496 if include_schema and effective_schema: 

7497 schema_name = self.preparer.quote_schema(effective_schema) 

7498 else: 

7499 schema_name = None 

7500 

7501 index_name: str = self.preparer.format_index(index) 

7502 

7503 if schema_name: 

7504 index_name = schema_name + "." + index_name 

7505 return index_name 

7506 

7507 def visit_add_constraint(self, create, **kw): 

7508 return "ALTER TABLE %s ADD %s" % ( 

7509 self.preparer.format_table(create.element.table), 

7510 self.process(create.element), 

7511 ) 

7512 

7513 def visit_set_table_comment(self, create, **kw): 

7514 return "COMMENT ON TABLE %s IS %s" % ( 

7515 self.preparer.format_table(create.element), 

7516 self.sql_compiler.render_literal_value( 

7517 create.element.comment, sqltypes.String() 

7518 ), 

7519 ) 

7520 

7521 def visit_drop_table_comment(self, drop, **kw): 

7522 return "COMMENT ON TABLE %s IS NULL" % self.preparer.format_table( 

7523 drop.element 

7524 ) 

7525 

7526 def visit_set_column_comment(self, create, **kw): 

7527 return "COMMENT ON COLUMN %s IS %s" % ( 

7528 self.preparer.format_column( 

7529 create.element, use_table=True, use_schema=True 

7530 ), 

7531 self.sql_compiler.render_literal_value( 

7532 create.element.comment, sqltypes.String() 

7533 ), 

7534 ) 

7535 

7536 def visit_drop_column_comment(self, drop, **kw): 

7537 return "COMMENT ON COLUMN %s IS NULL" % self.preparer.format_column( 

7538 drop.element, use_table=True 

7539 ) 

7540 

7541 def visit_set_constraint_comment(self, create, **kw): 

7542 raise exc.UnsupportedCompilationError(self, type(create)) 

7543 

7544 def visit_drop_constraint_comment(self, drop, **kw): 

7545 raise exc.UnsupportedCompilationError(self, type(drop)) 

7546 

7547 def get_identity_options(self, identity_options: IdentityOptions) -> str: 

7548 text = [] 

7549 if identity_options.increment is not None: 

7550 text.append("INCREMENT BY %d" % identity_options.increment) 

7551 if identity_options.start is not None: 

7552 text.append("START WITH %d" % identity_options.start) 

7553 if identity_options.minvalue is not None: 

7554 text.append("MINVALUE %d" % identity_options.minvalue) 

7555 if identity_options.maxvalue is not None: 

7556 text.append("MAXVALUE %d" % identity_options.maxvalue) 

7557 if identity_options.nominvalue is not None: 

7558 text.append("NO MINVALUE") 

7559 if identity_options.nomaxvalue is not None: 

7560 text.append("NO MAXVALUE") 

7561 if identity_options.cache is not None: 

7562 text.append("CACHE %d" % identity_options.cache) 

7563 if identity_options.cycle is not None: 

7564 text.append("CYCLE" if identity_options.cycle else "NO CYCLE") 

7565 return " ".join(text) 

7566 

7567 def visit_create_sequence(self, create, prefix=None, **kw): 

7568 text = "CREATE SEQUENCE " 

7569 if create.if_not_exists: 

7570 text += "IF NOT EXISTS " 

7571 text += self.preparer.format_sequence(create.element) 

7572 

7573 if prefix: 

7574 text += prefix 

7575 options = self.get_identity_options(create.element) 

7576 if options: 

7577 text += " " + options 

7578 return text 

7579 

7580 def visit_drop_sequence(self, drop, **kw): 

7581 text = "DROP SEQUENCE " 

7582 if drop.if_exists: 

7583 text += "IF EXISTS " 

7584 return text + self.preparer.format_sequence(drop.element) 

7585 

7586 def visit_drop_constraint(self, drop, **kw): 

7587 constraint = drop.element 

7588 if constraint.name is not None: 

7589 formatted_name = self.preparer.format_constraint(constraint) 

7590 else: 

7591 formatted_name = None 

7592 

7593 if formatted_name is None: 

7594 raise exc.CompileError( 

7595 "Can't emit DROP CONSTRAINT for constraint %r; " 

7596 "it has no name" % drop.element 

7597 ) 

7598 return "ALTER TABLE %s DROP CONSTRAINT %s%s%s" % ( 

7599 self.preparer.format_table(drop.element.table), 

7600 "IF EXISTS " if drop.if_exists else "", 

7601 formatted_name, 

7602 " CASCADE" if drop.cascade else "", 

7603 ) 

7604 

7605 def get_column_specification( 

7606 self, column: Column[Any], **kwargs: Any 

7607 ) -> str: 

7608 colspec = ( 

7609 self.preparer.format_column(column) 

7610 + " " 

7611 + self.dialect.type_compiler_instance.process( 

7612 column.type, type_expression=column 

7613 ) 

7614 ) 

7615 default = self.get_column_default_string(column) 

7616 if default is not None: 

7617 colspec += " DEFAULT " + default 

7618 

7619 if column.computed is not None: 

7620 colspec += " " + self.process(column.computed) 

7621 

7622 if ( 

7623 column.identity is not None 

7624 and self.dialect.supports_identity_columns 

7625 ): 

7626 colspec += " " + self.process(column.identity) 

7627 

7628 if not column.nullable and ( 

7629 not column.identity or not self.dialect.supports_identity_columns 

7630 ): 

7631 colspec += " NOT NULL" 

7632 return colspec 

7633 

7634 def create_table_suffix(self, table: Table) -> str: 

7635 return "" 

7636 

7637 def post_create_table(self, table: Table) -> str: 

7638 return "" 

7639 

7640 def get_column_default_string(self, column: Column[Any]) -> Optional[str]: 

7641 if isinstance(column.server_default, schema.DefaultClause): 

7642 return self.render_default_string(column.server_default.arg) 

7643 else: 

7644 return None 

7645 

7646 def render_default_string(self, default: Union[Visitable, str]) -> str: 

7647 if isinstance(default, str): 

7648 return self.sql_compiler.render_literal_value( 

7649 default, sqltypes.STRINGTYPE 

7650 ) 

7651 else: 

7652 return self.sql_compiler.process(default, literal_binds=True) 

7653 

7654 def visit_table_or_column_check_constraint(self, constraint, **kw): 

7655 if constraint.is_column_level: 

7656 return self.visit_column_check_constraint(constraint) 

7657 else: 

7658 return self.visit_check_constraint(constraint) 

7659 

7660 def visit_check_constraint(self, constraint, **kw): 

7661 text = self.define_constraint_preamble(constraint, **kw) 

7662 text += self.define_check_body(constraint, **kw) 

7663 text += self.define_constraint_deferrability(constraint) 

7664 return text 

7665 

7666 def visit_column_check_constraint(self, constraint, **kw): 

7667 text = self.define_constraint_preamble(constraint, **kw) 

7668 text += self.define_check_body(constraint, **kw) 

7669 text += self.define_constraint_deferrability(constraint) 

7670 return text 

7671 

7672 def visit_primary_key_constraint( 

7673 self, constraint: PrimaryKeyConstraint, **kw: Any 

7674 ) -> str: 

7675 if len(constraint) == 0: 

7676 return "" 

7677 text = self.define_constraint_preamble(constraint, **kw) 

7678 text += self.define_primary_key_body(constraint, **kw) 

7679 text += self.define_constraint_deferrability(constraint) 

7680 return text 

7681 

7682 def visit_foreign_key_constraint( 

7683 self, constraint: ForeignKeyConstraint, **kw: Any 

7684 ) -> str: 

7685 text = self.define_constraint_preamble(constraint, **kw) 

7686 text += self.define_foreign_key_body(constraint, **kw) 

7687 text += self.define_constraint_match(constraint) 

7688 text += self.define_constraint_cascades(constraint) 

7689 text += self.define_constraint_deferrability(constraint) 

7690 return text 

7691 

7692 def define_constraint_remote_table(self, constraint, table, preparer): 

7693 """Format the remote table clause of a CREATE CONSTRAINT clause.""" 

7694 

7695 return preparer.format_table(table) 

7696 

7697 def visit_unique_constraint( 

7698 self, constraint: UniqueConstraint, **kw: Any 

7699 ) -> str: 

7700 if len(constraint) == 0: 

7701 return "" 

7702 text = self.define_constraint_preamble(constraint, **kw) 

7703 text += self.define_unique_body(constraint, **kw) 

7704 text += self.define_constraint_deferrability(constraint) 

7705 return text 

7706 

7707 def define_constraint_preamble( 

7708 self, constraint: Constraint, **kw: Any 

7709 ) -> str: 

7710 text = "" 

7711 if constraint.name is not None: 

7712 formatted_name = self.preparer.format_constraint(constraint) 

7713 if formatted_name is not None: 

7714 text += "CONSTRAINT %s " % formatted_name 

7715 return text 

7716 

7717 def define_primary_key_body( 

7718 self, constraint: PrimaryKeyConstraint, **kw: Any 

7719 ) -> str: 

7720 text = "" 

7721 text += "PRIMARY KEY " 

7722 text += "(%s)" % ", ".join( 

7723 self.preparer.quote(c.name) 

7724 for c in ( 

7725 constraint.columns_autoinc_first 

7726 if constraint._implicit_generated 

7727 else constraint.columns 

7728 ) 

7729 ) 

7730 return text 

7731 

7732 def define_foreign_key_body( 

7733 self, constraint: ForeignKeyConstraint, **kw: Any 

7734 ) -> str: 

7735 preparer = self.preparer 

7736 remote_table = list(constraint.elements)[0].column.table 

7737 text = "FOREIGN KEY(%s) REFERENCES %s (%s)" % ( 

7738 ", ".join( 

7739 preparer.quote(f.parent.name) for f in constraint.elements 

7740 ), 

7741 self.define_constraint_remote_table( 

7742 constraint, remote_table, preparer 

7743 ), 

7744 ", ".join( 

7745 preparer.quote(f.column.name) for f in constraint.elements 

7746 ), 

7747 ) 

7748 return text 

7749 

7750 def define_unique_body( 

7751 self, constraint: UniqueConstraint, **kw: Any 

7752 ) -> str: 

7753 text = "UNIQUE %s(%s)" % ( 

7754 self.define_unique_constraint_distinct(constraint, **kw), 

7755 ", ".join(self.preparer.quote(c.name) for c in constraint), 

7756 ) 

7757 return text 

7758 

7759 def define_check_body(self, constraint: CheckConstraint, **kw: Any) -> str: 

7760 text = "CHECK (%s)" % self.sql_compiler.process( 

7761 constraint.sqltext, include_table=False, literal_binds=True 

7762 ) 

7763 return text 

7764 

7765 def define_unique_constraint_distinct( 

7766 self, constraint: UniqueConstraint, **kw: Any 

7767 ) -> str: 

7768 return "" 

7769 

7770 def define_constraint_cascades( 

7771 self, constraint: ForeignKeyConstraint 

7772 ) -> str: 

7773 text = "" 

7774 if constraint.ondelete is not None: 

7775 text += self.define_constraint_ondelete_cascade(constraint) 

7776 

7777 if constraint.onupdate is not None: 

7778 text += self.define_constraint_onupdate_cascade(constraint) 

7779 return text 

7780 

7781 def define_constraint_ondelete_cascade( 

7782 self, constraint: ForeignKeyConstraint 

7783 ) -> str: 

7784 return " ON DELETE %s" % self.preparer.validate_sql_phrase( 

7785 constraint.ondelete, FK_ON_DELETE 

7786 ) 

7787 

7788 def define_constraint_onupdate_cascade( 

7789 self, constraint: ForeignKeyConstraint 

7790 ) -> str: 

7791 return " ON UPDATE %s" % self.preparer.validate_sql_phrase( 

7792 constraint.onupdate, FK_ON_UPDATE 

7793 ) 

7794 

7795 def define_constraint_deferrability(self, constraint: Constraint) -> str: 

7796 text = "" 

7797 if constraint.deferrable is not None: 

7798 if constraint.deferrable: 

7799 text += " DEFERRABLE" 

7800 else: 

7801 text += " NOT DEFERRABLE" 

7802 if constraint.initially is not None: 

7803 text += " INITIALLY %s" % self.preparer.validate_sql_phrase( 

7804 constraint.initially, FK_INITIALLY 

7805 ) 

7806 return text 

7807 

7808 def define_constraint_match(self, constraint: ForeignKeyConstraint) -> str: 

7809 text = "" 

7810 if constraint.match is not None: 

7811 text += " MATCH %s" % constraint.match 

7812 return text 

7813 

7814 def visit_computed_column(self, generated, **kw): 

7815 text = "GENERATED ALWAYS AS (%s)" % self.sql_compiler.process( 

7816 generated.sqltext, include_table=False, literal_binds=True 

7817 ) 

7818 if generated.persisted is True: 

7819 text += " STORED" 

7820 elif generated.persisted is False: 

7821 text += " VIRTUAL" 

7822 return text 

7823 

7824 def visit_identity_column(self, identity, **kw): 

7825 text = "GENERATED %s AS IDENTITY" % ( 

7826 "ALWAYS" if identity.always else "BY DEFAULT", 

7827 ) 

7828 options = self.get_identity_options(identity) 

7829 if options: 

7830 text += " (%s)" % options 

7831 return text 

7832 

7833 

7834class GenericTypeCompiler(TypeCompiler): 

7835 def visit_FLOAT(self, type_: sqltypes.Float[Any], **kw: Any) -> str: 

7836 return "FLOAT" 

7837 

7838 def visit_DOUBLE(self, type_: sqltypes.Double[Any], **kw: Any) -> str: 

7839 return "DOUBLE" 

7840 

7841 def visit_DOUBLE_PRECISION( 

7842 self, type_: sqltypes.DOUBLE_PRECISION[Any], **kw: Any 

7843 ) -> str: 

7844 return "DOUBLE PRECISION" 

7845 

7846 def visit_REAL(self, type_: sqltypes.REAL[Any], **kw: Any) -> str: 

7847 return "REAL" 

7848 

7849 def visit_NUMERIC(self, type_: sqltypes.Numeric[Any], **kw: Any) -> str: 

7850 if type_.precision is None: 

7851 return "NUMERIC" 

7852 elif type_.scale is None: 

7853 return "NUMERIC(%(precision)s)" % {"precision": type_.precision} 

7854 else: 

7855 return "NUMERIC(%(precision)s, %(scale)s)" % { 

7856 "precision": type_.precision, 

7857 "scale": type_.scale, 

7858 } 

7859 

7860 def visit_DECIMAL(self, type_: sqltypes.DECIMAL[Any], **kw: Any) -> str: 

7861 if type_.precision is None: 

7862 return "DECIMAL" 

7863 elif type_.scale is None: 

7864 return "DECIMAL(%(precision)s)" % {"precision": type_.precision} 

7865 else: 

7866 return "DECIMAL(%(precision)s, %(scale)s)" % { 

7867 "precision": type_.precision, 

7868 "scale": type_.scale, 

7869 } 

7870 

7871 def visit_INTEGER(self, type_: sqltypes.Integer, **kw: Any) -> str: 

7872 return "INTEGER" 

7873 

7874 def visit_SMALLINT(self, type_: sqltypes.SmallInteger, **kw: Any) -> str: 

7875 return "SMALLINT" 

7876 

7877 def visit_BIGINT(self, type_: sqltypes.BigInteger, **kw: Any) -> str: 

7878 return "BIGINT" 

7879 

7880 def visit_TIMESTAMP(self, type_: sqltypes.TIMESTAMP, **kw: Any) -> str: 

7881 return "TIMESTAMP" 

7882 

7883 def visit_DATETIME(self, type_: sqltypes.DateTime, **kw: Any) -> str: 

7884 return "DATETIME" 

7885 

7886 def visit_DATE(self, type_: sqltypes.Date, **kw: Any) -> str: 

7887 return "DATE" 

7888 

7889 def visit_TIME(self, type_: sqltypes.Time, **kw: Any) -> str: 

7890 return "TIME" 

7891 

7892 def visit_CLOB(self, type_: sqltypes.CLOB, **kw: Any) -> str: 

7893 return "CLOB" 

7894 

7895 def visit_NCLOB(self, type_: sqltypes.Text, **kw: Any) -> str: 

7896 return "NCLOB" 

7897 

7898 def _render_string_type( 

7899 self, 

7900 name: str, 

7901 length: Optional[int], 

7902 collation: Optional[str], 

7903 collation_schema: Optional[str] = None, 

7904 identifier_preparer: Optional[IdentifierPreparer] = None, 

7905 **kw: Any, 

7906 ) -> str: 

7907 text = name 

7908 if length: 

7909 text += f"({length})" 

7910 if collation: 

7911 if identifier_preparer is None: 

7912 identifier_preparer = self.dialect.identifier_preparer 

7913 text += " COLLATE " + identifier_preparer.format_collation( 

7914 collation, collation_schema 

7915 ) 

7916 return text 

7917 

7918 def visit_CHAR(self, type_: sqltypes.CHAR, **kw: Any) -> str: 

7919 return self._render_string_type( 

7920 "CHAR", 

7921 type_.length, 

7922 type_.collation, 

7923 type_.collation_schema, 

7924 **kw, 

7925 ) 

7926 

7927 def visit_NCHAR(self, type_: sqltypes.NCHAR, **kw: Any) -> str: 

7928 return self._render_string_type( 

7929 "NCHAR", 

7930 type_.length, 

7931 type_.collation, 

7932 type_.collation_schema, 

7933 **kw, 

7934 ) 

7935 

7936 def visit_VARCHAR(self, type_: sqltypes.String, **kw: Any) -> str: 

7937 return self._render_string_type( 

7938 "VARCHAR", 

7939 type_.length, 

7940 type_.collation, 

7941 type_.collation_schema, 

7942 **kw, 

7943 ) 

7944 

7945 def visit_NVARCHAR(self, type_: sqltypes.NVARCHAR, **kw: Any) -> str: 

7946 return self._render_string_type( 

7947 "NVARCHAR", 

7948 type_.length, 

7949 type_.collation, 

7950 type_.collation_schema, 

7951 **kw, 

7952 ) 

7953 

7954 def visit_TEXT(self, type_: sqltypes.Text, **kw: Any) -> str: 

7955 return self._render_string_type( 

7956 "TEXT", 

7957 type_.length, 

7958 type_.collation, 

7959 type_.collation_schema, 

7960 **kw, 

7961 ) 

7962 

7963 def visit_UUID(self, type_: sqltypes.Uuid[Any], **kw: Any) -> str: 

7964 return "UUID" 

7965 

7966 def visit_BLOB(self, type_: sqltypes.LargeBinary, **kw: Any) -> str: 

7967 return "BLOB" 

7968 

7969 def visit_BINARY(self, type_: sqltypes.BINARY, **kw: Any) -> str: 

7970 return "BINARY" + (type_.length and "(%d)" % type_.length or "") 

7971 

7972 def visit_VARBINARY(self, type_: sqltypes.VARBINARY, **kw: Any) -> str: 

7973 return "VARBINARY" + (type_.length and "(%d)" % type_.length or "") 

7974 

7975 def visit_BOOLEAN(self, type_: sqltypes.Boolean, **kw: Any) -> str: 

7976 return "BOOLEAN" 

7977 

7978 def visit_uuid(self, type_: sqltypes.Uuid[Any], **kw: Any) -> str: 

7979 if not type_.native_uuid or not self.dialect.supports_native_uuid: 

7980 return self._render_string_type( 

7981 "CHAR", 

7982 length=32, 

7983 collation=None, 

7984 collation_schema=None, 

7985 **kw, 

7986 ) 

7987 else: 

7988 return self.visit_UUID(type_, **kw) 

7989 

7990 def visit_large_binary( 

7991 self, type_: sqltypes.LargeBinary, **kw: Any 

7992 ) -> str: 

7993 return self.visit_BLOB(type_, **kw) 

7994 

7995 def visit_boolean(self, type_: sqltypes.Boolean, **kw: Any) -> str: 

7996 return self.visit_BOOLEAN(type_, **kw) 

7997 

7998 def visit_time(self, type_: sqltypes.Time, **kw: Any) -> str: 

7999 return self.visit_TIME(type_, **kw) 

8000 

8001 def visit_datetime(self, type_: sqltypes.DateTime, **kw: Any) -> str: 

8002 return self.visit_DATETIME(type_, **kw) 

8003 

8004 def visit_date(self, type_: sqltypes.Date, **kw: Any) -> str: 

8005 return self.visit_DATE(type_, **kw) 

8006 

8007 def visit_big_integer(self, type_: sqltypes.BigInteger, **kw: Any) -> str: 

8008 return self.visit_BIGINT(type_, **kw) 

8009 

8010 def visit_small_integer( 

8011 self, type_: sqltypes.SmallInteger, **kw: Any 

8012 ) -> str: 

8013 return self.visit_SMALLINT(type_, **kw) 

8014 

8015 def visit_integer(self, type_: sqltypes.Integer, **kw: Any) -> str: 

8016 return self.visit_INTEGER(type_, **kw) 

8017 

8018 def visit_real(self, type_: sqltypes.REAL[Any], **kw: Any) -> str: 

8019 return self.visit_REAL(type_, **kw) 

8020 

8021 def visit_float(self, type_: sqltypes.Float[Any], **kw: Any) -> str: 

8022 return self.visit_FLOAT(type_, **kw) 

8023 

8024 def visit_double(self, type_: sqltypes.Double[Any], **kw: Any) -> str: 

8025 return self.visit_DOUBLE(type_, **kw) 

8026 

8027 def visit_numeric(self, type_: sqltypes.Numeric[Any], **kw: Any) -> str: 

8028 return self.visit_NUMERIC(type_, **kw) 

8029 

8030 def visit_string(self, type_: sqltypes.String, **kw: Any) -> str: 

8031 return self.visit_VARCHAR(type_, **kw) 

8032 

8033 def visit_unicode(self, type_: sqltypes.Unicode, **kw: Any) -> str: 

8034 return self.visit_VARCHAR(type_, **kw) 

8035 

8036 def visit_text(self, type_: sqltypes.Text, **kw: Any) -> str: 

8037 return self.visit_TEXT(type_, **kw) 

8038 

8039 def visit_unicode_text( 

8040 self, type_: sqltypes.UnicodeText, **kw: Any 

8041 ) -> str: 

8042 return self.visit_TEXT(type_, **kw) 

8043 

8044 def visit_enum(self, type_: sqltypes.Enum, **kw: Any) -> str: 

8045 return self.visit_VARCHAR(type_, **kw) 

8046 

8047 def visit_null(self, type_, **kw): 

8048 raise exc.CompileError( 

8049 "Can't generate DDL for %r; " 

8050 "did you forget to specify a " 

8051 "type on this Column?" % type_ 

8052 ) 

8053 

8054 def visit_type_decorator( 

8055 self, type_: TypeDecorator[Any], **kw: Any 

8056 ) -> str: 

8057 return self.process(type_.type_engine(self.dialect), **kw) 

8058 

8059 def visit_user_defined( 

8060 self, type_: UserDefinedType[Any], **kw: Any 

8061 ) -> str: 

8062 return type_.get_col_spec(**kw) 

8063 

8064 

8065class StrSQLTypeCompiler(GenericTypeCompiler): 

8066 def process(self, type_, **kw): 

8067 try: 

8068 _compiler_dispatch = type_._compiler_dispatch 

8069 except AttributeError: 

8070 return self._visit_unknown(type_, **kw) 

8071 else: 

8072 return _compiler_dispatch(self, **kw) 

8073 

8074 def __getattr__(self, key): 

8075 if key.startswith("visit_"): 

8076 return self._visit_unknown 

8077 else: 

8078 raise AttributeError(key) 

8079 

8080 def _visit_unknown(self, type_, **kw): 

8081 if type_.__class__.__name__ == type_.__class__.__name__.upper(): 

8082 return type_.__class__.__name__ 

8083 else: 

8084 return repr(type_) 

8085 

8086 def visit_null(self, type_, **kw): 

8087 return "NULL" 

8088 

8089 def visit_user_defined(self, type_, **kw): 

8090 try: 

8091 get_col_spec = type_.get_col_spec 

8092 except AttributeError: 

8093 return repr(type_) 

8094 else: 

8095 return get_col_spec(**kw) 

8096 

8097 

8098class _SchemaForObjectCallable(Protocol): 

8099 def __call__(self, obj: Any, /) -> str: ... 

8100 

8101 

8102class _BindNameForColProtocol(Protocol): 

8103 def __call__(self, col: ColumnClause[Any]) -> str: ... 

8104 

8105 

8106class IdentifierPreparer: 

8107 """Handle quoting and case-folding of identifiers based on options.""" 

8108 

8109 reserved_words = RESERVED_WORDS 

8110 

8111 legal_characters = LEGAL_CHARACTERS 

8112 

8113 illegal_initial_characters = ILLEGAL_INITIAL_CHARACTERS 

8114 

8115 initial_quote: str 

8116 

8117 final_quote: str 

8118 

8119 _strings: MutableMapping[str, str] 

8120 

8121 schema_for_object: _SchemaForObjectCallable = operator.attrgetter("schema") 

8122 """Return the .schema attribute for an object. 

8123 

8124 For the default IdentifierPreparer, the schema for an object is always 

8125 the value of the ".schema" attribute. if the preparer is replaced 

8126 with one that has a non-empty schema_translate_map, the value of the 

8127 ".schema" attribute is rendered a symbol that will be converted to a 

8128 real schema name from the mapping post-compile. 

8129 

8130 """ 

8131 

8132 _includes_none_schema_translate: bool = False 

8133 

8134 def __init__( 

8135 self, 

8136 dialect: Dialect, 

8137 initial_quote: str = '"', 

8138 final_quote: Optional[str] = None, 

8139 escape_quote: str = '"', 

8140 quote_case_sensitive_collations: bool = True, 

8141 omit_schema: bool = False, 

8142 ): 

8143 """Construct a new ``IdentifierPreparer`` object. 

8144 

8145 initial_quote 

8146 Character that begins a delimited identifier. 

8147 

8148 final_quote 

8149 Character that ends a delimited identifier. Defaults to 

8150 `initial_quote`. 

8151 

8152 omit_schema 

8153 Prevent prepending schema name. Useful for databases that do 

8154 not support schemae. 

8155 """ 

8156 

8157 self.dialect = dialect 

8158 self.initial_quote = initial_quote 

8159 self.final_quote = final_quote or self.initial_quote 

8160 self.escape_quote = escape_quote 

8161 self.escape_to_quote = self.escape_quote * 2 

8162 self.omit_schema = omit_schema 

8163 self.quote_case_sensitive_collations = quote_case_sensitive_collations 

8164 self._strings = {} 

8165 self._double_percents = self.dialect.paramstyle in ( 

8166 "format", 

8167 "pyformat", 

8168 ) 

8169 

8170 def _with_schema_translate(self, schema_translate_map): 

8171 prep = self.__class__.__new__(self.__class__) 

8172 prep.__dict__.update(self.__dict__) 

8173 

8174 includes_none = None in schema_translate_map 

8175 

8176 def symbol_getter(obj): 

8177 name = obj.schema 

8178 if obj._use_schema_map and (name is not None or includes_none): 

8179 if name is not None and ("[" in name or "]" in name): 

8180 raise exc.CompileError( 

8181 "Square bracket characters ([]) not supported " 

8182 "in schema translate name '%s'" % name 

8183 ) 

8184 return quoted_name( 

8185 "__[SCHEMA_%s]" % (name or "_none"), quote=False 

8186 ) 

8187 else: 

8188 return obj.schema 

8189 

8190 prep.schema_for_object = symbol_getter 

8191 prep._includes_none_schema_translate = includes_none 

8192 return prep 

8193 

8194 def _render_schema_translates( 

8195 self, statement: str, schema_translate_map: SchemaTranslateMapType 

8196 ) -> str: 

8197 d = schema_translate_map 

8198 if None in d: 

8199 if not self._includes_none_schema_translate: 

8200 raise exc.InvalidRequestError( 

8201 "schema translate map which previously did not have " 

8202 "`None` present as a key now has `None` present; compiled " 

8203 "statement may lack adequate placeholders. Please use " 

8204 "consistent keys in successive " 

8205 "schema_translate_map dictionaries." 

8206 ) 

8207 

8208 d["_none"] = d[None] # type: ignore[index] 

8209 

8210 def replace(m): 

8211 name = m.group(2) 

8212 if name in d: 

8213 effective_schema = d[name] 

8214 else: 

8215 if name in (None, "_none"): 

8216 raise exc.InvalidRequestError( 

8217 "schema translate map which previously had `None` " 

8218 "present as a key now no longer has it present; don't " 

8219 "know how to apply schema for compiled statement. " 

8220 "Please use consistent keys in successive " 

8221 "schema_translate_map dictionaries." 

8222 ) 

8223 effective_schema = name 

8224 

8225 if not effective_schema: 

8226 effective_schema = self.dialect.default_schema_name 

8227 if not effective_schema: 

8228 # TODO: no coverage here 

8229 raise exc.CompileError( 

8230 "Dialect has no default schema name; can't " 

8231 "use None as dynamic schema target." 

8232 ) 

8233 return self.quote_schema(effective_schema) 

8234 

8235 return re.sub(r"(__\[SCHEMA_([^\]]+)\])", replace, statement) 

8236 

8237 def _escape_identifier(self, value: str) -> str: 

8238 """Escape an identifier. 

8239 

8240 Subclasses should override this to provide database-dependent 

8241 escaping behavior. 

8242 """ 

8243 

8244 value = value.replace(self.escape_quote, self.escape_to_quote) 

8245 if self._double_percents: 

8246 value = value.replace("%", "%%") 

8247 return value 

8248 

8249 def _unescape_identifier(self, value: str) -> str: 

8250 """Canonicalize an escaped identifier. 

8251 

8252 Subclasses should override this to provide database-dependent 

8253 unescaping behavior that reverses _escape_identifier. 

8254 """ 

8255 

8256 return value.replace(self.escape_to_quote, self.escape_quote) 

8257 

8258 def validate_sql_phrase(self, element, reg): 

8259 """keyword sequence filter. 

8260 

8261 a filter for elements that are intended to represent keyword sequences, 

8262 such as "INITIALLY", "INITIALLY DEFERRED", etc. no special characters 

8263 should be present. 

8264 

8265 """ 

8266 

8267 if element is not None and not reg.match(element): 

8268 raise exc.CompileError( 

8269 "Unexpected SQL phrase: %r (matching against %r)" 

8270 % (element, reg.pattern) 

8271 ) 

8272 return element 

8273 

8274 def quote_identifier(self, value: str) -> str: 

8275 """Quote an identifier. 

8276 

8277 Subclasses should override this to provide database-dependent 

8278 quoting behavior. 

8279 """ 

8280 

8281 return ( 

8282 self.initial_quote 

8283 + self._escape_identifier(value) 

8284 + self.final_quote 

8285 ) 

8286 

8287 def _requires_quotes(self, value: str) -> bool: 

8288 """Return True if the given identifier requires quoting.""" 

8289 if not value: 

8290 # a blank name is legal on SQLite only, where it can be 

8291 # delivered by reflection; quote it so that it renders as the 

8292 # database has it, rather than indexing into an empty string 

8293 # below 

8294 return True 

8295 lc_value = value.lower() 

8296 return ( 

8297 lc_value in self.reserved_words 

8298 or value[0] in self.illegal_initial_characters 

8299 or not self.legal_characters.match(str(value)) 

8300 or (lc_value != value) 

8301 ) 

8302 

8303 def _requires_quotes_illegal_chars(self, value): 

8304 """Return True if the given identifier requires quoting, but 

8305 not taking case convention into account.""" 

8306 return not self.legal_characters.match(str(value)) 

8307 

8308 def quote_schema(self, schema: str) -> str: 

8309 """Conditionally quote a schema name. 

8310 

8311 

8312 The name is quoted if it is a reserved word, contains quote-necessary 

8313 characters, or is an instance of :class:`.quoted_name` which includes 

8314 ``quote`` set to ``True``. 

8315 

8316 Subclasses can override this to provide database-dependent 

8317 quoting behavior for schema names. 

8318 

8319 :param schema: string schema name 

8320 """ 

8321 return self.quote(schema) 

8322 

8323 def quote(self, ident: str) -> str: 

8324 """Conditionally quote an identifier. 

8325 

8326 The identifier is quoted if it is a reserved word, contains 

8327 quote-necessary characters, or is an instance of 

8328 :class:`.quoted_name` which includes ``quote`` set to ``True``. 

8329 

8330 Subclasses can override this to provide database-dependent 

8331 quoting behavior for identifier names. 

8332 

8333 :param ident: string identifier 

8334 """ 

8335 force = getattr(ident, "quote", None) 

8336 

8337 if force is None: 

8338 if ident in self._strings: 

8339 return self._strings[ident] 

8340 else: 

8341 if self._requires_quotes(ident): 

8342 self._strings[ident] = self.quote_identifier(ident) 

8343 else: 

8344 self._strings[ident] = ident 

8345 return self._strings[ident] 

8346 elif force: 

8347 return self.quote_identifier(ident) 

8348 else: 

8349 return ident 

8350 

8351 def format_collation(self, collation_name, collation_schema=None): 

8352 if self.quote_case_sensitive_collations: 

8353 name = self.quote(collation_name) 

8354 else: 

8355 name = collation_name 

8356 

8357 if collation_schema is not None: 

8358 return f"{self.quote_schema(collation_schema)}.{name}" 

8359 else: 

8360 return name 

8361 

8362 def format_sequence( 

8363 self, sequence: schema.Sequence, use_schema: bool = True 

8364 ) -> str: 

8365 name = self.quote(sequence.name) 

8366 

8367 effective_schema = self.schema_for_object(sequence) 

8368 

8369 if ( 

8370 not self.omit_schema 

8371 and use_schema 

8372 and effective_schema is not None 

8373 ): 

8374 name = self.quote_schema(effective_schema) + "." + name 

8375 return name 

8376 

8377 def format_label( 

8378 self, label: Label[Any], name: Optional[str] = None 

8379 ) -> str: 

8380 return self.quote(name or label.name) 

8381 

8382 def format_alias( 

8383 self, alias: Optional[AliasedReturnsRows], name: Optional[str] = None 

8384 ) -> str: 

8385 if name is None: 

8386 assert alias is not None 

8387 return self.quote(alias.name) 

8388 else: 

8389 return self.quote(name) 

8390 

8391 def format_savepoint(self, savepoint, name=None): 

8392 # Running the savepoint name through quoting is unnecessary 

8393 # for all known dialects. This is here to support potential 

8394 # third party use cases 

8395 ident = name or savepoint.ident 

8396 if self._requires_quotes(ident): 

8397 ident = self.quote_identifier(ident) 

8398 return ident 

8399 

8400 @util.preload_module("sqlalchemy.sql.naming") 

8401 def format_constraint( 

8402 self, constraint: Union[Constraint, Index], _alembic_quote: bool = True 

8403 ) -> Optional[str]: 

8404 naming = util.preloaded.sql_naming 

8405 

8406 if constraint.name is _NONE_NAME: 

8407 name = naming._constraint_name_for_table( 

8408 constraint, constraint.table 

8409 ) 

8410 

8411 if name is None: 

8412 return None 

8413 else: 

8414 name = constraint.name 

8415 

8416 assert name is not None 

8417 if constraint.__visit_name__ == "index": 

8418 return self.truncate_and_render_index_name( 

8419 name, _alembic_quote=_alembic_quote 

8420 ) 

8421 else: 

8422 return self.truncate_and_render_constraint_name( 

8423 name, _alembic_quote=_alembic_quote 

8424 ) 

8425 

8426 def truncate_and_render_index_name( 

8427 self, name: str, _alembic_quote: bool = True 

8428 ) -> str: 

8429 # calculate these at format time so that ad-hoc changes 

8430 # to dialect.max_identifier_length etc. can be reflected 

8431 # as IdentifierPreparer is long lived 

8432 max_ = ( 

8433 self.dialect.max_index_name_length 

8434 or self.dialect.max_identifier_length 

8435 ) 

8436 return self._truncate_and_render_maxlen_name( 

8437 name, max_, _alembic_quote 

8438 ) 

8439 

8440 def truncate_and_render_constraint_name( 

8441 self, name: str, _alembic_quote: bool = True 

8442 ) -> str: 

8443 # calculate these at format time so that ad-hoc changes 

8444 # to dialect.max_identifier_length etc. can be reflected 

8445 # as IdentifierPreparer is long lived 

8446 max_ = ( 

8447 self.dialect.max_constraint_name_length 

8448 or self.dialect.max_identifier_length 

8449 ) 

8450 return self._truncate_and_render_maxlen_name( 

8451 name, max_, _alembic_quote 

8452 ) 

8453 

8454 def _truncate_and_render_maxlen_name( 

8455 self, name: str, max_: int, _alembic_quote: bool 

8456 ) -> str: 

8457 if isinstance(name, elements._truncated_label): 

8458 if len(name) > max_: 

8459 name = name[0 : max_ - 8] + "_" + util.md5_hex(name)[-4:] 

8460 else: 

8461 self.dialect.validate_identifier(name) 

8462 

8463 if not _alembic_quote: 

8464 return name 

8465 else: 

8466 return self.quote(name) 

8467 

8468 def format_index(self, index: Index) -> str: 

8469 name = self.format_constraint(index) 

8470 assert name is not None 

8471 return name 

8472 

8473 def format_table( 

8474 self, 

8475 table: FromClause, 

8476 use_schema: bool = True, 

8477 name: Optional[str] = None, 

8478 ) -> str: 

8479 """Prepare a quoted table and schema name.""" 

8480 if name is None: 

8481 if TYPE_CHECKING: 

8482 assert isinstance(table, NamedFromClause) 

8483 name = table.name 

8484 

8485 result = self.quote(name) 

8486 

8487 effective_schema = self.schema_for_object(table) 

8488 

8489 if not self.omit_schema and use_schema and effective_schema: 

8490 result = self.quote_schema(effective_schema) + "." + result 

8491 return result 

8492 

8493 def format_schema(self, name): 

8494 """Prepare a quoted schema name.""" 

8495 

8496 return self.quote(name) 

8497 

8498 def format_label_name( 

8499 self, 

8500 name, 

8501 anon_map=None, 

8502 ): 

8503 """Prepare a quoted column name.""" 

8504 

8505 if anon_map is not None and isinstance( 

8506 name, elements._truncated_label 

8507 ): 

8508 name = name.apply_map(anon_map) 

8509 

8510 return self.quote(name) 

8511 

8512 def format_column( 

8513 self, 

8514 column: ColumnElement[Any], 

8515 use_table: bool = False, 

8516 name: Optional[str] = None, 

8517 table_name: Optional[str] = None, 

8518 use_schema: bool = False, 

8519 anon_map: Optional[Mapping[str, Any]] = None, 

8520 ) -> str: 

8521 """Prepare a quoted column name.""" 

8522 

8523 if name is None: 

8524 name = column.name 

8525 assert name is not None 

8526 

8527 if anon_map is not None and isinstance( 

8528 name, elements._truncated_label 

8529 ): 

8530 name = name.apply_map(anon_map) 

8531 

8532 if not getattr(column, "is_literal", False): 

8533 if use_table: 

8534 return ( 

8535 self.format_table( 

8536 column.table, use_schema=use_schema, name=table_name 

8537 ) 

8538 + "." 

8539 + self.quote(name) 

8540 ) 

8541 else: 

8542 return self.quote(name) 

8543 else: 

8544 # literal textual elements get stuck into ColumnClause a lot, 

8545 # which shouldn't get quoted 

8546 

8547 if use_table: 

8548 return ( 

8549 self.format_table( 

8550 column.table, use_schema=use_schema, name=table_name 

8551 ) 

8552 + "." 

8553 + name 

8554 ) 

8555 else: 

8556 return name 

8557 

8558 def format_table_seq(self, table, use_schema=True): 

8559 """Format table name and schema as a tuple.""" 

8560 

8561 # Dialects with more levels in their fully qualified references 

8562 # ('database', 'owner', etc.) could override this and return 

8563 # a longer sequence. 

8564 

8565 effective_schema = self.schema_for_object(table) 

8566 

8567 if not self.omit_schema and use_schema and effective_schema: 

8568 return ( 

8569 self.quote_schema(effective_schema), 

8570 self.format_table(table, use_schema=False), 

8571 ) 

8572 else: 

8573 return (self.format_table(table, use_schema=False),) 

8574 

8575 @util.memoized_property 

8576 def _r_identifiers(self): 

8577 initial, final, escaped_final = ( 

8578 re.escape(s) 

8579 for s in ( 

8580 self.initial_quote, 

8581 self.final_quote, 

8582 self._escape_identifier(self.final_quote), 

8583 ) 

8584 ) 

8585 r = re.compile( 

8586 r"(?:" 

8587 r"(?:%(initial)s((?:%(escaped)s|[^%(final)s])+)%(final)s" 

8588 r"|([^\.]+))(?=\.|$))+" 

8589 % {"initial": initial, "final": final, "escaped": escaped_final} 

8590 ) 

8591 return r 

8592 

8593 def unformat_identifiers(self, identifiers: str) -> Sequence[str]: 

8594 """Unpack 'schema.table.column'-like strings into components.""" 

8595 

8596 r = self._r_identifiers 

8597 return [ 

8598 self._unescape_identifier(i) 

8599 for i in [a or b for a, b in r.findall(identifiers)] 

8600 ]