1# sql/compiler.py
2# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
7# mypy: allow-untyped-defs, allow-untyped-calls
8
9"""Base SQL and DDL compiler implementations.
10
11Classes provided include:
12
13:class:`.compiler.SQLCompiler` - renders SQL
14strings
15
16:class:`.compiler.DDLCompiler` - renders DDL
17(data definition language) strings
18
19:class:`.compiler.GenericTypeCompiler` - renders
20type specification strings.
21
22To generate user-defined SQL strings, see
23:doc:`/ext/compiler`.
24
25"""
26from __future__ import annotations
27
28import collections
29import collections.abc as collections_abc
30import contextlib
31from enum import IntEnum
32import functools
33import itertools
34import operator
35import re
36from time import perf_counter
37import typing
38from typing import Any
39from typing import Callable
40from typing import cast
41from typing import ClassVar
42from typing import Dict
43from typing import FrozenSet
44from typing import Iterable
45from typing import Iterator
46from typing import List
47from typing import Mapping
48from typing import MutableMapping
49from typing import NamedTuple
50from typing import NoReturn
51from typing import Optional
52from typing import Pattern
53from typing import Protocol
54from typing import Sequence
55from typing import Set
56from typing import Tuple
57from typing import Type
58from typing import TYPE_CHECKING
59from typing import TypedDict
60from typing import Union
61
62from . import base
63from . import coercions
64from . import crud
65from . import elements
66from . import functions
67from . import operators
68from . import roles
69from . import schema
70from . import selectable
71from . import sqltypes
72from . import util as sql_util
73from ._typing import is_column_element
74from ._typing import is_dml
75from .base import _de_clone
76from .base import _from_objects
77from .base import _NONE_NAME
78from .base import _SentinelDefaultCharacterization
79from .base import Executable
80from .base import NO_ARG
81from .elements import ClauseElement
82from .elements import quoted_name
83from .schema import Column
84from .sqltypes import TupleType
85from .type_api import TypeEngine
86from .visitors import prefix_anon_map
87from .visitors import Visitable
88from .. import exc
89from .. import util
90from ..util import FastIntFlag
91from ..util.typing import Literal
92from ..util.typing import TupleAny
93from ..util.typing import Unpack
94
95if typing.TYPE_CHECKING:
96 from .annotation import _AnnotationDict
97 from .base import _AmbiguousTableNameMap
98 from .base import CompileState
99 from .cache_key import CacheKey
100 from .ddl import ExecutableDDLElement
101 from .dml import Insert
102 from .dml import UpdateBase
103 from .dml import ValuesBase
104 from .elements import _truncated_label
105 from .elements import BindParameter
106 from .elements import ColumnClause
107 from .elements import ColumnElement
108 from .elements import Label
109 from .functions import Function
110 from .schema import Table
111 from .selectable import AliasedReturnsRows
112 from .selectable import CompoundSelectState
113 from .selectable import CTE
114 from .selectable import FromClause
115 from .selectable import NamedFromClause
116 from .selectable import ReturnsRows
117 from .selectable import Select
118 from .selectable import SelectState
119 from .type_api import _BindProcessorType
120 from ..engine.cursor import CursorResultMetaData
121 from ..engine.interfaces import _CoreSingleExecuteParams
122 from ..engine.interfaces import _DBAPIAnyExecuteParams
123 from ..engine.interfaces import _DBAPIMultiExecuteParams
124 from ..engine.interfaces import _DBAPISingleExecuteParams
125 from ..engine.interfaces import _ExecuteOptions
126 from ..engine.interfaces import _GenericSetInputSizesType
127 from ..engine.interfaces import _MutableCoreSingleExecuteParams
128 from ..engine.interfaces import Dialect
129 from ..engine.interfaces import SchemaTranslateMapType
130
131_FromHintsType = Dict["FromClause", str]
132
133RESERVED_WORDS = {
134 "all",
135 "analyse",
136 "analyze",
137 "and",
138 "any",
139 "array",
140 "as",
141 "asc",
142 "asymmetric",
143 "authorization",
144 "between",
145 "binary",
146 "both",
147 "case",
148 "cast",
149 "check",
150 "collate",
151 "column",
152 "constraint",
153 "create",
154 "cross",
155 "current_date",
156 "current_role",
157 "current_time",
158 "current_timestamp",
159 "current_user",
160 "default",
161 "deferrable",
162 "desc",
163 "distinct",
164 "do",
165 "else",
166 "end",
167 "except",
168 "false",
169 "for",
170 "foreign",
171 "freeze",
172 "from",
173 "full",
174 "grant",
175 "group",
176 "having",
177 "ilike",
178 "in",
179 "initially",
180 "inner",
181 "intersect",
182 "into",
183 "is",
184 "isnull",
185 "join",
186 "leading",
187 "left",
188 "like",
189 "limit",
190 "localtime",
191 "localtimestamp",
192 "natural",
193 "new",
194 "not",
195 "notnull",
196 "null",
197 "off",
198 "offset",
199 "old",
200 "on",
201 "only",
202 "or",
203 "order",
204 "outer",
205 "overlaps",
206 "placing",
207 "primary",
208 "references",
209 "right",
210 "select",
211 "session_user",
212 "set",
213 "similar",
214 "some",
215 "symmetric",
216 "table",
217 "then",
218 "to",
219 "trailing",
220 "true",
221 "union",
222 "unique",
223 "user",
224 "using",
225 "verbose",
226 "when",
227 "where",
228}
229
230LEGAL_CHARACTERS = re.compile(r"^[A-Z0-9_$]+$", re.I)
231LEGAL_CHARACTERS_PLUS_SPACE = re.compile(r"^[A-Z0-9_ $]+$", re.I)
232ILLEGAL_INITIAL_CHARACTERS = {str(x) for x in range(0, 10)}.union(["$"])
233
234FK_ON_DELETE = re.compile(
235 r"^(?:RESTRICT|CASCADE|SET NULL|NO ACTION|SET DEFAULT)$", re.I
236)
237FK_ON_UPDATE = re.compile(
238 r"^(?:RESTRICT|CASCADE|SET NULL|NO ACTION|SET DEFAULT)$", re.I
239)
240FK_INITIALLY = re.compile(r"^(?:DEFERRED|IMMEDIATE)$", re.I)
241BIND_PARAMS = re.compile(r"(?<![:\w\$\x5c]):([\w\$]+)(?![:\w\$])", re.UNICODE)
242BIND_PARAMS_ESC = re.compile(r"\x5c(:[\w\$]*)(?![:\w\$])", re.UNICODE)
243
244_pyformat_template = "%%(%(name)s)s"
245BIND_TEMPLATES = {
246 "pyformat": _pyformat_template,
247 "qmark": "?",
248 "format": "%%s",
249 "numeric": ":[_POSITION]",
250 "numeric_dollar": "$[_POSITION]",
251 "named": ":%(name)s",
252}
253
254
255OPERATORS = {
256 # binary
257 operators.and_: " AND ",
258 operators.or_: " OR ",
259 operators.add: " + ",
260 operators.mul: " * ",
261 operators.sub: " - ",
262 operators.mod: " % ",
263 operators.neg: "-",
264 operators.lt: " < ",
265 operators.le: " <= ",
266 operators.ne: " != ",
267 operators.gt: " > ",
268 operators.ge: " >= ",
269 operators.eq: " = ",
270 operators.is_distinct_from: " IS DISTINCT FROM ",
271 operators.is_not_distinct_from: " IS NOT DISTINCT FROM ",
272 operators.concat_op: " || ",
273 operators.match_op: " MATCH ",
274 operators.not_match_op: " NOT MATCH ",
275 operators.in_op: " IN ",
276 operators.not_in_op: " NOT IN ",
277 operators.comma_op: ", ",
278 operators.from_: " FROM ",
279 operators.as_: " AS ",
280 operators.is_: " IS ",
281 operators.is_not: " IS NOT ",
282 operators.collate: " COLLATE ",
283 # unary
284 operators.exists: "EXISTS ",
285 operators.distinct_op: "DISTINCT ",
286 operators.inv: "NOT ",
287 operators.any_op: "ANY ",
288 operators.all_op: "ALL ",
289 # modifiers
290 operators.desc_op: " DESC",
291 operators.asc_op: " ASC",
292 operators.nulls_first_op: " NULLS FIRST",
293 operators.nulls_last_op: " NULLS LAST",
294 # bitwise
295 operators.bitwise_xor_op: " ^ ",
296 operators.bitwise_or_op: " | ",
297 operators.bitwise_and_op: " & ",
298 operators.bitwise_not_op: "~",
299 operators.bitwise_lshift_op: " << ",
300 operators.bitwise_rshift_op: " >> ",
301}
302
303FUNCTIONS: Dict[Type[Function[Any]], str] = {
304 functions.coalesce: "coalesce",
305 functions.current_date: "CURRENT_DATE",
306 functions.current_time: "CURRENT_TIME",
307 functions.current_timestamp: "CURRENT_TIMESTAMP",
308 functions.current_user: "CURRENT_USER",
309 functions.localtime: "LOCALTIME",
310 functions.localtimestamp: "LOCALTIMESTAMP",
311 functions.random: "random",
312 functions.sysdate: "sysdate",
313 functions.session_user: "SESSION_USER",
314 functions.user: "USER",
315 functions.cube: "CUBE",
316 functions.rollup: "ROLLUP",
317 functions.grouping_sets: "GROUPING SETS",
318}
319
320
321EXTRACT_MAP = {
322 "month": "month",
323 "day": "day",
324 "year": "year",
325 "second": "second",
326 "hour": "hour",
327 "doy": "doy",
328 "minute": "minute",
329 "quarter": "quarter",
330 "dow": "dow",
331 "week": "week",
332 "epoch": "epoch",
333 "milliseconds": "milliseconds",
334 "microseconds": "microseconds",
335 "timezone_hour": "timezone_hour",
336 "timezone_minute": "timezone_minute",
337}
338
339COMPOUND_KEYWORDS = {
340 selectable._CompoundSelectKeyword.UNION: "UNION",
341 selectable._CompoundSelectKeyword.UNION_ALL: "UNION ALL",
342 selectable._CompoundSelectKeyword.EXCEPT: "EXCEPT",
343 selectable._CompoundSelectKeyword.EXCEPT_ALL: "EXCEPT ALL",
344 selectable._CompoundSelectKeyword.INTERSECT: "INTERSECT",
345 selectable._CompoundSelectKeyword.INTERSECT_ALL: "INTERSECT ALL",
346}
347
348
349class ResultColumnsEntry(NamedTuple):
350 """Tracks a column expression that is expected to be represented
351 in the result rows for this statement.
352
353 This normally refers to the columns clause of a SELECT statement
354 but may also refer to a RETURNING clause, as well as for dialect-specific
355 emulations.
356
357 """
358
359 keyname: str
360 """string name that's expected in cursor.description"""
361
362 name: str
363 """column name, may be labeled"""
364
365 objects: Tuple[Any, ...]
366 """sequence of objects that should be able to locate this column
367 in a RowMapping. This is typically string names and aliases
368 as well as Column objects.
369
370 """
371
372 type: TypeEngine[Any]
373 """Datatype to be associated with this column. This is where
374 the "result processing" logic directly links the compiled statement
375 to the rows that come back from the cursor.
376
377 """
378
379
380class _ResultMapAppender(Protocol):
381 def __call__(
382 self,
383 keyname: str,
384 name: str,
385 objects: Sequence[Any],
386 type_: TypeEngine[Any],
387 ) -> None: ...
388
389
390# integer indexes into ResultColumnsEntry used by cursor.py.
391# some profiling showed integer access faster than named tuple
392RM_RENDERED_NAME: Literal[0] = 0
393RM_NAME: Literal[1] = 1
394RM_OBJECTS: Literal[2] = 2
395RM_TYPE: Literal[3] = 3
396
397
398class _BaseCompilerStackEntry(TypedDict):
399 asfrom_froms: Set[FromClause]
400 correlate_froms: Set[FromClause]
401 selectable: ReturnsRows
402
403
404class _CompilerStackEntry(_BaseCompilerStackEntry, total=False):
405 compile_state: CompileState
406 need_result_map_for_nested: bool
407 need_result_map_for_compound: bool
408 select_0: ReturnsRows
409 insert_from_select: Select[Unpack[TupleAny]]
410
411
412class ExpandedState(NamedTuple):
413 """represents state to use when producing "expanded" and
414 "post compile" bound parameters for a statement.
415
416 "expanded" parameters are parameters that are generated at
417 statement execution time to suit a number of parameters passed, the most
418 prominent example being the individual elements inside of an IN expression.
419
420 "post compile" parameters are parameters where the SQL literal value
421 will be rendered into the SQL statement at execution time, rather than
422 being passed as separate parameters to the driver.
423
424 To create an :class:`.ExpandedState` instance, use the
425 :meth:`.SQLCompiler.construct_expanded_state` method on any
426 :class:`.SQLCompiler` instance.
427
428 """
429
430 statement: str
431 """String SQL statement with parameters fully expanded"""
432
433 parameters: _CoreSingleExecuteParams
434 """Parameter dictionary with parameters fully expanded.
435
436 For a statement that uses named parameters, this dictionary will map
437 exactly to the names in the statement. For a statement that uses
438 positional parameters, the :attr:`.ExpandedState.positional_parameters`
439 will yield a tuple with the positional parameter set.
440
441 """
442
443 processors: Mapping[str, _BindProcessorType[Any]]
444 """mapping of bound value processors"""
445
446 positiontup: Optional[Sequence[str]]
447 """Sequence of string names indicating the order of positional
448 parameters"""
449
450 parameter_expansion: Mapping[str, List[str]]
451 """Mapping representing the intermediary link from original parameter
452 name to list of "expanded" parameter names, for those parameters that
453 were expanded."""
454
455 @property
456 def positional_parameters(self) -> Tuple[Any, ...]:
457 """Tuple of positional parameters, for statements that were compiled
458 using a positional paramstyle.
459
460 """
461 if self.positiontup is None:
462 raise exc.InvalidRequestError(
463 "statement does not use a positional paramstyle"
464 )
465 return tuple(self.parameters[key] for key in self.positiontup)
466
467 @property
468 def additional_parameters(self) -> _CoreSingleExecuteParams:
469 """synonym for :attr:`.ExpandedState.parameters`."""
470 return self.parameters
471
472
473class _InsertManyValues(NamedTuple):
474 """represents state to use for executing an "insertmanyvalues" statement.
475
476 The primary consumers of this object are the
477 :meth:`.SQLCompiler._deliver_insertmanyvalues_batches` and
478 :meth:`.DefaultDialect._deliver_insertmanyvalues_batches` methods.
479
480 .. versionadded:: 2.0
481
482 """
483
484 is_default_expr: bool
485 """if True, the statement is of the form
486 ``INSERT INTO TABLE DEFAULT VALUES``, and can't be rewritten as a "batch"
487
488 """
489
490 single_values_expr: str
491 """The rendered "values" clause of the INSERT statement.
492
493 This is typically the parenthesized section e.g. "(?, ?, ?)" or similar.
494 The insertmanyvalues logic uses this string as a search and replace
495 target.
496
497 """
498
499 insert_crud_params: List[crud._CrudParamElementStr]
500 """List of Column / bind names etc. used while rewriting the statement"""
501
502 num_positional_params_counted: int
503 """the number of bound parameters in a single-row statement.
504
505 This count may be larger or smaller than the actual number of columns
506 targeted in the INSERT, as it accommodates for SQL expressions
507 in the values list that may have zero or more parameters embedded
508 within them.
509
510 This count is part of what's used to organize rewritten parameter lists
511 when batching.
512
513 """
514
515 sort_by_parameter_order: bool = False
516 """if the deterministic_returnined_order parameter were used on the
517 insert.
518
519 All of the attributes following this will only be used if this is True.
520
521 """
522
523 includes_upsert_behaviors: bool = False
524 """if True, we have to accommodate for upsert behaviors.
525
526 This will in some cases downgrade "insertmanyvalues" that requests
527 deterministic ordering.
528
529 """
530
531 sentinel_columns: Optional[Sequence[Column[Any]]] = None
532 """List of sentinel columns that were located.
533
534 This list is only here if the INSERT asked for
535 sort_by_parameter_order=True,
536 and dialect-appropriate sentinel columns were located.
537
538 .. versionadded:: 2.0.10
539
540 """
541
542 num_sentinel_columns: int = 0
543 """how many sentinel columns are in the above list, if any.
544
545 This is the same as
546 ``len(sentinel_columns) if sentinel_columns is not None else 0``
547
548 """
549
550 sentinel_param_keys: Optional[Sequence[str]] = None
551 """parameter str keys in each param dictionary / tuple
552 that would link to the client side "sentinel" values for that row, which
553 we can use to match up parameter sets to result rows.
554
555 This is only present if sentinel_columns is present and the INSERT
556 statement actually refers to client side values for these sentinel
557 columns.
558
559 .. versionadded:: 2.0.10
560
561 .. versionchanged:: 2.0.29 - the sequence is now string dictionary keys
562 only, used against the "compiled parameteters" collection before
563 the parameters were converted by bound parameter processors
564
565 """
566
567 implicit_sentinel: bool = False
568 """if True, we have exactly one sentinel column and it uses a server side
569 value, currently has to generate an incrementing integer value.
570
571 The dialect in question would have asserted that it supports receiving
572 these values back and sorting on that value as a means of guaranteeing
573 correlation with the incoming parameter list.
574
575 .. versionadded:: 2.0.10
576
577 """
578
579 embed_values_counter: bool = False
580 """Whether to embed an incrementing integer counter in each parameter
581 set within the VALUES clause as parameters are batched over.
582
583 This is only used for a specific INSERT..SELECT..VALUES..RETURNING syntax
584 where a subquery is used to produce value tuples. Current support
585 includes PostgreSQL, Microsoft SQL Server.
586
587 .. versionadded:: 2.0.10
588
589 """
590
591
592class _InsertManyValuesBatch(NamedTuple):
593 """represents an individual batch SQL statement for insertmanyvalues.
594
595 This is passed through the
596 :meth:`.SQLCompiler._deliver_insertmanyvalues_batches` and
597 :meth:`.DefaultDialect._deliver_insertmanyvalues_batches` methods out
598 to the :class:`.Connection` within the
599 :meth:`.Connection._exec_insertmany_context` method.
600
601 .. versionadded:: 2.0.10
602
603 """
604
605 replaced_statement: str
606 replaced_parameters: _DBAPIAnyExecuteParams
607 processed_setinputsizes: Optional[_GenericSetInputSizesType]
608 batch: Sequence[_DBAPISingleExecuteParams]
609 sentinel_values: Sequence[Tuple[Any, ...]]
610 current_batch_size: int
611 batchnum: int
612 total_batches: int
613 rows_sorted: bool
614 is_downgraded: bool
615
616
617class InsertmanyvaluesSentinelOpts(FastIntFlag):
618 """bitflag enum indicating styles of PK defaults
619 which can work as implicit sentinel columns
620
621 """
622
623 NOT_SUPPORTED = 1
624 AUTOINCREMENT = 2
625 IDENTITY = 4
626 SEQUENCE = 8
627
628 ANY_AUTOINCREMENT = AUTOINCREMENT | IDENTITY | SEQUENCE
629 _SUPPORTED_OR_NOT = NOT_SUPPORTED | ANY_AUTOINCREMENT
630
631 USE_INSERT_FROM_SELECT = 16
632 RENDER_SELECT_COL_CASTS = 64
633
634
635class CompilerState(IntEnum):
636 COMPILING = 0
637 """statement is present, compilation phase in progress"""
638
639 STRING_APPLIED = 1
640 """statement is present, string form of the statement has been applied.
641
642 Additional processors by subclasses may still be pending.
643
644 """
645
646 NO_STATEMENT = 2
647 """compiler does not have a statement to compile, is used
648 for method access"""
649
650
651class Linting(IntEnum):
652 """represent preferences for the 'SQL linting' feature.
653
654 this feature currently includes support for flagging cartesian products
655 in SQL statements.
656
657 """
658
659 NO_LINTING = 0
660 "Disable all linting."
661
662 COLLECT_CARTESIAN_PRODUCTS = 1
663 """Collect data on FROMs and cartesian products and gather into
664 'self.from_linter'"""
665
666 WARN_LINTING = 2
667 "Emit warnings for linters that find problems"
668
669 FROM_LINTING = COLLECT_CARTESIAN_PRODUCTS | WARN_LINTING
670 """Warn for cartesian products; combines COLLECT_CARTESIAN_PRODUCTS
671 and WARN_LINTING"""
672
673
674NO_LINTING, COLLECT_CARTESIAN_PRODUCTS, WARN_LINTING, FROM_LINTING = tuple(
675 Linting
676)
677
678
679class FromLinter(collections.namedtuple("FromLinter", ["froms", "edges"])):
680 """represents current state for the "cartesian product" detection
681 feature."""
682
683 def lint(self, start=None):
684 froms = self.froms
685 if not froms:
686 return None, None
687
688 edges = set(self.edges)
689 the_rest = set(froms)
690
691 if start is not None:
692 start_with = start
693 the_rest.remove(start_with)
694 else:
695 start_with = the_rest.pop()
696
697 stack = collections.deque([start_with])
698
699 while stack and the_rest:
700 node = stack.popleft()
701 the_rest.discard(node)
702
703 # comparison of nodes in edges here is based on hash equality, as
704 # there are "annotated" elements that match the non-annotated ones.
705 # to remove the need for in-python hash() calls, use native
706 # containment routines (e.g. "node in edge", "edge.index(node)")
707 to_remove = {edge for edge in edges if node in edge}
708
709 # appendleft the node in each edge that is not
710 # the one that matched.
711 stack.extendleft(edge[not edge.index(node)] for edge in to_remove)
712 edges.difference_update(to_remove)
713
714 # FROMS left over? boom
715 if the_rest:
716 return the_rest, start_with
717 else:
718 return None, None
719
720 def warn(self, stmt_type="SELECT"):
721 the_rest, start_with = self.lint()
722
723 # FROMS left over? boom
724 if the_rest:
725 froms = the_rest
726 if froms:
727 template = (
728 "{stmt_type} statement has a cartesian product between "
729 "FROM element(s) {froms} and "
730 'FROM element "{start}". Apply join condition(s) '
731 "between each element to resolve."
732 )
733 froms_str = ", ".join(
734 f'"{self.froms[from_]}"' for from_ in froms
735 )
736 message = template.format(
737 stmt_type=stmt_type,
738 froms=froms_str,
739 start=self.froms[start_with],
740 )
741
742 util.warn(message)
743
744
745class Compiled:
746 """Represent a compiled SQL or DDL expression.
747
748 The ``__str__`` method of the ``Compiled`` object should produce
749 the actual text of the statement. ``Compiled`` objects are
750 specific to their underlying database dialect, and also may
751 or may not be specific to the columns referenced within a
752 particular set of bind parameters. In no case should the
753 ``Compiled`` object be dependent on the actual values of those
754 bind parameters, even though it may reference those values as
755 defaults.
756 """
757
758 statement: Optional[ClauseElement] = None
759 "The statement to compile."
760 string: str = ""
761 "The string representation of the ``statement``"
762
763 state: CompilerState
764 """description of the compiler's state"""
765
766 is_sql = False
767 is_ddl = False
768
769 _cached_metadata: Optional[CursorResultMetaData] = None
770
771 _result_columns: Optional[List[ResultColumnsEntry]] = None
772
773 schema_translate_map: Optional[SchemaTranslateMapType] = None
774
775 execution_options: _ExecuteOptions = util.EMPTY_DICT
776 """
777 Execution options propagated from the statement. In some cases,
778 sub-elements of the statement can modify these.
779 """
780
781 preparer: IdentifierPreparer
782
783 _annotations: _AnnotationDict = util.EMPTY_DICT
784
785 compile_state: Optional[CompileState] = None
786 """Optional :class:`.CompileState` object that maintains additional
787 state used by the compiler.
788
789 Major executable objects such as :class:`_expression.Insert`,
790 :class:`_expression.Update`, :class:`_expression.Delete`,
791 :class:`_expression.Select` will generate this
792 state when compiled in order to calculate additional information about the
793 object. For the top level object that is to be executed, the state can be
794 stored here where it can also have applicability towards result set
795 processing.
796
797 .. versionadded:: 1.4
798
799 """
800
801 dml_compile_state: Optional[CompileState] = None
802 """Optional :class:`.CompileState` assigned at the same point that
803 .isinsert, .isupdate, or .isdelete is assigned.
804
805 This will normally be the same object as .compile_state, with the
806 exception of cases like the :class:`.ORMFromStatementCompileState`
807 object.
808
809 .. versionadded:: 1.4.40
810
811 """
812
813 cache_key: Optional[CacheKey] = None
814 """The :class:`.CacheKey` that was generated ahead of creating this
815 :class:`.Compiled` object.
816
817 This is used for routines that need access to the original
818 :class:`.CacheKey` instance generated when the :class:`.Compiled`
819 instance was first cached, typically in order to reconcile
820 the original list of :class:`.BindParameter` objects with a
821 per-statement list that's generated on each call.
822
823 """
824
825 _gen_time: float
826 """Generation time of this :class:`.Compiled`, used for reporting
827 cache stats."""
828
829 def __init__(
830 self,
831 dialect: Dialect,
832 statement: Optional[ClauseElement],
833 schema_translate_map: Optional[SchemaTranslateMapType] = None,
834 render_schema_translate: bool = False,
835 compile_kwargs: Mapping[str, Any] = util.immutabledict(),
836 ):
837 """Construct a new :class:`.Compiled` object.
838
839 :param dialect: :class:`.Dialect` to compile against.
840
841 :param statement: :class:`_expression.ClauseElement` to be compiled.
842
843 :param schema_translate_map: dictionary of schema names to be
844 translated when forming the resultant SQL
845
846 .. seealso::
847
848 :ref:`schema_translating`
849
850 :param compile_kwargs: additional kwargs that will be
851 passed to the initial call to :meth:`.Compiled.process`.
852
853
854 """
855 self.dialect = dialect
856 self.preparer = self.dialect.identifier_preparer
857 if schema_translate_map:
858 self.schema_translate_map = schema_translate_map
859 self.preparer = self.preparer._with_schema_translate(
860 schema_translate_map
861 )
862
863 if statement is not None:
864 self.state = CompilerState.COMPILING
865 self.statement = statement
866 self.can_execute = statement.supports_execution
867 self._annotations = statement._annotations
868 if self.can_execute:
869 if TYPE_CHECKING:
870 assert isinstance(statement, Executable)
871 self.execution_options = statement._execution_options
872 self.string = self.process(self.statement, **compile_kwargs)
873
874 if render_schema_translate:
875 self.string = self.preparer._render_schema_translates(
876 self.string, schema_translate_map
877 )
878
879 self.state = CompilerState.STRING_APPLIED
880 else:
881 self.state = CompilerState.NO_STATEMENT
882
883 self._gen_time = perf_counter()
884
885 def __init_subclass__(cls) -> None:
886 cls._init_compiler_cls()
887 return super().__init_subclass__()
888
889 @classmethod
890 def _init_compiler_cls(cls):
891 pass
892
893 def _execute_on_connection(
894 self, connection, distilled_params, execution_options
895 ):
896 if self.can_execute:
897 return connection._execute_compiled(
898 self, distilled_params, execution_options
899 )
900 else:
901 raise exc.ObjectNotExecutableError(self.statement)
902
903 def visit_unsupported_compilation(self, element, err, **kw):
904 raise exc.UnsupportedCompilationError(self, type(element)) from err
905
906 @property
907 def sql_compiler(self):
908 """Return a Compiled that is capable of processing SQL expressions.
909
910 If this compiler is one, it would likely just return 'self'.
911
912 """
913
914 raise NotImplementedError()
915
916 def process(self, obj: Visitable, **kwargs: Any) -> str:
917 return obj._compiler_dispatch(self, **kwargs)
918
919 def __str__(self) -> str:
920 """Return the string text of the generated SQL or DDL."""
921
922 if self.state is CompilerState.STRING_APPLIED:
923 return self.string
924 else:
925 return ""
926
927 def construct_params(
928 self,
929 params: Optional[_CoreSingleExecuteParams] = None,
930 extracted_parameters: Optional[Sequence[BindParameter[Any]]] = None,
931 escape_names: bool = True,
932 ) -> Optional[_MutableCoreSingleExecuteParams]:
933 """Return the bind params for this compiled object.
934
935 :param params: a dict of string/object pairs whose values will
936 override bind values compiled in to the
937 statement.
938 """
939
940 raise NotImplementedError()
941
942 @property
943 def params(self):
944 """Return the bind params for this compiled object."""
945 return self.construct_params()
946
947
948class TypeCompiler(util.EnsureKWArg):
949 """Produces DDL specification for TypeEngine objects."""
950
951 ensure_kwarg = r"visit_\w+"
952
953 def __init__(self, dialect: Dialect):
954 self.dialect = dialect
955
956 def process(self, type_: TypeEngine[Any], **kw: Any) -> str:
957 if (
958 type_._variant_mapping
959 and self.dialect.name in type_._variant_mapping
960 ):
961 type_ = type_._variant_mapping[self.dialect.name]
962 return type_._compiler_dispatch(self, **kw)
963
964 def visit_unsupported_compilation(
965 self, element: Any, err: Exception, **kw: Any
966 ) -> NoReturn:
967 raise exc.UnsupportedCompilationError(self, element) from err
968
969
970# this was a Visitable, but to allow accurate detection of
971# column elements this is actually a column element
972class _CompileLabel(
973 roles.BinaryElementRole[Any], elements.CompilerColumnElement
974):
975 """lightweight label object which acts as an expression.Label."""
976
977 __visit_name__ = "label"
978 __slots__ = "element", "name", "_alt_names"
979
980 def __init__(self, col, name, alt_names=()):
981 self.element = col
982 self.name = name
983 self._alt_names = (col,) + alt_names
984
985 @property
986 def proxy_set(self):
987 return self.element.proxy_set
988
989 @property
990 def type(self):
991 return self.element.type
992
993 def self_group(self, **kw):
994 return self
995
996
997class ilike_case_insensitive(
998 roles.BinaryElementRole[Any], elements.CompilerColumnElement
999):
1000 """produce a wrapping element for a case-insensitive portion of
1001 an ILIKE construct.
1002
1003 The construct usually renders the ``lower()`` function, but on
1004 PostgreSQL will pass silently with the assumption that "ILIKE"
1005 is being used.
1006
1007 .. versionadded:: 2.0
1008
1009 """
1010
1011 __visit_name__ = "ilike_case_insensitive_operand"
1012 __slots__ = "element", "comparator"
1013
1014 def __init__(self, element):
1015 self.element = element
1016 self.comparator = element.comparator
1017
1018 @property
1019 def proxy_set(self):
1020 return self.element.proxy_set
1021
1022 @property
1023 def type(self):
1024 return self.element.type
1025
1026 def self_group(self, **kw):
1027 return self
1028
1029 def _with_binary_element_type(self, type_):
1030 return ilike_case_insensitive(
1031 self.element._with_binary_element_type(type_)
1032 )
1033
1034
1035class SQLCompiler(Compiled):
1036 """Default implementation of :class:`.Compiled`.
1037
1038 Compiles :class:`_expression.ClauseElement` objects into SQL strings.
1039
1040 """
1041
1042 extract_map = EXTRACT_MAP
1043
1044 bindname_escape_characters: ClassVar[Mapping[str, str]] = (
1045 util.immutabledict(
1046 {
1047 "%": "P",
1048 "(": "A",
1049 ")": "Z",
1050 ":": "C",
1051 ".": "_",
1052 "[": "_",
1053 "]": "_",
1054 " ": "_",
1055 }
1056 )
1057 )
1058 """A mapping (e.g. dict or similar) containing a lookup of
1059 characters keyed to replacement characters which will be applied to all
1060 'bind names' used in SQL statements as a form of 'escaping'; the given
1061 characters are replaced entirely with the 'replacement' character when
1062 rendered in the SQL statement, and a similar translation is performed
1063 on the incoming names used in parameter dictionaries passed to methods
1064 like :meth:`_engine.Connection.execute`.
1065
1066 This allows bound parameter names used in :func:`_sql.bindparam` and
1067 other constructs to have any arbitrary characters present without any
1068 concern for characters that aren't allowed at all on the target database.
1069
1070 Third party dialects can establish their own dictionary here to replace the
1071 default mapping, which will ensure that the particular characters in the
1072 mapping will never appear in a bound parameter name.
1073
1074 The dictionary is evaluated at **class creation time**, so cannot be
1075 modified at runtime; it must be present on the class when the class
1076 is first declared.
1077
1078 Note that for dialects that have additional bound parameter rules such
1079 as additional restrictions on leading characters, the
1080 :meth:`_sql.SQLCompiler.bindparam_string` method may need to be augmented.
1081 See the cx_Oracle compiler for an example of this.
1082
1083 .. versionadded:: 2.0.0rc1
1084
1085 """
1086
1087 _bind_translate_re: ClassVar[Pattern[str]]
1088 _bind_translate_chars: ClassVar[Mapping[str, str]]
1089
1090 is_sql = True
1091
1092 compound_keywords = COMPOUND_KEYWORDS
1093
1094 isdelete: bool = False
1095 isinsert: bool = False
1096 isupdate: bool = False
1097 """class-level defaults which can be set at the instance
1098 level to define if this Compiled instance represents
1099 INSERT/UPDATE/DELETE
1100 """
1101
1102 postfetch: Optional[List[Column[Any]]]
1103 """list of columns that can be post-fetched after INSERT or UPDATE to
1104 receive server-updated values"""
1105
1106 insert_prefetch: Sequence[Column[Any]] = ()
1107 """list of columns for which default values should be evaluated before
1108 an INSERT takes place"""
1109
1110 update_prefetch: Sequence[Column[Any]] = ()
1111 """list of columns for which onupdate default values should be evaluated
1112 before an UPDATE takes place"""
1113
1114 implicit_returning: Optional[Sequence[ColumnElement[Any]]] = None
1115 """list of "implicit" returning columns for a toplevel INSERT or UPDATE
1116 statement, used to receive newly generated values of columns.
1117
1118 .. versionadded:: 2.0 ``implicit_returning`` replaces the previous
1119 ``returning`` collection, which was not a generalized RETURNING
1120 collection and instead was in fact specific to the "implicit returning"
1121 feature.
1122
1123 """
1124
1125 isplaintext: bool = False
1126
1127 binds: Dict[str, BindParameter[Any]]
1128 """a dictionary of bind parameter keys to BindParameter instances."""
1129
1130 bind_names: Dict[BindParameter[Any], str]
1131 """a dictionary of BindParameter instances to "compiled" names
1132 that are actually present in the generated SQL"""
1133
1134 stack: List[_CompilerStackEntry]
1135 """major statements such as SELECT, INSERT, UPDATE, DELETE are
1136 tracked in this stack using an entry format."""
1137
1138 returning_precedes_values: bool = False
1139 """set to True classwide to generate RETURNING
1140 clauses before the VALUES or WHERE clause (i.e. MSSQL)
1141 """
1142
1143 render_table_with_column_in_update_from: bool = False
1144 """set to True classwide to indicate the SET clause
1145 in a multi-table UPDATE statement should qualify
1146 columns with the table name (i.e. MySQL only)
1147 """
1148
1149 ansi_bind_rules: bool = False
1150 """SQL 92 doesn't allow bind parameters to be used
1151 in the columns clause of a SELECT, nor does it allow
1152 ambiguous expressions like "? = ?". A compiler
1153 subclass can set this flag to False if the target
1154 driver/DB enforces this
1155 """
1156
1157 bindtemplate: str
1158 """template to render bound parameters based on paramstyle."""
1159
1160 compilation_bindtemplate: str
1161 """template used by compiler to render parameters before positional
1162 paramstyle application"""
1163
1164 _numeric_binds_identifier_char: str
1165 """Character that's used to as the identifier of a numerical bind param.
1166 For example if this char is set to ``$``, numerical binds will be rendered
1167 in the form ``$1, $2, $3``.
1168 """
1169
1170 _result_columns: List[ResultColumnsEntry]
1171 """relates label names in the final SQL to a tuple of local
1172 column/label name, ColumnElement object (if any) and
1173 TypeEngine. CursorResult uses this for type processing and
1174 column targeting"""
1175
1176 _textual_ordered_columns: bool = False
1177 """tell the result object that the column names as rendered are important,
1178 but they are also "ordered" vs. what is in the compiled object here.
1179
1180 As of 1.4.42 this condition is only present when the statement is a
1181 TextualSelect, e.g. text("....").columns(...), where it is required
1182 that the columns are considered positionally and not by name.
1183
1184 """
1185
1186 _ad_hoc_textual: bool = False
1187 """tell the result that we encountered text() or '*' constructs in the
1188 middle of the result columns, but we also have compiled columns, so
1189 if the number of columns in cursor.description does not match how many
1190 expressions we have, that means we can't rely on positional at all and
1191 should match on name.
1192
1193 """
1194
1195 _ordered_columns: bool = True
1196 """
1197 if False, means we can't be sure the list of entries
1198 in _result_columns is actually the rendered order. Usually
1199 True unless using an unordered TextualSelect.
1200 """
1201
1202 _loose_column_name_matching: bool = False
1203 """tell the result object that the SQL statement is textual, wants to match
1204 up to Column objects, and may be using the ._tq_label in the SELECT rather
1205 than the base name.
1206
1207 """
1208
1209 _numeric_binds: bool = False
1210 """
1211 True if paramstyle is "numeric". This paramstyle is trickier than
1212 all the others.
1213
1214 """
1215
1216 _render_postcompile: bool = False
1217 """
1218 whether to render out POSTCOMPILE params during the compile phase.
1219
1220 This attribute is used only for end-user invocation of stmt.compile();
1221 it's never used for actual statement execution, where instead the
1222 dialect internals access and render the internal postcompile structure
1223 directly.
1224
1225 """
1226
1227 _post_compile_expanded_state: Optional[ExpandedState] = None
1228 """When render_postcompile is used, the ``ExpandedState`` used to create
1229 the "expanded" SQL is assigned here, and then used by the ``.params``
1230 accessor and ``.construct_params()`` methods for their return values.
1231
1232 .. versionadded:: 2.0.0rc1
1233
1234 """
1235
1236 _pre_expanded_string: Optional[str] = None
1237 """Stores the original string SQL before 'post_compile' is applied,
1238 for cases where 'post_compile' were used.
1239
1240 """
1241
1242 _pre_expanded_positiontup: Optional[List[str]] = None
1243
1244 _insertmanyvalues: Optional[_InsertManyValues] = None
1245
1246 _insert_crud_params: Optional[crud._CrudParamSequence] = None
1247
1248 literal_execute_params: FrozenSet[BindParameter[Any]] = frozenset()
1249 """bindparameter objects that are rendered as literal values at statement
1250 execution time.
1251
1252 """
1253
1254 post_compile_params: FrozenSet[BindParameter[Any]] = frozenset()
1255 """bindparameter objects that are rendered as bound parameter placeholders
1256 at statement execution time.
1257
1258 """
1259
1260 escaped_bind_names: util.immutabledict[str, str] = util.EMPTY_DICT
1261 """Late escaping of bound parameter names that has to be converted
1262 to the original name when looking in the parameter dictionary.
1263
1264 """
1265
1266 has_out_parameters = False
1267 """if True, there are bindparam() objects that have the isoutparam
1268 flag set."""
1269
1270 postfetch_lastrowid = False
1271 """if True, and this in insert, use cursor.lastrowid to populate
1272 result.inserted_primary_key. """
1273
1274 _cache_key_bind_match: Optional[
1275 Tuple[
1276 Dict[
1277 BindParameter[Any],
1278 List[BindParameter[Any]],
1279 ],
1280 Dict[
1281 str,
1282 BindParameter[Any],
1283 ],
1284 ]
1285 ] = None
1286 """a mapping that will relate the BindParameter object we compile
1287 to those that are part of the extracted collection of parameters
1288 in the cache key, if we were given a cache key.
1289
1290 """
1291
1292 positiontup: Optional[List[str]] = None
1293 """for a compiled construct that uses a positional paramstyle, will be
1294 a sequence of strings, indicating the names of bound parameters in order.
1295
1296 This is used in order to render bound parameters in their correct order,
1297 and is combined with the :attr:`_sql.Compiled.params` dictionary to
1298 render parameters.
1299
1300 This sequence always contains the unescaped name of the parameters.
1301
1302 .. seealso::
1303
1304 :ref:`faq_sql_expression_string` - includes a usage example for
1305 debugging use cases.
1306
1307 """
1308 _values_bindparam: Optional[List[str]] = None
1309
1310 _visited_bindparam: Optional[List[str]] = None
1311
1312 inline: bool = False
1313
1314 ctes: Optional[MutableMapping[CTE, str]]
1315
1316 # Detect same CTE references - Dict[(level, name), cte]
1317 # Level is required for supporting nesting
1318 ctes_by_level_name: Dict[Tuple[int, str], CTE]
1319
1320 # To retrieve key/level in ctes_by_level_name -
1321 # Dict[cte_reference, (level, cte_name, cte_opts)]
1322 level_name_by_cte: Dict[CTE, Tuple[int, str, selectable._CTEOpts]]
1323
1324 ctes_recursive: bool
1325
1326 _post_compile_pattern = re.compile(r"__\[POSTCOMPILE_(\S+?)(~~.+?~~)?\]")
1327 _pyformat_pattern = re.compile(r"%\(([^)]+?)\)s")
1328 _positional_pattern = re.compile(
1329 f"{_pyformat_pattern.pattern}|{_post_compile_pattern.pattern}"
1330 )
1331
1332 @classmethod
1333 def _init_compiler_cls(cls):
1334 cls._init_bind_translate()
1335
1336 @classmethod
1337 def _init_bind_translate(cls):
1338 reg = re.escape("".join(cls.bindname_escape_characters))
1339 cls._bind_translate_re = re.compile(f"[{reg}]")
1340 cls._bind_translate_chars = cls.bindname_escape_characters
1341
1342 def __init__(
1343 self,
1344 dialect: Dialect,
1345 statement: Optional[ClauseElement],
1346 cache_key: Optional[CacheKey] = None,
1347 column_keys: Optional[Sequence[str]] = None,
1348 for_executemany: bool = False,
1349 linting: Linting = NO_LINTING,
1350 _supporting_against: Optional[SQLCompiler] = None,
1351 **kwargs: Any,
1352 ):
1353 """Construct a new :class:`.SQLCompiler` object.
1354
1355 :param dialect: :class:`.Dialect` to be used
1356
1357 :param statement: :class:`_expression.ClauseElement` to be compiled
1358
1359 :param column_keys: a list of column names to be compiled into an
1360 INSERT or UPDATE statement.
1361
1362 :param for_executemany: whether INSERT / UPDATE statements should
1363 expect that they are to be invoked in an "executemany" style,
1364 which may impact how the statement will be expected to return the
1365 values of defaults and autoincrement / sequences and similar.
1366 Depending on the backend and driver in use, support for retrieving
1367 these values may be disabled which means SQL expressions may
1368 be rendered inline, RETURNING may not be rendered, etc.
1369
1370 :param kwargs: additional keyword arguments to be consumed by the
1371 superclass.
1372
1373 """
1374 self.column_keys = column_keys
1375
1376 self.cache_key = cache_key
1377
1378 if cache_key:
1379 cksm = {b.key: b for b in cache_key[1]}
1380 ckbm = {b: [b] for b in cache_key[1]}
1381 self._cache_key_bind_match = (ckbm, cksm)
1382
1383 # compile INSERT/UPDATE defaults/sequences to expect executemany
1384 # style execution, which may mean no pre-execute of defaults,
1385 # or no RETURNING
1386 self.for_executemany = for_executemany
1387
1388 self.linting = linting
1389
1390 # a dictionary of bind parameter keys to BindParameter
1391 # instances.
1392 self.binds = {}
1393
1394 # a dictionary of BindParameter instances to "compiled" names
1395 # that are actually present in the generated SQL
1396 self.bind_names = util.column_dict()
1397
1398 # stack which keeps track of nested SELECT statements
1399 self.stack = []
1400
1401 self._result_columns = []
1402
1403 # true if the paramstyle is positional
1404 self.positional = dialect.positional
1405 if self.positional:
1406 self._numeric_binds = nb = dialect.paramstyle.startswith("numeric")
1407 if nb:
1408 self._numeric_binds_identifier_char = (
1409 "$" if dialect.paramstyle == "numeric_dollar" else ":"
1410 )
1411
1412 self.compilation_bindtemplate = _pyformat_template
1413 else:
1414 self.compilation_bindtemplate = BIND_TEMPLATES[dialect.paramstyle]
1415
1416 self.ctes = None
1417
1418 self.label_length = (
1419 dialect.label_length or dialect.max_identifier_length
1420 )
1421
1422 # a map which tracks "anonymous" identifiers that are created on
1423 # the fly here
1424 self.anon_map = prefix_anon_map()
1425
1426 # a map which tracks "truncated" names based on
1427 # dialect.label_length or dialect.max_identifier_length
1428 self.truncated_names: Dict[Tuple[str, str], str] = {}
1429 self._truncated_counters: Dict[str, int] = {}
1430
1431 Compiled.__init__(self, dialect, statement, **kwargs)
1432
1433 if self.isinsert or self.isupdate or self.isdelete:
1434 if TYPE_CHECKING:
1435 assert isinstance(statement, UpdateBase)
1436
1437 if self.isinsert or self.isupdate:
1438 if TYPE_CHECKING:
1439 assert isinstance(statement, ValuesBase)
1440 if statement._inline:
1441 self.inline = True
1442 elif self.for_executemany and (
1443 not self.isinsert
1444 or (
1445 self.dialect.insert_executemany_returning
1446 and statement._return_defaults
1447 )
1448 ):
1449 self.inline = True
1450
1451 self.bindtemplate = BIND_TEMPLATES[dialect.paramstyle]
1452
1453 if _supporting_against:
1454 self.__dict__.update(
1455 {
1456 k: v
1457 for k, v in _supporting_against.__dict__.items()
1458 if k
1459 not in {
1460 "state",
1461 "dialect",
1462 "preparer",
1463 "positional",
1464 "_numeric_binds",
1465 "compilation_bindtemplate",
1466 "bindtemplate",
1467 }
1468 }
1469 )
1470
1471 if self.state is CompilerState.STRING_APPLIED:
1472 if self.positional:
1473 if self._numeric_binds:
1474 self._process_numeric()
1475 else:
1476 self._process_positional()
1477
1478 if self._render_postcompile:
1479 parameters = self.construct_params(
1480 escape_names=False,
1481 _no_postcompile=True,
1482 )
1483
1484 self._process_parameters_for_postcompile(
1485 parameters, _populate_self=True
1486 )
1487
1488 @property
1489 def insert_single_values_expr(self) -> Optional[str]:
1490 """When an INSERT is compiled with a single set of parameters inside
1491 a VALUES expression, the string is assigned here, where it can be
1492 used for insert batching schemes to rewrite the VALUES expression.
1493
1494 .. versionadded:: 1.3.8
1495
1496 .. versionchanged:: 2.0 This collection is no longer used by
1497 SQLAlchemy's built-in dialects, in favor of the currently
1498 internal ``_insertmanyvalues`` collection that is used only by
1499 :class:`.SQLCompiler`.
1500
1501 """
1502 if self._insertmanyvalues is None:
1503 return None
1504 else:
1505 return self._insertmanyvalues.single_values_expr
1506
1507 @util.ro_memoized_property
1508 def effective_returning(self) -> Optional[Sequence[ColumnElement[Any]]]:
1509 """The effective "returning" columns for INSERT, UPDATE or DELETE.
1510
1511 This is either the so-called "implicit returning" columns which are
1512 calculated by the compiler on the fly, or those present based on what's
1513 present in ``self.statement._returning`` (expanded into individual
1514 columns using the ``._all_selected_columns`` attribute) i.e. those set
1515 explicitly using the :meth:`.UpdateBase.returning` method.
1516
1517 .. versionadded:: 2.0
1518
1519 """
1520 if self.implicit_returning:
1521 return self.implicit_returning
1522 elif self.statement is not None and is_dml(self.statement):
1523 return [
1524 c
1525 for c in self.statement._all_selected_columns
1526 if is_column_element(c)
1527 ]
1528
1529 else:
1530 return None
1531
1532 @property
1533 def returning(self):
1534 """backwards compatibility; returns the
1535 effective_returning collection.
1536
1537 """
1538 return self.effective_returning
1539
1540 @property
1541 def current_executable(self):
1542 """Return the current 'executable' that is being compiled.
1543
1544 This is currently the :class:`_sql.Select`, :class:`_sql.Insert`,
1545 :class:`_sql.Update`, :class:`_sql.Delete`,
1546 :class:`_sql.CompoundSelect` object that is being compiled.
1547 Specifically it's assigned to the ``self.stack`` list of elements.
1548
1549 When a statement like the above is being compiled, it normally
1550 is also assigned to the ``.statement`` attribute of the
1551 :class:`_sql.Compiler` object. However, all SQL constructs are
1552 ultimately nestable, and this attribute should never be consulted
1553 by a ``visit_`` method, as it is not guaranteed to be assigned
1554 nor guaranteed to correspond to the current statement being compiled.
1555
1556 .. versionadded:: 1.3.21
1557
1558 For compatibility with previous versions, use the following
1559 recipe::
1560
1561 statement = getattr(self, "current_executable", False)
1562 if statement is False:
1563 statement = self.stack[-1]["selectable"]
1564
1565 For versions 1.4 and above, ensure only .current_executable
1566 is used; the format of "self.stack" may change.
1567
1568
1569 """
1570 try:
1571 return self.stack[-1]["selectable"]
1572 except IndexError as ie:
1573 raise IndexError("Compiler does not have a stack entry") from ie
1574
1575 @property
1576 def prefetch(self):
1577 return list(self.insert_prefetch) + list(self.update_prefetch)
1578
1579 @util.memoized_property
1580 def _global_attributes(self) -> Dict[Any, Any]:
1581 return {}
1582
1583 @util.memoized_instancemethod
1584 def _init_cte_state(self) -> MutableMapping[CTE, str]:
1585 """Initialize collections related to CTEs only if
1586 a CTE is located, to save on the overhead of
1587 these collections otherwise.
1588
1589 """
1590 # collect CTEs to tack on top of a SELECT
1591 # To store the query to print - Dict[cte, text_query]
1592 ctes: MutableMapping[CTE, str] = util.OrderedDict()
1593 self.ctes = ctes
1594
1595 # Detect same CTE references - Dict[(level, name), cte]
1596 # Level is required for supporting nesting
1597 self.ctes_by_level_name = {}
1598
1599 # To retrieve key/level in ctes_by_level_name -
1600 # Dict[cte_reference, (level, cte_name, cte_opts)]
1601 self.level_name_by_cte = {}
1602
1603 self.ctes_recursive = False
1604
1605 return ctes
1606
1607 @contextlib.contextmanager
1608 def _nested_result(self):
1609 """special API to support the use case of 'nested result sets'"""
1610 result_columns, ordered_columns = (
1611 self._result_columns,
1612 self._ordered_columns,
1613 )
1614 self._result_columns, self._ordered_columns = [], False
1615
1616 try:
1617 if self.stack:
1618 entry = self.stack[-1]
1619 entry["need_result_map_for_nested"] = True
1620 else:
1621 entry = None
1622 yield self._result_columns, self._ordered_columns
1623 finally:
1624 if entry:
1625 entry.pop("need_result_map_for_nested")
1626 self._result_columns, self._ordered_columns = (
1627 result_columns,
1628 ordered_columns,
1629 )
1630
1631 def _process_positional(self):
1632 assert not self.positiontup
1633 assert self.state is CompilerState.STRING_APPLIED
1634 assert not self._numeric_binds
1635
1636 if self.dialect.paramstyle == "format":
1637 placeholder = "%s"
1638 else:
1639 assert self.dialect.paramstyle == "qmark"
1640 placeholder = "?"
1641
1642 positions = []
1643
1644 def find_position(m: re.Match[str]) -> str:
1645 normal_bind = m.group(1)
1646 if normal_bind:
1647 positions.append(normal_bind)
1648 return placeholder
1649 else:
1650 # this a post-compile bind
1651 positions.append(m.group(2))
1652 return m.group(0)
1653
1654 self.string = re.sub(
1655 self._positional_pattern, find_position, self.string
1656 )
1657
1658 if self.escaped_bind_names:
1659 reverse_escape = {v: k for k, v in self.escaped_bind_names.items()}
1660 assert len(self.escaped_bind_names) == len(reverse_escape)
1661 self.positiontup = [
1662 reverse_escape.get(name, name) for name in positions
1663 ]
1664 else:
1665 self.positiontup = positions
1666
1667 if self._insertmanyvalues:
1668 positions = []
1669
1670 single_values_expr = re.sub(
1671 self._positional_pattern,
1672 find_position,
1673 self._insertmanyvalues.single_values_expr,
1674 )
1675 insert_crud_params = [
1676 (
1677 v[0],
1678 v[1],
1679 re.sub(self._positional_pattern, find_position, v[2]),
1680 v[3],
1681 )
1682 for v in self._insertmanyvalues.insert_crud_params
1683 ]
1684
1685 self._insertmanyvalues = self._insertmanyvalues._replace(
1686 single_values_expr=single_values_expr,
1687 insert_crud_params=insert_crud_params,
1688 )
1689
1690 def _process_numeric(self):
1691 assert self._numeric_binds
1692 assert self.state is CompilerState.STRING_APPLIED
1693
1694 num = 1
1695 param_pos: Dict[str, str] = {}
1696 order: Iterable[str]
1697 if self._insertmanyvalues and self._values_bindparam is not None:
1698 # bindparams that are not in values are always placed first.
1699 # this avoids the need of changing them when using executemany
1700 # values () ()
1701 order = itertools.chain(
1702 (
1703 name
1704 for name in self.bind_names.values()
1705 if name not in self._values_bindparam
1706 ),
1707 self.bind_names.values(),
1708 )
1709 else:
1710 order = self.bind_names.values()
1711
1712 for bind_name in order:
1713 if bind_name in param_pos:
1714 continue
1715 bind = self.binds[bind_name]
1716 if (
1717 bind in self.post_compile_params
1718 or bind in self.literal_execute_params
1719 ):
1720 # set to None to just mark the in positiontup, it will not
1721 # be replaced below.
1722 param_pos[bind_name] = None # type: ignore
1723 else:
1724 ph = f"{self._numeric_binds_identifier_char}{num}"
1725 num += 1
1726 param_pos[bind_name] = ph
1727
1728 self.next_numeric_pos = num
1729
1730 self.positiontup = list(param_pos)
1731 if self.escaped_bind_names:
1732 len_before = len(param_pos)
1733 param_pos = {
1734 self.escaped_bind_names.get(name, name): pos
1735 for name, pos in param_pos.items()
1736 }
1737 assert len(param_pos) == len_before
1738
1739 # Can't use format here since % chars are not escaped.
1740 self.string = self._pyformat_pattern.sub(
1741 lambda m: param_pos[m.group(1)], self.string
1742 )
1743
1744 if self._insertmanyvalues:
1745 single_values_expr = (
1746 # format is ok here since single_values_expr includes only
1747 # place-holders
1748 self._insertmanyvalues.single_values_expr
1749 % param_pos
1750 )
1751 insert_crud_params = [
1752 (v[0], v[1], "%s", v[3])
1753 for v in self._insertmanyvalues.insert_crud_params
1754 ]
1755
1756 self._insertmanyvalues = self._insertmanyvalues._replace(
1757 # This has the numbers (:1, :2)
1758 single_values_expr=single_values_expr,
1759 # The single binds are instead %s so they can be formatted
1760 insert_crud_params=insert_crud_params,
1761 )
1762
1763 @util.memoized_property
1764 def _bind_processors(
1765 self,
1766 ) -> MutableMapping[
1767 str, Union[_BindProcessorType[Any], Sequence[_BindProcessorType[Any]]]
1768 ]:
1769 # mypy is not able to see the two value types as the above Union,
1770 # it just sees "object". don't know how to resolve
1771 return {
1772 key: value # type: ignore
1773 for key, value in (
1774 (
1775 self.bind_names[bindparam],
1776 (
1777 bindparam.type._cached_bind_processor(self.dialect)
1778 if not bindparam.type._is_tuple_type
1779 else tuple(
1780 elem_type._cached_bind_processor(self.dialect)
1781 for elem_type in cast(
1782 TupleType, bindparam.type
1783 ).types
1784 )
1785 ),
1786 )
1787 for bindparam in self.bind_names
1788 )
1789 if value is not None
1790 }
1791
1792 def is_subquery(self):
1793 return len(self.stack) > 1
1794
1795 @property
1796 def sql_compiler(self):
1797 return self
1798
1799 def construct_expanded_state(
1800 self,
1801 params: Optional[_CoreSingleExecuteParams] = None,
1802 escape_names: bool = True,
1803 ) -> ExpandedState:
1804 """Return a new :class:`.ExpandedState` for a given parameter set.
1805
1806 For queries that use "expanding" or other late-rendered parameters,
1807 this method will provide for both the finalized SQL string as well
1808 as the parameters that would be used for a particular parameter set.
1809
1810 .. versionadded:: 2.0.0rc1
1811
1812 """
1813 parameters = self.construct_params(
1814 params,
1815 escape_names=escape_names,
1816 _no_postcompile=True,
1817 )
1818 return self._process_parameters_for_postcompile(
1819 parameters,
1820 )
1821
1822 def construct_params(
1823 self,
1824 params: Optional[_CoreSingleExecuteParams] = None,
1825 extracted_parameters: Optional[Sequence[BindParameter[Any]]] = None,
1826 escape_names: bool = True,
1827 _group_number: Optional[int] = None,
1828 _check: bool = True,
1829 _no_postcompile: bool = False,
1830 ) -> _MutableCoreSingleExecuteParams:
1831 """return a dictionary of bind parameter keys and values"""
1832
1833 if self._render_postcompile and not _no_postcompile:
1834 assert self._post_compile_expanded_state is not None
1835 if not params:
1836 return dict(self._post_compile_expanded_state.parameters)
1837 else:
1838 raise exc.InvalidRequestError(
1839 "can't construct new parameters when render_postcompile "
1840 "is used; the statement is hard-linked to the original "
1841 "parameters. Use construct_expanded_state to generate a "
1842 "new statement and parameters."
1843 )
1844
1845 has_escaped_names = escape_names and bool(self.escaped_bind_names)
1846
1847 if extracted_parameters:
1848 # related the bound parameters collected in the original cache key
1849 # to those collected in the incoming cache key. They will not have
1850 # matching names but they will line up positionally in the same
1851 # way. The parameters present in self.bind_names may be clones of
1852 # these original cache key params in the case of DML but the .key
1853 # will be guaranteed to match.
1854 if self.cache_key is None:
1855 raise exc.CompileError(
1856 "This compiled object has no original cache key; "
1857 "can't pass extracted_parameters to construct_params"
1858 )
1859 else:
1860 orig_extracted = self.cache_key[1]
1861
1862 ckbm_tuple = self._cache_key_bind_match
1863 assert ckbm_tuple is not None
1864 ckbm, _ = ckbm_tuple
1865 resolved_extracted = {
1866 bind: extracted
1867 for b, extracted in zip(orig_extracted, extracted_parameters)
1868 for bind in ckbm[b]
1869 }
1870 else:
1871 resolved_extracted = None
1872
1873 if params:
1874 pd = {}
1875 for bindparam, name in self.bind_names.items():
1876 escaped_name = (
1877 self.escaped_bind_names.get(name, name)
1878 if has_escaped_names
1879 else name
1880 )
1881
1882 if bindparam.key in params:
1883 pd[escaped_name] = params[bindparam.key]
1884 elif name in params:
1885 pd[escaped_name] = params[name]
1886
1887 elif _check and bindparam.required:
1888 if _group_number:
1889 raise exc.InvalidRequestError(
1890 "A value is required for bind parameter %r, "
1891 "in parameter group %d"
1892 % (bindparam.key, _group_number),
1893 code="cd3x",
1894 )
1895 else:
1896 raise exc.InvalidRequestError(
1897 "A value is required for bind parameter %r"
1898 % bindparam.key,
1899 code="cd3x",
1900 )
1901 else:
1902 if resolved_extracted:
1903 value_param = resolved_extracted.get(
1904 bindparam, bindparam
1905 )
1906 else:
1907 value_param = bindparam
1908
1909 if bindparam.callable:
1910 pd[escaped_name] = value_param.effective_value
1911 else:
1912 pd[escaped_name] = value_param.value
1913 return pd
1914 else:
1915 pd = {}
1916 for bindparam, name in self.bind_names.items():
1917 escaped_name = (
1918 self.escaped_bind_names.get(name, name)
1919 if has_escaped_names
1920 else name
1921 )
1922
1923 if _check and bindparam.required:
1924 if _group_number:
1925 raise exc.InvalidRequestError(
1926 "A value is required for bind parameter %r, "
1927 "in parameter group %d"
1928 % (bindparam.key, _group_number),
1929 code="cd3x",
1930 )
1931 else:
1932 raise exc.InvalidRequestError(
1933 "A value is required for bind parameter %r"
1934 % bindparam.key,
1935 code="cd3x",
1936 )
1937
1938 if resolved_extracted:
1939 value_param = resolved_extracted.get(bindparam, bindparam)
1940 else:
1941 value_param = bindparam
1942
1943 if bindparam.callable:
1944 pd[escaped_name] = value_param.effective_value
1945 else:
1946 pd[escaped_name] = value_param.value
1947
1948 return pd
1949
1950 @util.memoized_instancemethod
1951 def _get_set_input_sizes_lookup(self):
1952 dialect = self.dialect
1953
1954 include_types = dialect.include_set_input_sizes
1955 exclude_types = dialect.exclude_set_input_sizes
1956
1957 dbapi = dialect.dbapi
1958
1959 def lookup_type(typ):
1960 dbtype = typ._unwrapped_dialect_impl(dialect).get_dbapi_type(dbapi)
1961
1962 if (
1963 dbtype is not None
1964 and (exclude_types is None or dbtype not in exclude_types)
1965 and (include_types is None or dbtype in include_types)
1966 ):
1967 return dbtype
1968 else:
1969 return None
1970
1971 inputsizes = {}
1972
1973 literal_execute_params = self.literal_execute_params
1974
1975 for bindparam in self.bind_names:
1976 if bindparam in literal_execute_params:
1977 continue
1978
1979 if bindparam.type._is_tuple_type:
1980 inputsizes[bindparam] = [
1981 lookup_type(typ)
1982 for typ in cast(TupleType, bindparam.type).types
1983 ]
1984 else:
1985 inputsizes[bindparam] = lookup_type(bindparam.type)
1986
1987 return inputsizes
1988
1989 @property
1990 def params(self):
1991 """Return the bind param dictionary embedded into this
1992 compiled object, for those values that are present.
1993
1994 .. seealso::
1995
1996 :ref:`faq_sql_expression_string` - includes a usage example for
1997 debugging use cases.
1998
1999 """
2000 return self.construct_params(_check=False)
2001
2002 def _process_parameters_for_postcompile(
2003 self,
2004 parameters: _MutableCoreSingleExecuteParams,
2005 _populate_self: bool = False,
2006 ) -> ExpandedState:
2007 """handle special post compile parameters.
2008
2009 These include:
2010
2011 * "expanding" parameters -typically IN tuples that are rendered
2012 on a per-parameter basis for an otherwise fixed SQL statement string.
2013
2014 * literal_binds compiled with the literal_execute flag. Used for
2015 things like SQL Server "TOP N" where the driver does not accommodate
2016 N as a bound parameter.
2017
2018 """
2019
2020 expanded_parameters = {}
2021 new_positiontup: Optional[List[str]]
2022
2023 pre_expanded_string = self._pre_expanded_string
2024 if pre_expanded_string is None:
2025 pre_expanded_string = self.string
2026
2027 if self.positional:
2028 new_positiontup = []
2029
2030 pre_expanded_positiontup = self._pre_expanded_positiontup
2031 if pre_expanded_positiontup is None:
2032 pre_expanded_positiontup = self.positiontup
2033
2034 else:
2035 new_positiontup = pre_expanded_positiontup = None
2036
2037 processors = self._bind_processors
2038 single_processors = cast(
2039 "Mapping[str, _BindProcessorType[Any]]", processors
2040 )
2041 tuple_processors = cast(
2042 "Mapping[str, Sequence[_BindProcessorType[Any]]]", processors
2043 )
2044
2045 new_processors: Dict[str, _BindProcessorType[Any]] = {}
2046
2047 replacement_expressions: Dict[str, Any] = {}
2048 to_update_sets: Dict[str, Any] = {}
2049
2050 # notes:
2051 # *unescaped* parameter names in:
2052 # self.bind_names, self.binds, self._bind_processors, self.positiontup
2053 #
2054 # *escaped* parameter names in:
2055 # construct_params(), replacement_expressions
2056
2057 numeric_positiontup: Optional[List[str]] = None
2058
2059 if self.positional and pre_expanded_positiontup is not None:
2060 names: Iterable[str] = pre_expanded_positiontup
2061 if self._numeric_binds:
2062 numeric_positiontup = []
2063 else:
2064 names = self.bind_names.values()
2065
2066 ebn = self.escaped_bind_names
2067 for name in names:
2068 escaped_name = ebn.get(name, name) if ebn else name
2069 parameter = self.binds[name]
2070
2071 if parameter in self.literal_execute_params:
2072 if escaped_name not in replacement_expressions:
2073 replacement_expressions[escaped_name] = (
2074 self.render_literal_bindparam(
2075 parameter,
2076 render_literal_value=parameters.pop(escaped_name),
2077 )
2078 )
2079 continue
2080
2081 if parameter in self.post_compile_params:
2082 if escaped_name in replacement_expressions:
2083 to_update = to_update_sets[escaped_name]
2084 values = None
2085 else:
2086 # we are removing the parameter from parameters
2087 # because it is a list value, which is not expected by
2088 # TypeEngine objects that would otherwise be asked to
2089 # process it. the single name is being replaced with
2090 # individual numbered parameters for each value in the
2091 # param.
2092 #
2093 # note we are also inserting *escaped* parameter names
2094 # into the given dictionary. default dialect will
2095 # use these param names directly as they will not be
2096 # in the escaped_bind_names dictionary.
2097 values = parameters.pop(name)
2098
2099 leep_res = self._literal_execute_expanding_parameter(
2100 escaped_name, parameter, values
2101 )
2102 (to_update, replacement_expr) = leep_res
2103
2104 to_update_sets[escaped_name] = to_update
2105 replacement_expressions[escaped_name] = replacement_expr
2106
2107 if not parameter.literal_execute:
2108 parameters.update(to_update)
2109 if parameter.type._is_tuple_type:
2110 assert values is not None
2111 new_processors.update(
2112 (
2113 "%s_%s_%s" % (name, i, j),
2114 tuple_processors[name][j - 1],
2115 )
2116 for i, tuple_element in enumerate(values, 1)
2117 for j, _ in enumerate(tuple_element, 1)
2118 if name in tuple_processors
2119 and tuple_processors[name][j - 1] is not None
2120 )
2121 else:
2122 new_processors.update(
2123 (key, single_processors[name])
2124 for key, _ in to_update
2125 if name in single_processors
2126 )
2127 if numeric_positiontup is not None:
2128 numeric_positiontup.extend(
2129 name for name, _ in to_update
2130 )
2131 elif new_positiontup is not None:
2132 # to_update has escaped names, but that's ok since
2133 # these are new names, that aren't in the
2134 # escaped_bind_names dict.
2135 new_positiontup.extend(name for name, _ in to_update)
2136 expanded_parameters[name] = [
2137 expand_key for expand_key, _ in to_update
2138 ]
2139 elif new_positiontup is not None:
2140 new_positiontup.append(name)
2141
2142 def process_expanding(m):
2143 key = m.group(1)
2144 expr = replacement_expressions[key]
2145
2146 # if POSTCOMPILE included a bind_expression, render that
2147 # around each element
2148 if m.group(2):
2149 tok = m.group(2).split("~~")
2150 be_left, be_right = tok[1], tok[3]
2151 expr = ", ".join(
2152 "%s%s%s" % (be_left, exp, be_right)
2153 for exp in expr.split(", ")
2154 )
2155 return expr
2156
2157 statement = re.sub(
2158 self._post_compile_pattern, process_expanding, pre_expanded_string
2159 )
2160
2161 if numeric_positiontup is not None:
2162 assert new_positiontup is not None
2163 param_pos = {
2164 key: f"{self._numeric_binds_identifier_char}{num}"
2165 for num, key in enumerate(
2166 numeric_positiontup, self.next_numeric_pos
2167 )
2168 }
2169 # Can't use format here since % chars are not escaped.
2170 statement = self._pyformat_pattern.sub(
2171 lambda m: param_pos[m.group(1)], statement
2172 )
2173 new_positiontup.extend(numeric_positiontup)
2174
2175 expanded_state = ExpandedState(
2176 statement,
2177 parameters,
2178 new_processors,
2179 new_positiontup,
2180 expanded_parameters,
2181 )
2182
2183 if _populate_self:
2184 # this is for the "render_postcompile" flag, which is not
2185 # otherwise used internally and is for end-user debugging and
2186 # special use cases.
2187 self._pre_expanded_string = pre_expanded_string
2188 self._pre_expanded_positiontup = pre_expanded_positiontup
2189 self.string = expanded_state.statement
2190 self.positiontup = (
2191 list(expanded_state.positiontup or ())
2192 if self.positional
2193 else None
2194 )
2195 self._post_compile_expanded_state = expanded_state
2196
2197 return expanded_state
2198
2199 @util.preload_module("sqlalchemy.engine.cursor")
2200 def _create_result_map(self):
2201 """utility method used for unit tests only."""
2202 cursor = util.preloaded.engine_cursor
2203 return cursor.CursorResultMetaData._create_description_match_map(
2204 self._result_columns
2205 )
2206
2207 # assigned by crud.py for insert/update statements
2208 _get_bind_name_for_col: _BindNameForColProtocol
2209
2210 @util.memoized_property
2211 def _within_exec_param_key_getter(self) -> Callable[[Any], str]:
2212 getter = self._get_bind_name_for_col
2213 return getter
2214
2215 @util.memoized_property
2216 @util.preload_module("sqlalchemy.engine.result")
2217 def _inserted_primary_key_from_lastrowid_getter(self):
2218 result = util.preloaded.engine_result
2219
2220 param_key_getter = self._within_exec_param_key_getter
2221
2222 assert self.compile_state is not None
2223 statement = self.compile_state.statement
2224
2225 if TYPE_CHECKING:
2226 assert isinstance(statement, Insert)
2227
2228 table = statement.table
2229
2230 getters = [
2231 (operator.methodcaller("get", param_key_getter(col), None), col)
2232 for col in table.primary_key
2233 ]
2234
2235 autoinc_getter = None
2236 autoinc_col = table._autoincrement_column
2237 if autoinc_col is not None:
2238 # apply type post processors to the lastrowid
2239 lastrowid_processor = autoinc_col.type._cached_result_processor(
2240 self.dialect, None
2241 )
2242 autoinc_key = param_key_getter(autoinc_col)
2243
2244 # if a bind value is present for the autoincrement column
2245 # in the parameters, we need to do the logic dictated by
2246 # #7998; honor a non-None user-passed parameter over lastrowid.
2247 # previously in the 1.4 series we weren't fetching lastrowid
2248 # at all if the key were present in the parameters
2249 if autoinc_key in self.binds:
2250
2251 def _autoinc_getter(lastrowid, parameters):
2252 param_value = parameters.get(autoinc_key, lastrowid)
2253 if param_value is not None:
2254 # they supplied non-None parameter, use that.
2255 # SQLite at least is observed to return the wrong
2256 # cursor.lastrowid for INSERT..ON CONFLICT so it
2257 # can't be used in all cases
2258 return param_value
2259 else:
2260 # use lastrowid
2261 return lastrowid
2262
2263 # work around mypy https://github.com/python/mypy/issues/14027
2264 autoinc_getter = _autoinc_getter
2265
2266 else:
2267 lastrowid_processor = None
2268
2269 row_fn = result.result_tuple([col.key for col in table.primary_key])
2270
2271 def get(lastrowid, parameters):
2272 """given cursor.lastrowid value and the parameters used for INSERT,
2273 return a "row" that represents the primary key, either by
2274 using the "lastrowid" or by extracting values from the parameters
2275 that were sent along with the INSERT.
2276
2277 """
2278 if lastrowid_processor is not None:
2279 lastrowid = lastrowid_processor(lastrowid)
2280
2281 if lastrowid is None:
2282 return row_fn(getter(parameters) for getter, col in getters)
2283 else:
2284 return row_fn(
2285 (
2286 (
2287 autoinc_getter(lastrowid, parameters)
2288 if autoinc_getter is not None
2289 else lastrowid
2290 )
2291 if col is autoinc_col
2292 else getter(parameters)
2293 )
2294 for getter, col in getters
2295 )
2296
2297 return get
2298
2299 @util.memoized_property
2300 @util.preload_module("sqlalchemy.engine.result")
2301 def _inserted_primary_key_from_returning_getter(self):
2302 if typing.TYPE_CHECKING:
2303 from ..engine import result
2304 else:
2305 result = util.preloaded.engine_result
2306
2307 assert self.compile_state is not None
2308 statement = self.compile_state.statement
2309
2310 if TYPE_CHECKING:
2311 assert isinstance(statement, Insert)
2312
2313 param_key_getter = self._within_exec_param_key_getter
2314 table = statement.table
2315
2316 returning = self.implicit_returning
2317 assert returning is not None
2318 ret = {col: idx for idx, col in enumerate(returning)}
2319
2320 getters = cast(
2321 "List[Tuple[Callable[[Any], Any], bool]]",
2322 [
2323 (
2324 (operator.itemgetter(ret[col]), True)
2325 if col in ret
2326 else (
2327 operator.methodcaller(
2328 "get", param_key_getter(col), None
2329 ),
2330 False,
2331 )
2332 )
2333 for col in table.primary_key
2334 ],
2335 )
2336
2337 row_fn = result.result_tuple([col.key for col in table.primary_key])
2338
2339 def get(row, parameters):
2340 return row_fn(
2341 getter(row) if use_row else getter(parameters)
2342 for getter, use_row in getters
2343 )
2344
2345 return get
2346
2347 def default_from(self):
2348 """Called when a SELECT statement has no froms, and no FROM clause is
2349 to be appended.
2350
2351 Gives Oracle a chance to tack on a ``FROM DUAL`` to the string output.
2352
2353 """
2354 return ""
2355
2356 def visit_override_binds(self, override_binds, **kw):
2357 """SQL compile the nested element of an _OverrideBinds with
2358 bindparams swapped out.
2359
2360 The _OverrideBinds is not normally expected to be compiled; it
2361 is meant to be used when an already cached statement is to be used,
2362 the compilation was already performed, and only the bound params should
2363 be swapped in at execution time.
2364
2365 However, there are test cases that exericise this object, and
2366 additionally the ORM subquery loader is known to feed in expressions
2367 which include this construct into new queries (discovered in #11173),
2368 so it has to do the right thing at compile time as well.
2369
2370 """
2371
2372 # get SQL text first
2373 sqltext = override_binds.element._compiler_dispatch(self, **kw)
2374
2375 # for a test compile that is not for caching, change binds after the
2376 # fact. note that we don't try to
2377 # swap the bindparam as we compile, because our element may be
2378 # elsewhere in the statement already (e.g. a subquery or perhaps a
2379 # CTE) and was already visited / compiled. See
2380 # test_relationship_criteria.py ->
2381 # test_selectinload_local_criteria_subquery
2382 for k in override_binds.translate:
2383 if k not in self.binds:
2384 continue
2385 bp = self.binds[k]
2386
2387 # so this would work, just change the value of bp in place.
2388 # but we dont want to mutate things outside.
2389 # bp.value = override_binds.translate[bp.key]
2390 # continue
2391
2392 # instead, need to replace bp with new_bp or otherwise accommodate
2393 # in all internal collections
2394 new_bp = bp._with_value(
2395 override_binds.translate[bp.key],
2396 maintain_key=True,
2397 required=False,
2398 )
2399
2400 name = self.bind_names[bp]
2401 self.binds[k] = self.binds[name] = new_bp
2402 self.bind_names[new_bp] = name
2403 self.bind_names.pop(bp, None)
2404
2405 if bp in self.post_compile_params:
2406 self.post_compile_params |= {new_bp}
2407 if bp in self.literal_execute_params:
2408 self.literal_execute_params |= {new_bp}
2409
2410 ckbm_tuple = self._cache_key_bind_match
2411 if ckbm_tuple:
2412 ckbm, cksm = ckbm_tuple
2413 for bp in bp._cloned_set:
2414 if bp.key in cksm:
2415 cb = cksm[bp.key]
2416 ckbm[cb].append(new_bp)
2417
2418 return sqltext
2419
2420 def visit_grouping(self, grouping, asfrom=False, **kwargs):
2421 return "(" + grouping.element._compiler_dispatch(self, **kwargs) + ")"
2422
2423 def visit_select_statement_grouping(self, grouping, **kwargs):
2424 return "(" + grouping.element._compiler_dispatch(self, **kwargs) + ")"
2425
2426 def visit_label_reference(
2427 self, element, within_columns_clause=False, **kwargs
2428 ):
2429 if self.stack and self.dialect.supports_simple_order_by_label:
2430 try:
2431 compile_state = cast(
2432 "Union[SelectState, CompoundSelectState]",
2433 self.stack[-1]["compile_state"],
2434 )
2435 except KeyError as ke:
2436 raise exc.CompileError(
2437 "Can't resolve label reference for ORDER BY / "
2438 "GROUP BY / DISTINCT etc."
2439 ) from ke
2440
2441 (
2442 with_cols,
2443 only_froms,
2444 only_cols,
2445 ) = compile_state._label_resolve_dict
2446 if within_columns_clause:
2447 resolve_dict = only_froms
2448 else:
2449 resolve_dict = only_cols
2450
2451 # this can be None in the case that a _label_reference()
2452 # were subject to a replacement operation, in which case
2453 # the replacement of the Label element may have changed
2454 # to something else like a ColumnClause expression.
2455 order_by_elem = element.element._order_by_label_element
2456
2457 if (
2458 order_by_elem is not None
2459 and order_by_elem.name in resolve_dict
2460 and order_by_elem.shares_lineage(
2461 resolve_dict[order_by_elem.name]
2462 )
2463 ):
2464 kwargs["render_label_as_label"] = (
2465 element.element._order_by_label_element
2466 )
2467 return self.process(
2468 element.element,
2469 within_columns_clause=within_columns_clause,
2470 **kwargs,
2471 )
2472
2473 def visit_textual_label_reference(
2474 self, element, within_columns_clause=False, **kwargs
2475 ):
2476 if not self.stack:
2477 # compiling the element outside of the context of a SELECT
2478 return self.process(element._text_clause)
2479
2480 try:
2481 compile_state = cast(
2482 "Union[SelectState, CompoundSelectState]",
2483 self.stack[-1]["compile_state"],
2484 )
2485 except KeyError as ke:
2486 coercions._no_text_coercion(
2487 element.element,
2488 extra=(
2489 "Can't resolve label reference for ORDER BY / "
2490 "GROUP BY / DISTINCT etc."
2491 ),
2492 exc_cls=exc.CompileError,
2493 err=ke,
2494 )
2495
2496 with_cols, only_froms, only_cols = compile_state._label_resolve_dict
2497 try:
2498 if within_columns_clause:
2499 col = only_froms[element.element]
2500 else:
2501 col = with_cols[element.element]
2502 except KeyError as err:
2503 coercions._no_text_coercion(
2504 element.element,
2505 extra=(
2506 "Can't resolve label reference for ORDER BY / "
2507 "GROUP BY / DISTINCT etc."
2508 ),
2509 exc_cls=exc.CompileError,
2510 err=err,
2511 )
2512 else:
2513 kwargs["render_label_as_label"] = col
2514 return self.process(
2515 col, within_columns_clause=within_columns_clause, **kwargs
2516 )
2517
2518 def visit_label(
2519 self,
2520 label,
2521 add_to_result_map=None,
2522 within_label_clause=False,
2523 within_columns_clause=False,
2524 render_label_as_label=None,
2525 result_map_targets=(),
2526 **kw,
2527 ):
2528 # only render labels within the columns clause
2529 # or ORDER BY clause of a select. dialect-specific compilers
2530 # can modify this behavior.
2531 render_label_with_as = (
2532 within_columns_clause and not within_label_clause
2533 )
2534 render_label_only = render_label_as_label is label
2535
2536 if render_label_only or render_label_with_as:
2537 if isinstance(label.name, elements._truncated_label):
2538 labelname = self._truncated_identifier("colident", label.name)
2539 else:
2540 labelname = label.name
2541
2542 if render_label_with_as:
2543 if add_to_result_map is not None:
2544 add_to_result_map(
2545 labelname,
2546 label.name,
2547 (label, labelname) + label._alt_names + result_map_targets,
2548 label.type,
2549 )
2550 return (
2551 label.element._compiler_dispatch(
2552 self,
2553 within_columns_clause=True,
2554 within_label_clause=True,
2555 **kw,
2556 )
2557 + OPERATORS[operators.as_]
2558 + self.preparer.format_label(label, labelname)
2559 )
2560 elif render_label_only:
2561 return self.preparer.format_label(label, labelname)
2562 else:
2563 return label.element._compiler_dispatch(
2564 self, within_columns_clause=False, **kw
2565 )
2566
2567 def _fallback_column_name(self, column):
2568 raise exc.CompileError(
2569 "Cannot compile Column object until its 'name' is assigned."
2570 )
2571
2572 def visit_lambda_element(self, element, **kw):
2573 sql_element = element._resolved
2574 return self.process(sql_element, **kw)
2575
2576 def visit_column(
2577 self,
2578 column: ColumnClause[Any],
2579 add_to_result_map: Optional[_ResultMapAppender] = None,
2580 include_table: bool = True,
2581 result_map_targets: Tuple[Any, ...] = (),
2582 ambiguous_table_name_map: Optional[_AmbiguousTableNameMap] = None,
2583 **kwargs: Any,
2584 ) -> str:
2585 name = orig_name = column.name
2586 if name is None:
2587 name = self._fallback_column_name(column)
2588
2589 is_literal = column.is_literal
2590 if not is_literal and isinstance(name, elements._truncated_label):
2591 name = self._truncated_identifier("colident", name)
2592
2593 if add_to_result_map is not None:
2594 targets = (column, name, column.key) + result_map_targets
2595 if column._tq_label:
2596 targets += (column._tq_label,)
2597
2598 add_to_result_map(name, orig_name, targets, column.type)
2599
2600 if is_literal:
2601 # note we are not currently accommodating for
2602 # literal_column(quoted_name('ident', True)) here
2603 name = self.escape_literal_column(name)
2604 else:
2605 name = self.preparer.quote(name)
2606 table = column.table
2607 if table is None or not include_table or not table.named_with_column:
2608 return name
2609 else:
2610 effective_schema = self.preparer.schema_for_object(table)
2611
2612 if effective_schema:
2613 schema_prefix = (
2614 self.preparer.quote_schema(effective_schema) + "."
2615 )
2616 else:
2617 schema_prefix = ""
2618
2619 if TYPE_CHECKING:
2620 assert isinstance(table, NamedFromClause)
2621 tablename = table.name
2622
2623 if (
2624 not effective_schema
2625 and ambiguous_table_name_map
2626 and tablename in ambiguous_table_name_map
2627 ):
2628 tablename = ambiguous_table_name_map[tablename]
2629
2630 if isinstance(tablename, elements._truncated_label):
2631 tablename = self._truncated_identifier("alias", tablename)
2632
2633 return schema_prefix + self.preparer.quote(tablename) + "." + name
2634
2635 def visit_collation(self, element, **kw):
2636 return self.preparer.format_collation(element.collation)
2637
2638 def visit_fromclause(self, fromclause, **kwargs):
2639 return fromclause.name
2640
2641 def visit_index(self, index, **kwargs):
2642 return index.name
2643
2644 def visit_typeclause(self, typeclause, **kw):
2645 kw["type_expression"] = typeclause
2646 kw["identifier_preparer"] = self.preparer
2647 return self.dialect.type_compiler_instance.process(
2648 typeclause.type, **kw
2649 )
2650
2651 def post_process_text(self, text):
2652 if self.preparer._double_percents:
2653 text = text.replace("%", "%%")
2654 return text
2655
2656 def escape_literal_column(self, text):
2657 if self.preparer._double_percents:
2658 text = text.replace("%", "%%")
2659 return text
2660
2661 def visit_textclause(self, textclause, add_to_result_map=None, **kw):
2662 def do_bindparam(m):
2663 name = m.group(1)
2664 if name in textclause._bindparams:
2665 return self.process(textclause._bindparams[name], **kw)
2666 else:
2667 return self.bindparam_string(name, **kw)
2668
2669 if not self.stack:
2670 self.isplaintext = True
2671
2672 if add_to_result_map:
2673 # text() object is present in the columns clause of a
2674 # select(). Add a no-name entry to the result map so that
2675 # row[text()] produces a result
2676 add_to_result_map(None, None, (textclause,), sqltypes.NULLTYPE)
2677
2678 # un-escape any \:params
2679 return BIND_PARAMS_ESC.sub(
2680 lambda m: m.group(1),
2681 BIND_PARAMS.sub(
2682 do_bindparam, self.post_process_text(textclause.text)
2683 ),
2684 )
2685
2686 def visit_textual_select(
2687 self, taf, compound_index=None, asfrom=False, **kw
2688 ):
2689 toplevel = not self.stack
2690 entry = self._default_stack_entry if toplevel else self.stack[-1]
2691
2692 new_entry: _CompilerStackEntry = {
2693 "correlate_froms": set(),
2694 "asfrom_froms": set(),
2695 "selectable": taf,
2696 }
2697 self.stack.append(new_entry)
2698
2699 if taf._independent_ctes:
2700 self._dispatch_independent_ctes(taf, kw)
2701
2702 populate_result_map = (
2703 toplevel
2704 or (
2705 compound_index == 0
2706 and entry.get("need_result_map_for_compound", False)
2707 )
2708 or entry.get("need_result_map_for_nested", False)
2709 )
2710
2711 if populate_result_map:
2712 self._ordered_columns = self._textual_ordered_columns = (
2713 taf.positional
2714 )
2715
2716 # enable looser result column matching when the SQL text links to
2717 # Column objects by name only
2718 self._loose_column_name_matching = not taf.positional and bool(
2719 taf.column_args
2720 )
2721
2722 for c in taf.column_args:
2723 self.process(
2724 c,
2725 within_columns_clause=True,
2726 add_to_result_map=self._add_to_result_map,
2727 )
2728
2729 text = self.process(taf.element, **kw)
2730 if self.ctes:
2731 nesting_level = len(self.stack) if not toplevel else None
2732 text = self._render_cte_clause(nesting_level=nesting_level) + text
2733
2734 self.stack.pop(-1)
2735
2736 return text
2737
2738 def visit_null(self, expr, **kw):
2739 return "NULL"
2740
2741 def visit_true(self, expr, **kw):
2742 if self.dialect.supports_native_boolean:
2743 return "true"
2744 else:
2745 return "1"
2746
2747 def visit_false(self, expr, **kw):
2748 if self.dialect.supports_native_boolean:
2749 return "false"
2750 else:
2751 return "0"
2752
2753 def _generate_delimited_list(self, elements, separator, **kw):
2754 return separator.join(
2755 s
2756 for s in (c._compiler_dispatch(self, **kw) for c in elements)
2757 if s
2758 )
2759
2760 def _generate_delimited_and_list(self, clauses, **kw):
2761 lcc, clauses = elements.BooleanClauseList._process_clauses_for_boolean(
2762 operators.and_,
2763 elements.True_._singleton,
2764 elements.False_._singleton,
2765 clauses,
2766 )
2767 if lcc == 1:
2768 return clauses[0]._compiler_dispatch(self, **kw)
2769 else:
2770 separator = OPERATORS[operators.and_]
2771 return separator.join(
2772 s
2773 for s in (c._compiler_dispatch(self, **kw) for c in clauses)
2774 if s
2775 )
2776
2777 def visit_tuple(self, clauselist, **kw):
2778 return "(%s)" % self.visit_clauselist(clauselist, **kw)
2779
2780 def visit_clauselist(self, clauselist, **kw):
2781 sep = clauselist.operator
2782 if sep is None:
2783 sep = " "
2784 else:
2785 sep = OPERATORS[clauselist.operator]
2786
2787 return self._generate_delimited_list(clauselist.clauses, sep, **kw)
2788
2789 def visit_expression_clauselist(self, clauselist, **kw):
2790 operator_ = clauselist.operator
2791
2792 disp = self._get_operator_dispatch(
2793 operator_, "expression_clauselist", None
2794 )
2795 if disp:
2796 return disp(clauselist, operator_, **kw)
2797
2798 try:
2799 opstring = OPERATORS[operator_]
2800 except KeyError as err:
2801 raise exc.UnsupportedCompilationError(self, operator_) from err
2802 else:
2803 kw["_in_operator_expression"] = True
2804 return self._generate_delimited_list(
2805 clauselist.clauses, opstring, **kw
2806 )
2807
2808 def visit_case(self, clause, **kwargs):
2809 x = "CASE "
2810 if clause.value is not None:
2811 x += clause.value._compiler_dispatch(self, **kwargs) + " "
2812 for cond, result in clause.whens:
2813 x += (
2814 "WHEN "
2815 + cond._compiler_dispatch(self, **kwargs)
2816 + " THEN "
2817 + result._compiler_dispatch(self, **kwargs)
2818 + " "
2819 )
2820 if clause.else_ is not None:
2821 x += (
2822 "ELSE " + clause.else_._compiler_dispatch(self, **kwargs) + " "
2823 )
2824 x += "END"
2825 return x
2826
2827 def visit_type_coerce(self, type_coerce, **kw):
2828 return type_coerce.typed_expression._compiler_dispatch(self, **kw)
2829
2830 def visit_cast(self, cast, **kwargs):
2831 type_clause = cast.typeclause._compiler_dispatch(self, **kwargs)
2832 match = re.match("(.*)( COLLATE .*)", type_clause)
2833 return "CAST(%s AS %s)%s" % (
2834 cast.clause._compiler_dispatch(self, **kwargs),
2835 match.group(1) if match else type_clause,
2836 match.group(2) if match else "",
2837 )
2838
2839 def visit_frame_clause(self, frameclause, **kw):
2840
2841 if frameclause.lower_type is elements._FrameClauseType.RANGE_UNBOUNDED:
2842 left = "UNBOUNDED PRECEDING"
2843 elif frameclause.lower_type is elements._FrameClauseType.RANGE_CURRENT:
2844 left = "CURRENT ROW"
2845 else:
2846 val = self.process(frameclause.lower_integer_bind, **kw)
2847 if (
2848 frameclause.lower_type
2849 is elements._FrameClauseType.RANGE_PRECEDING
2850 ):
2851 left = f"{val} PRECEDING"
2852 else:
2853 left = f"{val} FOLLOWING"
2854
2855 if frameclause.upper_type is elements._FrameClauseType.RANGE_UNBOUNDED:
2856 right = "UNBOUNDED FOLLOWING"
2857 elif frameclause.upper_type is elements._FrameClauseType.RANGE_CURRENT:
2858 right = "CURRENT ROW"
2859 else:
2860 val = self.process(frameclause.upper_integer_bind, **kw)
2861 if (
2862 frameclause.upper_type
2863 is elements._FrameClauseType.RANGE_PRECEDING
2864 ):
2865 right = f"{val} PRECEDING"
2866 else:
2867 right = f"{val} FOLLOWING"
2868
2869 return f"{left} AND {right}"
2870
2871 def visit_over(self, over, **kwargs):
2872 text = over.element._compiler_dispatch(self, **kwargs)
2873 if over.range_ is not None:
2874 range_ = f"RANGE BETWEEN {self.process(over.range_, **kwargs)}"
2875 elif over.rows is not None:
2876 range_ = f"ROWS BETWEEN {self.process(over.rows, **kwargs)}"
2877 else:
2878 range_ = None
2879
2880 return "%s OVER (%s)" % (
2881 text,
2882 " ".join(
2883 [
2884 "%s BY %s"
2885 % (word, clause._compiler_dispatch(self, **kwargs))
2886 for word, clause in (
2887 ("PARTITION", over.partition_by),
2888 ("ORDER", over.order_by),
2889 )
2890 if clause is not None and len(clause)
2891 ]
2892 + ([range_] if range_ else [])
2893 ),
2894 )
2895
2896 def visit_withingroup(self, withingroup, **kwargs):
2897 return "%s WITHIN GROUP (ORDER BY %s)" % (
2898 withingroup.element._compiler_dispatch(self, **kwargs),
2899 withingroup.order_by._compiler_dispatch(self, **kwargs),
2900 )
2901
2902 def visit_funcfilter(self, funcfilter, **kwargs):
2903 return "%s FILTER (WHERE %s)" % (
2904 funcfilter.func._compiler_dispatch(self, **kwargs),
2905 funcfilter.criterion._compiler_dispatch(self, **kwargs),
2906 )
2907
2908 def visit_extract(self, extract, **kwargs):
2909 field = self.extract_map.get(extract.field, extract.field)
2910 return "EXTRACT(%s FROM %s)" % (
2911 field,
2912 extract.expr._compiler_dispatch(self, **kwargs),
2913 )
2914
2915 def visit_scalar_function_column(self, element, **kw):
2916 compiled_fn = self.visit_function(element.fn, **kw)
2917 compiled_col = self.visit_column(element, **kw)
2918 return "(%s).%s" % (compiled_fn, compiled_col)
2919
2920 def visit_function(
2921 self,
2922 func: Function[Any],
2923 add_to_result_map: Optional[_ResultMapAppender] = None,
2924 **kwargs: Any,
2925 ) -> str:
2926 if add_to_result_map is not None:
2927 add_to_result_map(func.name, func.name, (func.name,), func.type)
2928
2929 disp = getattr(self, "visit_%s_func" % func.name.lower(), None)
2930
2931 text: str
2932
2933 if disp:
2934 text = disp(func, **kwargs)
2935 else:
2936 name = FUNCTIONS.get(func._deannotate().__class__, None)
2937 if name:
2938 if func._has_args:
2939 name += "%(expr)s"
2940 else:
2941 name = func.name
2942 name = (
2943 self.preparer.quote(name)
2944 if self.preparer._requires_quotes_illegal_chars(name)
2945 or isinstance(name, elements.quoted_name)
2946 else name
2947 )
2948 name = name + "%(expr)s"
2949 text = ".".join(
2950 [
2951 (
2952 self.preparer.quote(tok)
2953 if self.preparer._requires_quotes_illegal_chars(tok)
2954 or isinstance(name, elements.quoted_name)
2955 else tok
2956 )
2957 for tok in func.packagenames
2958 ]
2959 + [name]
2960 ) % {"expr": self.function_argspec(func, **kwargs)}
2961
2962 if func._with_ordinality:
2963 text += " WITH ORDINALITY"
2964 return text
2965
2966 def visit_next_value_func(self, next_value, **kw):
2967 return self.visit_sequence(next_value.sequence)
2968
2969 def visit_sequence(self, sequence, **kw):
2970 raise NotImplementedError(
2971 "Dialect '%s' does not support sequence increments."
2972 % self.dialect.name
2973 )
2974
2975 def function_argspec(self, func, **kwargs):
2976 return func.clause_expr._compiler_dispatch(self, **kwargs)
2977
2978 def visit_compound_select(
2979 self, cs, asfrom=False, compound_index=None, **kwargs
2980 ):
2981 toplevel = not self.stack
2982
2983 compile_state = cs._compile_state_factory(cs, self, **kwargs)
2984
2985 if toplevel and not self.compile_state:
2986 self.compile_state = compile_state
2987
2988 compound_stmt = compile_state.statement
2989
2990 entry = self._default_stack_entry if toplevel else self.stack[-1]
2991 need_result_map = toplevel or (
2992 not compound_index
2993 and entry.get("need_result_map_for_compound", False)
2994 )
2995
2996 # indicates there is already a CompoundSelect in play
2997 if compound_index == 0:
2998 entry["select_0"] = cs
2999
3000 self.stack.append(
3001 {
3002 "correlate_froms": entry["correlate_froms"],
3003 "asfrom_froms": entry["asfrom_froms"],
3004 "selectable": cs,
3005 "compile_state": compile_state,
3006 "need_result_map_for_compound": need_result_map,
3007 }
3008 )
3009
3010 if compound_stmt._independent_ctes:
3011 self._dispatch_independent_ctes(compound_stmt, kwargs)
3012
3013 keyword = self.compound_keywords[cs.keyword]
3014
3015 text = (" " + keyword + " ").join(
3016 (
3017 c._compiler_dispatch(
3018 self, asfrom=asfrom, compound_index=i, **kwargs
3019 )
3020 for i, c in enumerate(cs.selects)
3021 )
3022 )
3023
3024 kwargs["include_table"] = False
3025 text += self.group_by_clause(cs, **dict(asfrom=asfrom, **kwargs))
3026 text += self.order_by_clause(cs, **kwargs)
3027 if cs._has_row_limiting_clause:
3028 text += self._row_limit_clause(cs, **kwargs)
3029
3030 if self.ctes:
3031 nesting_level = len(self.stack) if not toplevel else None
3032 text = (
3033 self._render_cte_clause(
3034 nesting_level=nesting_level,
3035 include_following_stack=True,
3036 )
3037 + text
3038 )
3039
3040 self.stack.pop(-1)
3041 return text
3042
3043 def _row_limit_clause(self, cs, **kwargs):
3044 if cs._fetch_clause is not None:
3045 return self.fetch_clause(cs, **kwargs)
3046 else:
3047 return self.limit_clause(cs, **kwargs)
3048
3049 def _get_operator_dispatch(self, operator_, qualifier1, qualifier2):
3050 attrname = "visit_%s_%s%s" % (
3051 operator_.__name__,
3052 qualifier1,
3053 "_" + qualifier2 if qualifier2 else "",
3054 )
3055 return getattr(self, attrname, None)
3056
3057 def visit_unary(
3058 self, unary, add_to_result_map=None, result_map_targets=(), **kw
3059 ):
3060 if add_to_result_map is not None:
3061 result_map_targets += (unary,)
3062 kw["add_to_result_map"] = add_to_result_map
3063 kw["result_map_targets"] = result_map_targets
3064
3065 if unary.operator:
3066 if unary.modifier:
3067 raise exc.CompileError(
3068 "Unary expression does not support operator "
3069 "and modifier simultaneously"
3070 )
3071 disp = self._get_operator_dispatch(
3072 unary.operator, "unary", "operator"
3073 )
3074 if disp:
3075 return disp(unary, unary.operator, **kw)
3076 else:
3077 return self._generate_generic_unary_operator(
3078 unary, OPERATORS[unary.operator], **kw
3079 )
3080 elif unary.modifier:
3081 disp = self._get_operator_dispatch(
3082 unary.modifier, "unary", "modifier"
3083 )
3084 if disp:
3085 return disp(unary, unary.modifier, **kw)
3086 else:
3087 return self._generate_generic_unary_modifier(
3088 unary, OPERATORS[unary.modifier], **kw
3089 )
3090 else:
3091 raise exc.CompileError(
3092 "Unary expression has no operator or modifier"
3093 )
3094
3095 def visit_truediv_binary(self, binary, operator, **kw):
3096 if self.dialect.div_is_floordiv:
3097 return (
3098 self.process(binary.left, **kw)
3099 + " / "
3100 # TODO: would need a fast cast again here,
3101 # unless we want to use an implicit cast like "+ 0.0"
3102 + self.process(
3103 elements.Cast(
3104 binary.right,
3105 (
3106 binary.right.type
3107 if binary.right.type._type_affinity
3108 is sqltypes.Numeric
3109 else sqltypes.Numeric()
3110 ),
3111 ),
3112 **kw,
3113 )
3114 )
3115 else:
3116 return (
3117 self.process(binary.left, **kw)
3118 + " / "
3119 + self.process(binary.right, **kw)
3120 )
3121
3122 def visit_floordiv_binary(self, binary, operator, **kw):
3123 if (
3124 self.dialect.div_is_floordiv
3125 and binary.right.type._type_affinity is sqltypes.Integer
3126 ):
3127 return (
3128 self.process(binary.left, **kw)
3129 + " / "
3130 + self.process(binary.right, **kw)
3131 )
3132 else:
3133 return "FLOOR(%s)" % (
3134 self.process(binary.left, **kw)
3135 + " / "
3136 + self.process(binary.right, **kw)
3137 )
3138
3139 def visit_is_true_unary_operator(self, element, operator, **kw):
3140 if (
3141 element._is_implicitly_boolean
3142 or self.dialect.supports_native_boolean
3143 ):
3144 return self.process(element.element, **kw)
3145 else:
3146 return "%s = 1" % self.process(element.element, **kw)
3147
3148 def visit_is_false_unary_operator(self, element, operator, **kw):
3149 if (
3150 element._is_implicitly_boolean
3151 or self.dialect.supports_native_boolean
3152 ):
3153 return "NOT %s" % self.process(element.element, **kw)
3154 else:
3155 return "%s = 0" % self.process(element.element, **kw)
3156
3157 def visit_not_match_op_binary(self, binary, operator, **kw):
3158 return "NOT %s" % self.visit_binary(
3159 binary, override_operator=operators.match_op
3160 )
3161
3162 def visit_not_in_op_binary(self, binary, operator, **kw):
3163 # The brackets are required in the NOT IN operation because the empty
3164 # case is handled using the form "(col NOT IN (null) OR 1 = 1)".
3165 # The presence of the OR makes the brackets required.
3166 return "(%s)" % self._generate_generic_binary(
3167 binary, OPERATORS[operator], **kw
3168 )
3169
3170 def visit_empty_set_op_expr(self, type_, expand_op, **kw):
3171 if expand_op is operators.not_in_op:
3172 if len(type_) > 1:
3173 return "(%s)) OR (1 = 1" % (
3174 ", ".join("NULL" for element in type_)
3175 )
3176 else:
3177 return "NULL) OR (1 = 1"
3178 elif expand_op is operators.in_op:
3179 if len(type_) > 1:
3180 return "(%s)) AND (1 != 1" % (
3181 ", ".join("NULL" for element in type_)
3182 )
3183 else:
3184 return "NULL) AND (1 != 1"
3185 else:
3186 return self.visit_empty_set_expr(type_)
3187
3188 def visit_empty_set_expr(self, element_types, **kw):
3189 raise NotImplementedError(
3190 "Dialect '%s' does not support empty set expression."
3191 % self.dialect.name
3192 )
3193
3194 def _literal_execute_expanding_parameter_literal_binds(
3195 self, parameter, values, bind_expression_template=None
3196 ):
3197 typ_dialect_impl = parameter.type._unwrapped_dialect_impl(self.dialect)
3198
3199 if not values:
3200 # empty IN expression. note we don't need to use
3201 # bind_expression_template here because there are no
3202 # expressions to render.
3203
3204 if typ_dialect_impl._is_tuple_type:
3205 replacement_expression = (
3206 "VALUES " if self.dialect.tuple_in_values else ""
3207 ) + self.visit_empty_set_op_expr(
3208 parameter.type.types, parameter.expand_op
3209 )
3210
3211 else:
3212 replacement_expression = self.visit_empty_set_op_expr(
3213 [parameter.type], parameter.expand_op
3214 )
3215
3216 elif typ_dialect_impl._is_tuple_type or (
3217 typ_dialect_impl._isnull
3218 and isinstance(values[0], collections_abc.Sequence)
3219 and not isinstance(values[0], (str, bytes))
3220 ):
3221 if typ_dialect_impl._has_bind_expression:
3222 raise NotImplementedError(
3223 "bind_expression() on TupleType not supported with "
3224 "literal_binds"
3225 )
3226
3227 replacement_expression = (
3228 "VALUES " if self.dialect.tuple_in_values else ""
3229 ) + ", ".join(
3230 "(%s)"
3231 % (
3232 ", ".join(
3233 self.render_literal_value(value, param_type)
3234 for value, param_type in zip(
3235 tuple_element, parameter.type.types
3236 )
3237 )
3238 )
3239 for i, tuple_element in enumerate(values)
3240 )
3241 else:
3242 if bind_expression_template:
3243 post_compile_pattern = self._post_compile_pattern
3244 m = post_compile_pattern.search(bind_expression_template)
3245 assert m and m.group(
3246 2
3247 ), "unexpected format for expanding parameter"
3248
3249 tok = m.group(2).split("~~")
3250 be_left, be_right = tok[1], tok[3]
3251 replacement_expression = ", ".join(
3252 "%s%s%s"
3253 % (
3254 be_left,
3255 self.render_literal_value(value, parameter.type),
3256 be_right,
3257 )
3258 for value in values
3259 )
3260 else:
3261 replacement_expression = ", ".join(
3262 self.render_literal_value(value, parameter.type)
3263 for value in values
3264 )
3265
3266 return (), replacement_expression
3267
3268 def _literal_execute_expanding_parameter(self, name, parameter, values):
3269 if parameter.literal_execute:
3270 return self._literal_execute_expanding_parameter_literal_binds(
3271 parameter, values
3272 )
3273
3274 dialect = self.dialect
3275 typ_dialect_impl = parameter.type._unwrapped_dialect_impl(dialect)
3276
3277 if self._numeric_binds:
3278 bind_template = self.compilation_bindtemplate
3279 else:
3280 bind_template = self.bindtemplate
3281
3282 if (
3283 self.dialect._bind_typing_render_casts
3284 and typ_dialect_impl.render_bind_cast
3285 ):
3286
3287 def _render_bindtemplate(name):
3288 return self.render_bind_cast(
3289 parameter.type,
3290 typ_dialect_impl,
3291 bind_template % {"name": name},
3292 )
3293
3294 else:
3295
3296 def _render_bindtemplate(name):
3297 return bind_template % {"name": name}
3298
3299 if not values:
3300 to_update = []
3301 if typ_dialect_impl._is_tuple_type:
3302 replacement_expression = self.visit_empty_set_op_expr(
3303 parameter.type.types, parameter.expand_op
3304 )
3305 else:
3306 replacement_expression = self.visit_empty_set_op_expr(
3307 [parameter.type], parameter.expand_op
3308 )
3309
3310 elif typ_dialect_impl._is_tuple_type or (
3311 typ_dialect_impl._isnull
3312 and isinstance(values[0], collections_abc.Sequence)
3313 and not isinstance(values[0], (str, bytes))
3314 ):
3315 assert not typ_dialect_impl._is_array
3316 to_update = [
3317 ("%s_%s_%s" % (name, i, j), value)
3318 for i, tuple_element in enumerate(values, 1)
3319 for j, value in enumerate(tuple_element, 1)
3320 ]
3321
3322 replacement_expression = (
3323 "VALUES " if dialect.tuple_in_values else ""
3324 ) + ", ".join(
3325 "(%s)"
3326 % (
3327 ", ".join(
3328 _render_bindtemplate(
3329 to_update[i * len(tuple_element) + j][0]
3330 )
3331 for j, value in enumerate(tuple_element)
3332 )
3333 )
3334 for i, tuple_element in enumerate(values)
3335 )
3336 else:
3337 to_update = [
3338 ("%s_%s" % (name, i), value)
3339 for i, value in enumerate(values, 1)
3340 ]
3341 replacement_expression = ", ".join(
3342 _render_bindtemplate(key) for key, value in to_update
3343 )
3344
3345 return to_update, replacement_expression
3346
3347 def visit_binary(
3348 self,
3349 binary,
3350 override_operator=None,
3351 eager_grouping=False,
3352 from_linter=None,
3353 lateral_from_linter=None,
3354 **kw,
3355 ):
3356 if from_linter and operators.is_comparison(binary.operator):
3357 if lateral_from_linter is not None:
3358 enclosing_lateral = kw["enclosing_lateral"]
3359 lateral_from_linter.edges.update(
3360 itertools.product(
3361 _de_clone(
3362 binary.left._from_objects + [enclosing_lateral]
3363 ),
3364 _de_clone(
3365 binary.right._from_objects + [enclosing_lateral]
3366 ),
3367 )
3368 )
3369 else:
3370 from_linter.edges.update(
3371 itertools.product(
3372 _de_clone(binary.left._from_objects),
3373 _de_clone(binary.right._from_objects),
3374 )
3375 )
3376
3377 # don't allow "? = ?" to render
3378 if (
3379 self.ansi_bind_rules
3380 and isinstance(binary.left, elements.BindParameter)
3381 and isinstance(binary.right, elements.BindParameter)
3382 ):
3383 kw["literal_execute"] = True
3384
3385 operator_ = override_operator or binary.operator
3386 disp = self._get_operator_dispatch(operator_, "binary", None)
3387 if disp:
3388 return disp(binary, operator_, **kw)
3389 else:
3390 try:
3391 opstring = OPERATORS[operator_]
3392 except KeyError as err:
3393 raise exc.UnsupportedCompilationError(self, operator_) from err
3394 else:
3395 return self._generate_generic_binary(
3396 binary,
3397 opstring,
3398 from_linter=from_linter,
3399 lateral_from_linter=lateral_from_linter,
3400 **kw,
3401 )
3402
3403 def visit_function_as_comparison_op_binary(self, element, operator, **kw):
3404 return self.process(element.sql_function, **kw)
3405
3406 def visit_mod_binary(self, binary, operator, **kw):
3407 if self.preparer._double_percents:
3408 return (
3409 self.process(binary.left, **kw)
3410 + " %% "
3411 + self.process(binary.right, **kw)
3412 )
3413 else:
3414 return (
3415 self.process(binary.left, **kw)
3416 + " % "
3417 + self.process(binary.right, **kw)
3418 )
3419
3420 def visit_custom_op_binary(self, element, operator, **kw):
3421 kw["eager_grouping"] = operator.eager_grouping
3422 return self._generate_generic_binary(
3423 element,
3424 " " + self.escape_literal_column(operator.opstring) + " ",
3425 **kw,
3426 )
3427
3428 def visit_custom_op_unary_operator(self, element, operator, **kw):
3429 return self._generate_generic_unary_operator(
3430 element, self.escape_literal_column(operator.opstring) + " ", **kw
3431 )
3432
3433 def visit_custom_op_unary_modifier(self, element, operator, **kw):
3434 return self._generate_generic_unary_modifier(
3435 element, " " + self.escape_literal_column(operator.opstring), **kw
3436 )
3437
3438 def _generate_generic_binary(
3439 self, binary, opstring, eager_grouping=False, **kw
3440 ):
3441 _in_operator_expression = kw.get("_in_operator_expression", False)
3442
3443 kw["_in_operator_expression"] = True
3444 kw["_binary_op"] = binary.operator
3445 text = (
3446 binary.left._compiler_dispatch(
3447 self, eager_grouping=eager_grouping, **kw
3448 )
3449 + opstring
3450 + binary.right._compiler_dispatch(
3451 self, eager_grouping=eager_grouping, **kw
3452 )
3453 )
3454
3455 if _in_operator_expression and eager_grouping:
3456 text = "(%s)" % text
3457 return text
3458
3459 def _generate_generic_unary_operator(self, unary, opstring, **kw):
3460 return opstring + unary.element._compiler_dispatch(self, **kw)
3461
3462 def _generate_generic_unary_modifier(self, unary, opstring, **kw):
3463 return unary.element._compiler_dispatch(self, **kw) + opstring
3464
3465 @util.memoized_property
3466 def _like_percent_literal(self):
3467 return elements.literal_column("'%'", type_=sqltypes.STRINGTYPE)
3468
3469 def visit_ilike_case_insensitive_operand(self, element, **kw):
3470 return f"lower({element.element._compiler_dispatch(self, **kw)})"
3471
3472 def visit_contains_op_binary(self, binary, operator, **kw):
3473 binary = binary._clone()
3474 percent = self._like_percent_literal
3475 binary.right = percent.concat(binary.right).concat(percent)
3476 return self.visit_like_op_binary(binary, operator, **kw)
3477
3478 def visit_not_contains_op_binary(self, binary, operator, **kw):
3479 binary = binary._clone()
3480 percent = self._like_percent_literal
3481 binary.right = percent.concat(binary.right).concat(percent)
3482 return self.visit_not_like_op_binary(binary, operator, **kw)
3483
3484 def visit_icontains_op_binary(self, binary, operator, **kw):
3485 binary = binary._clone()
3486 percent = self._like_percent_literal
3487 binary.left = ilike_case_insensitive(binary.left)
3488 binary.right = percent.concat(
3489 ilike_case_insensitive(binary.right)
3490 ).concat(percent)
3491 return self.visit_ilike_op_binary(binary, operator, **kw)
3492
3493 def visit_not_icontains_op_binary(self, binary, operator, **kw):
3494 binary = binary._clone()
3495 percent = self._like_percent_literal
3496 binary.left = ilike_case_insensitive(binary.left)
3497 binary.right = percent.concat(
3498 ilike_case_insensitive(binary.right)
3499 ).concat(percent)
3500 return self.visit_not_ilike_op_binary(binary, operator, **kw)
3501
3502 def visit_startswith_op_binary(self, binary, operator, **kw):
3503 binary = binary._clone()
3504 percent = self._like_percent_literal
3505 binary.right = percent._rconcat(binary.right)
3506 return self.visit_like_op_binary(binary, operator, **kw)
3507
3508 def visit_not_startswith_op_binary(self, binary, operator, **kw):
3509 binary = binary._clone()
3510 percent = self._like_percent_literal
3511 binary.right = percent._rconcat(binary.right)
3512 return self.visit_not_like_op_binary(binary, operator, **kw)
3513
3514 def visit_istartswith_op_binary(self, binary, operator, **kw):
3515 binary = binary._clone()
3516 percent = self._like_percent_literal
3517 binary.left = ilike_case_insensitive(binary.left)
3518 binary.right = percent._rconcat(ilike_case_insensitive(binary.right))
3519 return self.visit_ilike_op_binary(binary, operator, **kw)
3520
3521 def visit_not_istartswith_op_binary(self, binary, operator, **kw):
3522 binary = binary._clone()
3523 percent = self._like_percent_literal
3524 binary.left = ilike_case_insensitive(binary.left)
3525 binary.right = percent._rconcat(ilike_case_insensitive(binary.right))
3526 return self.visit_not_ilike_op_binary(binary, operator, **kw)
3527
3528 def visit_endswith_op_binary(self, binary, operator, **kw):
3529 binary = binary._clone()
3530 percent = self._like_percent_literal
3531 binary.right = percent.concat(binary.right)
3532 return self.visit_like_op_binary(binary, operator, **kw)
3533
3534 def visit_not_endswith_op_binary(self, binary, operator, **kw):
3535 binary = binary._clone()
3536 percent = self._like_percent_literal
3537 binary.right = percent.concat(binary.right)
3538 return self.visit_not_like_op_binary(binary, operator, **kw)
3539
3540 def visit_iendswith_op_binary(self, binary, operator, **kw):
3541 binary = binary._clone()
3542 percent = self._like_percent_literal
3543 binary.left = ilike_case_insensitive(binary.left)
3544 binary.right = percent.concat(ilike_case_insensitive(binary.right))
3545 return self.visit_ilike_op_binary(binary, operator, **kw)
3546
3547 def visit_not_iendswith_op_binary(self, binary, operator, **kw):
3548 binary = binary._clone()
3549 percent = self._like_percent_literal
3550 binary.left = ilike_case_insensitive(binary.left)
3551 binary.right = percent.concat(ilike_case_insensitive(binary.right))
3552 return self.visit_not_ilike_op_binary(binary, operator, **kw)
3553
3554 def visit_like_op_binary(self, binary, operator, **kw):
3555 escape = binary.modifiers.get("escape", None)
3556
3557 return "%s LIKE %s" % (
3558 binary.left._compiler_dispatch(self, **kw),
3559 binary.right._compiler_dispatch(self, **kw),
3560 ) + (
3561 " ESCAPE " + self.render_literal_value(escape, sqltypes.STRINGTYPE)
3562 if escape is not None
3563 else ""
3564 )
3565
3566 def visit_not_like_op_binary(self, binary, operator, **kw):
3567 escape = binary.modifiers.get("escape", None)
3568 return "%s NOT LIKE %s" % (
3569 binary.left._compiler_dispatch(self, **kw),
3570 binary.right._compiler_dispatch(self, **kw),
3571 ) + (
3572 " ESCAPE " + self.render_literal_value(escape, sqltypes.STRINGTYPE)
3573 if escape is not None
3574 else ""
3575 )
3576
3577 def visit_ilike_op_binary(self, binary, operator, **kw):
3578 if operator is operators.ilike_op:
3579 binary = binary._clone()
3580 binary.left = ilike_case_insensitive(binary.left)
3581 binary.right = ilike_case_insensitive(binary.right)
3582 # else we assume ilower() has been applied
3583
3584 return self.visit_like_op_binary(binary, operator, **kw)
3585
3586 def visit_not_ilike_op_binary(self, binary, operator, **kw):
3587 if operator is operators.not_ilike_op:
3588 binary = binary._clone()
3589 binary.left = ilike_case_insensitive(binary.left)
3590 binary.right = ilike_case_insensitive(binary.right)
3591 # else we assume ilower() has been applied
3592
3593 return self.visit_not_like_op_binary(binary, operator, **kw)
3594
3595 def visit_between_op_binary(self, binary, operator, **kw):
3596 symmetric = binary.modifiers.get("symmetric", False)
3597 return self._generate_generic_binary(
3598 binary, " BETWEEN SYMMETRIC " if symmetric else " BETWEEN ", **kw
3599 )
3600
3601 def visit_not_between_op_binary(self, binary, operator, **kw):
3602 symmetric = binary.modifiers.get("symmetric", False)
3603 return self._generate_generic_binary(
3604 binary,
3605 " NOT BETWEEN SYMMETRIC " if symmetric else " NOT BETWEEN ",
3606 **kw,
3607 )
3608
3609 def visit_regexp_match_op_binary(self, binary, operator, **kw):
3610 raise exc.CompileError(
3611 "%s dialect does not support regular expressions"
3612 % self.dialect.name
3613 )
3614
3615 def visit_not_regexp_match_op_binary(self, binary, operator, **kw):
3616 raise exc.CompileError(
3617 "%s dialect does not support regular expressions"
3618 % self.dialect.name
3619 )
3620
3621 def visit_regexp_replace_op_binary(self, binary, operator, **kw):
3622 raise exc.CompileError(
3623 "%s dialect does not support regular expression replacements"
3624 % self.dialect.name
3625 )
3626
3627 def visit_bindparam(
3628 self,
3629 bindparam,
3630 within_columns_clause=False,
3631 literal_binds=False,
3632 skip_bind_expression=False,
3633 literal_execute=False,
3634 render_postcompile=False,
3635 **kwargs,
3636 ):
3637
3638 if not skip_bind_expression:
3639 impl = bindparam.type.dialect_impl(self.dialect)
3640 if impl._has_bind_expression:
3641 bind_expression = impl.bind_expression(bindparam)
3642 wrapped = self.process(
3643 bind_expression,
3644 skip_bind_expression=True,
3645 within_columns_clause=within_columns_clause,
3646 literal_binds=literal_binds and not bindparam.expanding,
3647 literal_execute=literal_execute,
3648 render_postcompile=render_postcompile,
3649 **kwargs,
3650 )
3651 if bindparam.expanding:
3652 # for postcompile w/ expanding, move the "wrapped" part
3653 # of this into the inside
3654
3655 m = re.match(
3656 r"^(.*)\(__\[POSTCOMPILE_(\S+?)\]\)(.*)$", wrapped
3657 )
3658 assert m, "unexpected format for expanding parameter"
3659 wrapped = "(__[POSTCOMPILE_%s~~%s~~REPL~~%s~~])" % (
3660 m.group(2),
3661 m.group(1),
3662 m.group(3),
3663 )
3664
3665 if literal_binds:
3666 ret = self.render_literal_bindparam(
3667 bindparam,
3668 within_columns_clause=True,
3669 bind_expression_template=wrapped,
3670 **kwargs,
3671 )
3672 return f"({ret})"
3673
3674 return wrapped
3675
3676 if not literal_binds:
3677 literal_execute = (
3678 literal_execute
3679 or bindparam.literal_execute
3680 or (within_columns_clause and self.ansi_bind_rules)
3681 )
3682 post_compile = literal_execute or bindparam.expanding
3683 else:
3684 post_compile = False
3685
3686 if literal_binds:
3687 ret = self.render_literal_bindparam(
3688 bindparam, within_columns_clause=True, **kwargs
3689 )
3690 if bindparam.expanding:
3691 ret = f"({ret})"
3692 return ret
3693
3694 name = self._truncate_bindparam(bindparam)
3695
3696 if name in self.binds:
3697 existing = self.binds[name]
3698 if existing is not bindparam:
3699 if (
3700 (existing.unique or bindparam.unique)
3701 and not existing.proxy_set.intersection(
3702 bindparam.proxy_set
3703 )
3704 and not existing._cloned_set.intersection(
3705 bindparam._cloned_set
3706 )
3707 ):
3708 raise exc.CompileError(
3709 "Bind parameter '%s' conflicts with "
3710 "unique bind parameter of the same name" % name
3711 )
3712 elif existing.expanding != bindparam.expanding:
3713 raise exc.CompileError(
3714 "Can't reuse bound parameter name '%s' in both "
3715 "'expanding' (e.g. within an IN expression) and "
3716 "non-expanding contexts. If this parameter is to "
3717 "receive a list/array value, set 'expanding=True' on "
3718 "it for expressions that aren't IN, otherwise use "
3719 "a different parameter name." % (name,)
3720 )
3721 elif existing._is_crud or bindparam._is_crud:
3722 if existing._is_crud and bindparam._is_crud:
3723 # TODO: this condition is not well understood.
3724 # see tests in test/sql/test_update.py
3725 raise exc.CompileError(
3726 "Encountered unsupported case when compiling an "
3727 "INSERT or UPDATE statement. If this is a "
3728 "multi-table "
3729 "UPDATE statement, please provide string-named "
3730 "arguments to the "
3731 "values() method with distinct names; support for "
3732 "multi-table UPDATE statements that "
3733 "target multiple tables for UPDATE is very "
3734 "limited",
3735 )
3736 else:
3737 raise exc.CompileError(
3738 f"bindparam() name '{bindparam.key}' is reserved "
3739 "for automatic usage in the VALUES or SET "
3740 "clause of this "
3741 "insert/update statement. Please use a "
3742 "name other than column name when using "
3743 "bindparam() "
3744 "with insert() or update() (for example, "
3745 f"'b_{bindparam.key}')."
3746 )
3747
3748 self.binds[bindparam.key] = self.binds[name] = bindparam
3749
3750 # if we are given a cache key that we're going to match against,
3751 # relate the bindparam here to one that is most likely present
3752 # in the "extracted params" portion of the cache key. this is used
3753 # to set up a positional mapping that is used to determine the
3754 # correct parameters for a subsequent use of this compiled with
3755 # a different set of parameter values. here, we accommodate for
3756 # parameters that may have been cloned both before and after the cache
3757 # key was been generated.
3758 ckbm_tuple = self._cache_key_bind_match
3759
3760 if ckbm_tuple:
3761 ckbm, cksm = ckbm_tuple
3762 for bp in bindparam._cloned_set:
3763 if bp.key in cksm:
3764 cb = cksm[bp.key]
3765 ckbm[cb].append(bindparam)
3766
3767 if bindparam.isoutparam:
3768 self.has_out_parameters = True
3769
3770 if post_compile:
3771 if render_postcompile:
3772 self._render_postcompile = True
3773
3774 if literal_execute:
3775 self.literal_execute_params |= {bindparam}
3776 else:
3777 self.post_compile_params |= {bindparam}
3778
3779 ret = self.bindparam_string(
3780 name,
3781 post_compile=post_compile,
3782 expanding=bindparam.expanding,
3783 bindparam_type=bindparam.type,
3784 **kwargs,
3785 )
3786
3787 if bindparam.expanding:
3788 ret = f"({ret})"
3789
3790 return ret
3791
3792 def render_bind_cast(self, type_, dbapi_type, sqltext):
3793 raise NotImplementedError()
3794
3795 def render_literal_bindparam(
3796 self,
3797 bindparam,
3798 render_literal_value=NO_ARG,
3799 bind_expression_template=None,
3800 **kw,
3801 ):
3802 if render_literal_value is not NO_ARG:
3803 value = render_literal_value
3804 else:
3805 if bindparam.value is None and bindparam.callable is None:
3806 op = kw.get("_binary_op", None)
3807 if op and op not in (operators.is_, operators.is_not):
3808 util.warn_limited(
3809 "Bound parameter '%s' rendering literal NULL in a SQL "
3810 "expression; comparisons to NULL should not use "
3811 "operators outside of 'is' or 'is not'",
3812 (bindparam.key,),
3813 )
3814 return self.process(sqltypes.NULLTYPE, **kw)
3815 value = bindparam.effective_value
3816
3817 if bindparam.expanding:
3818 leep = self._literal_execute_expanding_parameter_literal_binds
3819 to_update, replacement_expr = leep(
3820 bindparam,
3821 value,
3822 bind_expression_template=bind_expression_template,
3823 )
3824 return replacement_expr
3825 else:
3826 return self.render_literal_value(value, bindparam.type)
3827
3828 def render_literal_value(self, value, type_):
3829 """Render the value of a bind parameter as a quoted literal.
3830
3831 This is used for statement sections that do not accept bind parameters
3832 on the target driver/database.
3833
3834 This should be implemented by subclasses using the quoting services
3835 of the DBAPI.
3836
3837 """
3838
3839 if value is None and not type_.should_evaluate_none:
3840 # issue #10535 - handle NULL in the compiler without placing
3841 # this onto each type, except for "evaluate None" types
3842 # (e.g. JSON)
3843 return self.process(elements.Null._instance())
3844
3845 processor = type_._cached_literal_processor(self.dialect)
3846 if processor:
3847 try:
3848 return processor(value)
3849 except Exception as e:
3850 raise exc.CompileError(
3851 f"Could not render literal value "
3852 f'"{sql_util._repr_single_value(value)}" '
3853 f"with datatype "
3854 f"{type_}; see parent stack trace for "
3855 "more detail."
3856 ) from e
3857
3858 else:
3859 raise exc.CompileError(
3860 f"No literal value renderer is available for literal value "
3861 f'"{sql_util._repr_single_value(value)}" '
3862 f"with datatype {type_}"
3863 )
3864
3865 def _truncate_bindparam(self, bindparam):
3866 if bindparam in self.bind_names:
3867 return self.bind_names[bindparam]
3868
3869 bind_name = bindparam.key
3870 if isinstance(bind_name, elements._truncated_label):
3871 bind_name = self._truncated_identifier("bindparam", bind_name)
3872
3873 # add to bind_names for translation
3874 self.bind_names[bindparam] = bind_name
3875
3876 return bind_name
3877
3878 def _truncated_identifier(
3879 self, ident_class: str, name: _truncated_label
3880 ) -> str:
3881 if (ident_class, name) in self.truncated_names:
3882 return self.truncated_names[(ident_class, name)]
3883
3884 anonname = name.apply_map(self.anon_map)
3885
3886 if len(anonname) > self.label_length - 6:
3887 counter = self._truncated_counters.get(ident_class, 1)
3888 truncname = (
3889 anonname[0 : max(self.label_length - 6, 0)]
3890 + "_"
3891 + hex(counter)[2:]
3892 )
3893 self._truncated_counters[ident_class] = counter + 1
3894 else:
3895 truncname = anonname
3896 self.truncated_names[(ident_class, name)] = truncname
3897 return truncname
3898
3899 def _anonymize(self, name: str) -> str:
3900 return name % self.anon_map
3901
3902 def bindparam_string(
3903 self,
3904 name: str,
3905 post_compile: bool = False,
3906 expanding: bool = False,
3907 escaped_from: Optional[str] = None,
3908 bindparam_type: Optional[TypeEngine[Any]] = None,
3909 accumulate_bind_names: Optional[Set[str]] = None,
3910 visited_bindparam: Optional[List[str]] = None,
3911 **kw: Any,
3912 ) -> str:
3913 # TODO: accumulate_bind_names is passed by crud.py to gather
3914 # names on a per-value basis, visited_bindparam is passed by
3915 # visit_insert() to collect all parameters in the statement.
3916 # see if this gathering can be simplified somehow
3917 if accumulate_bind_names is not None:
3918 accumulate_bind_names.add(name)
3919 if visited_bindparam is not None:
3920 visited_bindparam.append(name)
3921
3922 if not escaped_from:
3923 if self._bind_translate_re.search(name):
3924 # not quite the translate use case as we want to
3925 # also get a quick boolean if we even found
3926 # unusual characters in the name
3927 new_name = self._bind_translate_re.sub(
3928 lambda m: self._bind_translate_chars[m.group(0)],
3929 name,
3930 )
3931 escaped_from = name
3932 name = new_name
3933
3934 if escaped_from:
3935 self.escaped_bind_names = self.escaped_bind_names.union(
3936 {escaped_from: name}
3937 )
3938 if post_compile:
3939 ret = "__[POSTCOMPILE_%s]" % name
3940 if expanding:
3941 # for expanding, bound parameters or literal values will be
3942 # rendered per item
3943 return ret
3944
3945 # otherwise, for non-expanding "literal execute", apply
3946 # bind casts as determined by the datatype
3947 if bindparam_type is not None:
3948 type_impl = bindparam_type._unwrapped_dialect_impl(
3949 self.dialect
3950 )
3951 if type_impl.render_literal_cast:
3952 ret = self.render_bind_cast(bindparam_type, type_impl, ret)
3953 return ret
3954 elif self.state is CompilerState.COMPILING:
3955 ret = self.compilation_bindtemplate % {"name": name}
3956 else:
3957 ret = self.bindtemplate % {"name": name}
3958
3959 if (
3960 bindparam_type is not None
3961 and self.dialect._bind_typing_render_casts
3962 ):
3963 type_impl = bindparam_type._unwrapped_dialect_impl(self.dialect)
3964 if type_impl.render_bind_cast:
3965 ret = self.render_bind_cast(bindparam_type, type_impl, ret)
3966
3967 return ret
3968
3969 def _dispatch_independent_ctes(self, stmt, kw):
3970 local_kw = kw.copy()
3971 local_kw.pop("cte_opts", None)
3972 for cte, opt in zip(
3973 stmt._independent_ctes, stmt._independent_ctes_opts
3974 ):
3975 cte._compiler_dispatch(self, cte_opts=opt, **local_kw)
3976