Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
base.py2294 linesDownload Raw Back to sql
1# sql/base.py
2# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
7# mypy: allow-untyped-defs, allow-untyped-calls
8
9"""Foundational utilities common to many sql modules."""
10
11
12from __future__ import annotations
13
14import collections
15from enum import Enum
16import itertools
17from itertools import zip_longest
18import operator
19import re
20from typing import Any
21from typing import Callable
22from typing import cast
23from typing import Dict
24from typing import FrozenSet
25from typing import Generator
26from typing import Generic
27from typing import Iterable
28from typing import Iterator
29from typing import List
30from typing import Mapping
31from typing import MutableMapping
32from typing import NamedTuple
33from typing import NoReturn
34from typing import Optional
35from typing import overload
36from typing import Sequence
37from typing import Set
38from typing import Tuple
39from typing import Type
40from typing import TYPE_CHECKING
41from typing import TypeVar
42from typing import Union
43
44from . import roles
45from . import visitors
46from .cache_key import HasCacheKey  # noqa
47from .cache_key import MemoizedHasCacheKey  # noqa
48from .traversals import HasCopyInternals  # noqa
49from .visitors import ClauseVisitor
50from .visitors import ExtendedInternalTraversal
51from .visitors import ExternallyTraversible
52from .visitors import InternalTraversal
53from .. import event
54from .. import exc
55from .. import util
56from ..util import HasMemoized as HasMemoized
57from ..util import hybridmethod
58from ..util import typing as compat_typing
59from ..util import warn_deprecated
60from ..util.typing import Final
61from ..util.typing import Protocol
62from ..util.typing import Self
63from ..util.typing import TypeGuard
64
65if TYPE_CHECKING:
66    from . import coercions
67    from . import elements
68    from . import type_api
69    from ._orm_types import DMLStrategyArgument
70    from ._orm_types import SynchronizeSessionArgument
71    from ._typing import _CLE
72    from .cache_key import CacheKey
73    from .compiler import SQLCompiler
74    from .elements import BindParameter
75    from .elements import ClauseList
76    from .elements import ColumnClause  # noqa
77    from .elements import ColumnElement
78    from .elements import NamedColumn
79    from .elements import SQLCoreOperations
80    from .elements import TextClause
81    from .schema import Column
82    from .schema import DefaultGenerator
83    from .selectable import _JoinTargetElement
84    from .selectable import _SelectIterable
85    from .selectable import FromClause
86    from .visitors import anon_map
87    from ..engine import Connection
88    from ..engine import CursorResult
89    from ..engine.interfaces import _CoreMultiExecuteParams
90    from ..engine.interfaces import _ExecuteOptions
91    from ..engine.interfaces import _ImmutableExecuteOptions
92    from ..engine.interfaces import CacheStats
93    from ..engine.interfaces import Compiled
94    from ..engine.interfaces import CompiledCacheType
95    from ..engine.interfaces import CoreExecuteOptionsParameter
96    from ..engine.interfaces import Dialect
97    from ..engine.interfaces import IsolationLevel
98    from ..engine.interfaces import SchemaTranslateMapType
99    from ..event import dispatcher
100
101if not TYPE_CHECKING:
102    coercions = None  # noqa
103    elements = None  # noqa
104    type_api = None  # noqa
105
106
107class _NoArg(Enum):
108    NO_ARG = 0
109
110    def __repr__(self):
111        return f"_NoArg.{self.name}"
112
113
114NO_ARG: Final = _NoArg.NO_ARG
115
116
117class _NoneName(Enum):
118    NONE_NAME = 0
119    """indicate a 'deferred' name that was ultimately the value None."""
120
121
122_NONE_NAME: Final = _NoneName.NONE_NAME
123
124_T = TypeVar("_T", bound=Any)
125
126_Fn = TypeVar("_Fn", bound=Callable[..., Any])
127
128_AmbiguousTableNameMap = MutableMapping[str, str]
129
130
131class _DefaultDescriptionTuple(NamedTuple):
132    arg: Any
133    is_scalar: Optional[bool]
134    is_callable: Optional[bool]
135    is_sentinel: Optional[bool]
136
137    @classmethod
138    def _from_column_default(
139        cls, default: Optional[DefaultGenerator]
140    ) -> _DefaultDescriptionTuple:
141        return (
142            _DefaultDescriptionTuple(
143                default.arg,  # type: ignore
144                default.is_scalar,
145                default.is_callable,
146                default.is_sentinel,
147            )
148            if default
149            and (
150                default.has_arg
151                or (not default.for_update and default.is_sentinel)
152            )
153            else _DefaultDescriptionTuple(None, None, None, None)
154        )
155
156
157_never_select_column: operator.attrgetter[Any] = operator.attrgetter(
158    "_omit_from_statements"
159)
160
161
162class _EntityNamespace(Protocol):
163    def __getattr__(self, key: str) -> SQLCoreOperations[Any]: ...
164
165
166class _HasEntityNamespace(Protocol):
167    @util.ro_non_memoized_property
168    def entity_namespace(self) -> _EntityNamespace: ...
169
170
171def _is_has_entity_namespace(element: Any) -> TypeGuard[_HasEntityNamespace]:
172    return hasattr(element, "entity_namespace")
173
174
175# Remove when https://github.com/python/mypy/issues/14640 will be fixed
176_Self = TypeVar("_Self", bound=Any)
177
178
179class Immutable:
180    """mark a ClauseElement as 'immutable' when expressions are cloned.
181
182    "immutable" objects refers to the "mutability" of an object in the
183    context of SQL DQL and DML generation.   Such as, in DQL, one can
184    compose a SELECT or subquery of varied forms, but one cannot modify
185    the structure of a specific table or column within DQL.
186    :class:`.Immutable` is mostly intended to follow this concept, and as
187    such the primary "immutable" objects are :class:`.ColumnClause`,
188    :class:`.Column`, :class:`.TableClause`, :class:`.Table`.
189
190    """
191
192    __slots__ = ()
193
194    _is_immutable: bool = True
195
196    def unique_params(self, *optionaldict: Any, **kwargs: Any) -> NoReturn:
197        raise NotImplementedError("Immutable objects do not support copying")
198
199    def params(self, *optionaldict: Any, **kwargs: Any) -> NoReturn:
200        raise NotImplementedError("Immutable objects do not support copying")
201
202    def _clone(self: _Self, **kw: Any) -> _Self:
203        return self
204
205    def _copy_internals(
206        self, *, omit_attrs: Iterable[str] = (), **kw: Any
207    ) -> None:
208        pass
209
210
211class SingletonConstant(Immutable):
212    """Represent SQL constants like NULL, TRUE, FALSE"""
213
214    _is_singleton_constant: bool = True
215
216    _singleton: SingletonConstant
217
218    def __new__(cls: _T, *arg: Any, **kw: Any) -> _T:
219        return cast(_T, cls._singleton)
220
221    @util.non_memoized_property
222    def proxy_set(self) -> FrozenSet[ColumnElement[Any]]:
223        raise NotImplementedError()
224
225    @classmethod
226    def _create_singleton(cls) -> None:
227        obj = object.__new__(cls)
228        obj.__init__()  # type: ignore
229
230        # for a long time this was an empty frozenset, meaning
231        # a SingletonConstant would never be a "corresponding column" in
232        # a statement.  This referred to #6259.  However, in #7154 we see
233        # that we do in fact need "correspondence" to work when matching cols
234        # in result sets, so the non-correspondence was moved to a more
235        # specific level when we are actually adapting expressions for SQL
236        # render only.
237        obj.proxy_set = frozenset([obj])
238        cls._singleton = obj
239
240
241def _from_objects(
242    *elements: Union[
243        ColumnElement[Any], FromClause, TextClause, _JoinTargetElement
244    ]
245) -> Iterator[FromClause]:
246    return itertools.chain.from_iterable(
247        [element._from_objects for element in elements]
248    )
249
250
251def _select_iterables(
252    elements: Iterable[roles.ColumnsClauseRole],
253) -> _SelectIterable:
254    """expand tables into individual columns in the
255    given list of column expressions.
256
257    """
258    return itertools.chain.from_iterable(
259        [c._select_iterable for c in elements]
260    )
261
262
263_SelfGenerativeType = TypeVar("_SelfGenerativeType", bound="_GenerativeType")
264
265
266class _GenerativeType(compat_typing.Protocol):
267    def _generate(self) -> Self: ...
268
269
270def _generative(fn: _Fn) -> _Fn:
271    """non-caching _generative() decorator.
272
273    This is basically the legacy decorator that copies the object and
274    runs a method on the new copy.
275
276    """
277
278    @util.decorator
279    def _generative(
280        fn: _Fn, self: _SelfGenerativeType, *args: Any, **kw: Any
281    ) -> _SelfGenerativeType:
282        """Mark a method as generative."""
283
284        self = self._generate()
285        x = fn(self, *args, **kw)
286        assert x is self, "generative methods must return self"
287        return self
288
289    decorated = _generative(fn)
290    decorated.non_generative = fn  # type: ignore
291    return decorated
292
293
294def _exclusive_against(*names: str, **kw: Any) -> Callable[[_Fn], _Fn]:
295    msgs: Dict[str, str] = kw.pop("msgs", {})
296
297    defaults: Dict[str, str] = kw.pop("defaults", {})
298
299    getters: List[Tuple[str, operator.attrgetter[Any], Optional[str]]] = [
300        (name, operator.attrgetter(name), defaults.get(name, None))
301        for name in names
302    ]
303
304    @util.decorator
305    def check(fn: _Fn, *args: Any, **kw: Any) -> Any:
306        # make pylance happy by not including "self" in the argument
307        # list
308        self = args[0]
309        args = args[1:]
310        for name, getter, default_ in getters:
311            if getter(self) is not default_:
312                msg = msgs.get(
313                    name,
314                    "Method %s() has already been invoked on this %s construct"
315                    % (fn.__name__, self.__class__),
316                )
317                raise exc.InvalidRequestError(msg)
318        return fn(self, *args, **kw)
319
320    return check
321
322
323def _clone(element, **kw):
324    return element._clone(**kw)
325
326
327def _expand_cloned(
328    elements: Iterable[_CLE],
329) -> Iterable[_CLE]:
330    """expand the given set of ClauseElements to be the set of all 'cloned'
331    predecessors.
332
333    """
334    # TODO: cython candidate
335    return itertools.chain(*[x._cloned_set for x in elements])
336
337
338def _de_clone(
339    elements: Iterable[_CLE],
340) -> Iterable[_CLE]:
341    for x in elements:
342        while x._is_clone_of is not None:
343            x = x._is_clone_of
344        yield x
345
346
347def _cloned_intersection(a: Iterable[_CLE], b: Iterable[_CLE]) -> Set[_CLE]:
348    """return the intersection of sets a and b, counting
349    any overlap between 'cloned' predecessors.
350
351    The returned set is in terms of the entities present within 'a'.
352
353    """
354    all_overlap: Set[_CLE] = set(_expand_cloned(a)).intersection(
355        _expand_cloned(b)
356    )
357    return {elem for elem in a if all_overlap.intersection(elem._cloned_set)}
358
359
360def _cloned_difference(a: Iterable[_CLE], b: Iterable[_CLE]) -> Set[_CLE]:
361    all_overlap: Set[_CLE] = set(_expand_cloned(a)).intersection(
362        _expand_cloned(b)
363    )
364    return {
365        elem for elem in a if not all_overlap.intersection(elem._cloned_set)
366    }
367
368
369class _DialectArgView(MutableMapping[str, Any]):
370    """A dictionary view of dialect-level arguments in the form
371    <dialectname>_<argument_name>.
372
373    """
374
375    __slots__ = ("obj",)
376
377    def __init__(self, obj: DialectKWArgs) -> None:
378        self.obj = obj
379
380    def _key(self, key: str) -> Tuple[str, str]:
381        try:
382            dialect, value_key = key.split("_", 1)
383        except ValueError as err:
384            raise KeyError(key) from err
385        else:
386            return dialect, value_key
387
388    def __getitem__(self, key: str) -> Any:
389        dialect, value_key = self._key(key)
390
391        try:
392            opt = self.obj.dialect_options[dialect]
393        except exc.NoSuchModuleError as err:
394            raise KeyError(key) from err
395        else:
396            return opt[value_key]
397
398    def __setitem__(self, key: str, value: Any) -> None:
399        try:
400            dialect, value_key = self._key(key)
401        except KeyError as err:
402            raise exc.ArgumentError(
403                "Keys must be of the form <dialectname>_<argname>"
404            ) from err
405        else:
406            self.obj.dialect_options[dialect][value_key] = value
407
408    def __delitem__(self, key: str) -> None:
409        dialect, value_key = self._key(key)
410        del self.obj.dialect_options[dialect][value_key]
411
412    def __len__(self) -> int:
413        return sum(
414            len(args._non_defaults)
415            for args in self.obj.dialect_options.values()
416        )
417
418    def __iter__(self) -> Generator[str, None, None]:
419        return (
420            "%s_%s" % (dialect_name, value_name)
421            for dialect_name in self.obj.dialect_options
422            for value_name in self.obj.dialect_options[
423                dialect_name
424            ]._non_defaults
425        )
426
427
428class _DialectArgDict(MutableMapping[str, Any]):
429    """A dictionary view of dialect-level arguments for a specific
430    dialect.
431
432    Maintains a separate collection of user-specified arguments
433    and dialect-specified default arguments.
434
435    """
436
437    def __init__(self) -> None:
438        self._non_defaults: Dict[str, Any] = {}
439        self._defaults: Dict[str, Any] = {}
440
441    def __len__(self) -> int:
442        return len(set(self._non_defaults).union(self._defaults))
443
444    def __iter__(self) -> Iterator[str]:
445        return iter(set(self._non_defaults).union(self._defaults))
446
447    def __getitem__(self, key: str) -> Any:
448        if key in self._non_defaults:
449            return self._non_defaults[key]
450        else:
451            return self._defaults[key]
452
453    def __setitem__(self, key: str, value: Any) -> None:
454        self._non_defaults[key] = value
455
456    def __delitem__(self, key: str) -> None:
457        del self._non_defaults[key]
458
459
460@util.preload_module("sqlalchemy.dialects")
461def _kw_reg_for_dialect(dialect_name: str) -> Optional[Dict[Any, Any]]:
462    dialect_cls = util.preloaded.dialects.registry.load(dialect_name)
463    if dialect_cls.construct_arguments is None:
464        return None
465    return dict(dialect_cls.construct_arguments)
466
467
468class DialectKWArgs:
469    """Establish the ability for a class to have dialect-specific arguments
470    with defaults and constructor validation.
471
472    The :class:`.DialectKWArgs` interacts with the
473    :attr:`.DefaultDialect.construct_arguments` present on a dialect.
474
475    .. seealso::
476
477        :attr:`.DefaultDialect.construct_arguments`
478
479    """
480
481    __slots__ = ()
482
483    _dialect_kwargs_traverse_internals: List[Tuple[str, Any]] = [
484        ("dialect_options", InternalTraversal.dp_dialect_options)
485    ]
486
487    def get_dialect_option(
488        self,
489        dialect: Dialect,
490        argument_name: str,
491        *,
492        else_: Any = None,
493        deprecated_fallback: Optional[str] = None,
494    ) -> Any:
495        r"""Return the value of a dialect-specific option, or *else_* if
496        this dialect does not register the given argument.
497
498        This is useful for DDL compilers that may be inherited by
499        third-party dialects whose ``construct_arguments`` do not
500        include the same set of keys as the parent dialect.
501
502        :param dialect: The dialect for which to retrieve the option.
503        :param argument_name: The name of the argument to retrieve.
504        :param else\_: The value to return if the argument is not present.
505        :param deprecated_fallback: Optional dialect name to fall back to
506         if the argument is not present for the current dialect. If the
507         argument is present for the fallback dialect but not the current
508         dialect, a deprecation warning will be emitted.
509
510        """
511
512        registry = DialectKWArgs._kw_registry[dialect.name]
513        if registry is None:
514            return else_
515
516        if argument_name in registry.get(self.__class__, {}):
517            if (
518                deprecated_fallback is None
519                or dialect.name == deprecated_fallback
520            ):
521                return self.dialect_options[dialect.name][argument_name]
522
523            # deprecated_fallback is present; need to look in two places
524
525            # Current dialect has this option registered.
526            # Check if user explicitly set it.
527            if (
528                dialect.name in self.dialect_options
529                and argument_name
530                in self.dialect_options[dialect.name]._non_defaults
531            ):
532                # User explicitly set this dialect's option - use it
533                return self.dialect_options[dialect.name][argument_name]
534
535            # User didn't set current dialect's option.
536            # Check for deprecated fallback.
537            elif (
538                deprecated_fallback in self.dialect_options
539                and argument_name
540                in self.dialect_options[deprecated_fallback]._non_defaults
541            ):
542                # User set fallback option but not current dialect's option
543                warn_deprecated(
544                    f"Using '{deprecated_fallback}_{argument_name}' "
545                    f"with the '{dialect.name}' dialect is deprecated; "
546                    f"please additionally specify "
547                    f"'{dialect.name}_{argument_name}'.",
548                    version="2.1",
549                )
550                return self.dialect_options[deprecated_fallback][argument_name]
551
552            # Return default value
553            return self.dialect_options[dialect.name][argument_name]
554        else:
555            # Current dialect doesn't have the option registered at all.
556            # Don't warn - if a third-party dialect doesn't support an
557            # option, that's their choice, not a deprecation case.
558            return else_
559
560    @classmethod
561    def argument_for(
562        cls, dialect_name: str, argument_name: str, default: Any
563    ) -> None:
564        """Add a new kind of dialect-specific keyword argument for this class.
565
566        E.g.::
567
568            Index.argument_for("mydialect", "length", None)
569
570            some_index = Index("a", "b", mydialect_length=5)
571
572        The :meth:`.DialectKWArgs.argument_for` method is a per-argument
573        way adding extra arguments to the
574        :attr:`.DefaultDialect.construct_arguments` dictionary. This
575        dictionary provides a list of argument names accepted by various
576        schema-level constructs on behalf of a dialect.
577
578        New dialects should typically specify this dictionary all at once as a
579        data member of the dialect class.  The use case for ad-hoc addition of
580        argument names is typically for end-user code that is also using
581        a custom compilation scheme which consumes the additional arguments.
582
583        :param dialect_name: name of a dialect.  The dialect must be
584         locatable, else a :class:`.NoSuchModuleError` is raised.   The
585         dialect must also include an existing
586         :attr:`.DefaultDialect.construct_arguments` collection, indicating
587         that it participates in the keyword-argument validation and default
588         system, else :class:`.ArgumentError` is raised.  If the dialect does
589         not include this collection, then any keyword argument can be
590         specified on behalf of this dialect already.  All dialects packaged
591         within SQLAlchemy include this collection, however for third party
592         dialects, support may vary.
593
594        :param argument_name: name of the parameter.
595
596        :param default: default value of the parameter.
597
598        """
599
600        construct_arg_dictionary: Optional[Dict[Any, Any]] = (
601            DialectKWArgs._kw_registry[dialect_name]
602        )
603        if construct_arg_dictionary is None:
604            raise exc.ArgumentError(
605                "Dialect '%s' does have keyword-argument "
606                "validation and defaults enabled configured" % dialect_name
607            )
608        if cls not in construct_arg_dictionary:
609            construct_arg_dictionary[cls] = {}
610        construct_arg_dictionary[cls][argument_name] = default
611
612    @property
613    def dialect_kwargs(self) -> _DialectArgView:
614        """A collection of keyword arguments specified as dialect-specific
615        options to this construct.
616
617        The arguments are present here in their original ``<dialect>_<kwarg>``
618        format.  Only arguments that were actually passed are included;
619        unlike the :attr:`.DialectKWArgs.dialect_options` collection, which
620        contains all options known by this dialect including defaults.
621
622        The collection is also writable; keys are accepted of the
623        form ``<dialect>_<kwarg>`` where the value will be assembled
624        into the list of options.
625
626        .. seealso::
627
628            :attr:`.DialectKWArgs.dialect_options` - nested dictionary form
629
630        """
631        return _DialectArgView(self)
632
633    @property
634    def kwargs(self) -> _DialectArgView:
635        """A synonym for :attr:`.DialectKWArgs.dialect_kwargs`."""
636        return self.dialect_kwargs
637
638    _kw_registry: util.PopulateDict[str, Optional[Dict[Any, Any]]] = (
639        util.PopulateDict(_kw_reg_for_dialect)
640    )
641
642    @classmethod
643    def _kw_reg_for_dialect_cls(cls, dialect_name: str) -> _DialectArgDict:
644        construct_arg_dictionary = DialectKWArgs._kw_registry[dialect_name]
645        d = _DialectArgDict()
646
647        if construct_arg_dictionary is None:
648            d._defaults.update({"*": None})
649        else:
650            for cls in reversed(cls.__mro__):
651                if cls in construct_arg_dictionary:
652                    d._defaults.update(construct_arg_dictionary[cls])
653        return d
654
655    @util.memoized_property
656    def dialect_options(self) -> util.PopulateDict[str, _DialectArgDict]:
657        """A collection of keyword arguments specified as dialect-specific
658        options to this construct.
659
660        This is a two-level nested registry, keyed to ``<dialect_name>``
661        and ``<argument_name>``.  For example, the ``postgresql_where``
662        argument would be locatable as::
663
664            arg = my_object.dialect_options["postgresql"]["where"]
665
666        .. versionadded:: 0.9.2
667
668        .. seealso::
669
670            :attr:`.DialectKWArgs.dialect_kwargs` - flat dictionary form
671
672        """
673
674        return util.PopulateDict(self._kw_reg_for_dialect_cls)
675
676    def _validate_dialect_kwargs(self, kwargs: Dict[str, Any]) -> None:
677        # validate remaining kwargs that they all specify DB prefixes
678
679        if not kwargs:
680            return
681
682        for k in kwargs:
683            m = re.match("^(.+?)_(.+)$", k)
684            if not m:
685                raise TypeError(
686                    "Additional arguments should be "
687                    "named <dialectname>_<argument>, got '%s'" % k
688                )
689            dialect_name, arg_name = m.group(1, 2)
690
691            try:
692                construct_arg_dictionary = self.dialect_options[dialect_name]
693            except exc.NoSuchModuleError:
694                util.warn(
695                    "Can't validate argument %r; can't "
696                    "locate any SQLAlchemy dialect named %r"
697                    % (k, dialect_name)
698                )
699                self.dialect_options[dialect_name] = d = _DialectArgDict()
700                d._defaults.update({"*": None})
701                d._non_defaults[arg_name] = kwargs[k]
702            else:
703                if (
704                    "*" not in construct_arg_dictionary
705                    and arg_name not in construct_arg_dictionary
706                ):
707                    raise exc.ArgumentError(
708                        "Argument %r is not accepted by "
709                        "dialect %r on behalf of %r"
710                        % (k, dialect_name, self.__class__)
711                    )
712                else:
713                    construct_arg_dictionary[arg_name] = kwargs[k]
714
715
716class CompileState:
717    """Produces additional object state necessary for a statement to be
718    compiled.
719
720    the :class:`.CompileState` class is at the base of classes that assemble
721    state for a particular statement object that is then used by the
722    compiler.   This process is essentially an extension of the process that
723    the SQLCompiler.visit_XYZ() method takes, however there is an emphasis
724    on converting raw user intent into more organized structures rather than
725    producing string output.   The top-level :class:`.CompileState` for the
726    statement being executed is also accessible when the execution context
727    works with invoking the statement and collecting results.
728
729    The production of :class:`.CompileState` is specific to the compiler,  such
730    as within the :meth:`.SQLCompiler.visit_insert`,
731    :meth:`.SQLCompiler.visit_select` etc. methods.  These methods are also
732    responsible for associating the :class:`.CompileState` with the
733    :class:`.SQLCompiler` itself, if the statement is the "toplevel" statement,
734    i.e. the outermost SQL statement that's actually being executed.
735    There can be other :class:`.CompileState` objects that are not the
736    toplevel, such as when a SELECT subquery or CTE-nested
737    INSERT/UPDATE/DELETE is generated.
738
739    .. versionadded:: 1.4
740
741    """
742
743    __slots__ = ("statement", "_ambiguous_table_name_map")
744
745    plugins: Dict[Tuple[str, str], Type[CompileState]] = {}
746
747    _ambiguous_table_name_map: Optional[_AmbiguousTableNameMap]
748
749    @classmethod
750    def create_for_statement(
751        cls, statement: Executable, compiler: SQLCompiler, **kw: Any
752    ) -> CompileState:
753        # factory construction.
754
755        if statement._propagate_attrs:
756            plugin_name = statement._propagate_attrs.get(
757                "compile_state_plugin", "default"
758            )
759            klass = cls.plugins.get(
760                (plugin_name, statement._effective_plugin_target), None
761            )
762            if klass is None:
763                klass = cls.plugins[
764                    ("default", statement._effective_plugin_target)
765                ]
766
767        else:
768            klass = cls.plugins[
769                ("default", statement._effective_plugin_target)
770            ]
771
772        if klass is cls:
773            return cls(statement, compiler, **kw)
774        else:
775            return klass.create_for_statement(statement, compiler, **kw)
776
777    def __init__(self, statement, compiler, **kw):
778        self.statement = statement
779
780    @classmethod
781    def get_plugin_class(
782        cls, statement: Executable
783    ) -> Optional[Type[CompileState]]:
784        plugin_name = statement._propagate_attrs.get(
785            "compile_state_plugin", None
786        )
787
788        if plugin_name:
789            key = (plugin_name, statement._effective_plugin_target)
790            if key in cls.plugins:
791                return cls.plugins[key]
792
793        # there's no case where we call upon get_plugin_class() and want
794        # to get None back, there should always be a default.  return that
795        # if there was no plugin-specific class  (e.g. Insert with "orm"
796        # plugin)
797        try:
798            return cls.plugins[("default", statement._effective_plugin_target)]
799        except KeyError:
800            return None
801
802    @classmethod
803    def _get_plugin_class_for_plugin(
804        cls, statement: Executable, plugin_name: str
805    ) -> Optional[Type[CompileState]]:
806        try:
807            return cls.plugins[
808                (plugin_name, statement._effective_plugin_target)
809            ]
810        except KeyError:
811            return None
812
813    @classmethod
814    def plugin_for(
815        cls, plugin_name: str, visit_name: str
816    ) -> Callable[[_Fn], _Fn]:
817        def decorate(cls_to_decorate):
818            cls.plugins[(plugin_name, visit_name)] = cls_to_decorate
819            return cls_to_decorate
820
821        return decorate
822
823
824class Generative(HasMemoized):
825    """Provide a method-chaining pattern in conjunction with the
826    @_generative decorator."""
827
828    def _generate(self) -> Self:
829        skip = self._memoized_keys
830        cls = self.__class__
831        s = cls.__new__(cls)
832        if skip:
833            # ensure this iteration remains atomic
834            s.__dict__ = {
835                k: v for k, v in self.__dict__.copy().items() if k not in skip
836            }
837        else:
838            s.__dict__ = self.__dict__.copy()
839        return s
840
841
842class InPlaceGenerative(HasMemoized):
843    """Provide a method-chaining pattern in conjunction with the
844    @_generative decorator that mutates in place."""
845
846    __slots__ = ()
847
848    def _generate(self) -> Self:
849        skip = self._memoized_keys
850        # note __dict__ needs to be in __slots__ if this is used
851        for k in skip:
852            self.__dict__.pop(k, None)
853        return self
854
855
856class HasCompileState(Generative):
857    """A class that has a :class:`.CompileState` associated with it."""
858
859    _compile_state_plugin: Optional[Type[CompileState]] = None
860
861    _attributes: util.immutabledict[str, Any] = util.EMPTY_DICT
862
863    _compile_state_factory = CompileState.create_for_statement
864
865
866class _MetaOptions(type):
867    """metaclass for the Options class.
868
869    This metaclass is actually necessary despite the availability of the
870    ``__init_subclass__()`` hook as this type also provides custom class-level
871    behavior for the ``__add__()`` method.
872
873    """
874
875    _cache_attrs: Tuple[str, ...]
876
877    def __add__(self, other):
878        o1 = self()
879
880        if set(other).difference(self._cache_attrs):
881            raise TypeError(
882                "dictionary contains attributes not covered by "
883                "Options class %s: %r"
884                % (self, set(other).difference(self._cache_attrs))
885            )
886
887        o1.__dict__.update(other)
888        return o1
889
890    if TYPE_CHECKING:
891
892        def __getattr__(self, key: str) -> Any: ...
893
894        def __setattr__(self, key: str, value: Any) -> None: ...
895
896        def __delattr__(self, key: str) -> None: ...
897
898
899class Options(metaclass=_MetaOptions):
900    """A cacheable option dictionary with defaults."""
901
902    __slots__ = ()
903
904    _cache_attrs: Tuple[str, ...]
905
906    def __init_subclass__(cls) -> None:
907        dict_ = cls.__dict__
908        cls._cache_attrs = tuple(
909            sorted(
910                d
911                for d in dict_
912                if not d.startswith("__")
913                and d not in ("_cache_key_traversal",)
914            )
915        )
916        super().__init_subclass__()
917
918    def __init__(self, **kw: Any) -> None:
919        self.__dict__.update(kw)
920
921    def __add__(self, other):
922        o1 = self.__class__.__new__(self.__class__)
923        o1.__dict__.update(self.__dict__)
924
925        if set(other).difference(self._cache_attrs):
926            raise TypeError(
927                "dictionary contains attributes not covered by "
928                "Options class %s: %r"
929                % (self, set(other).difference(self._cache_attrs))
930            )
931
932        o1.__dict__.update(other)
933        return o1
934
935    def __eq__(self, other):
936        # TODO: very inefficient.  This is used only in test suites
937        # right now.
938        for a, b in zip_longest(self._cache_attrs, other._cache_attrs):
939            if getattr(self, a) != getattr(other, b):
940                return False
941        return True
942
943    def __repr__(self) -> str:
944        # TODO: fairly inefficient, used only in debugging right now.
945
946        return "%s(%s)" % (
947            self.__class__.__name__,
948            ", ".join(
949                "%s=%r" % (k, self.__dict__[k])
950                for k in self._cache_attrs
951                if k in self.__dict__
952            ),
953        )
954
955    @classmethod
956    def isinstance(cls, klass: Type[Any]) -> bool:
957        return issubclass(cls, klass)
958
959    @hybridmethod
960    def add_to_element(self, name: str, value: str) -> Any:
961        return self + {name: getattr(self, name) + value}
962
963    @hybridmethod
964    def _state_dict_inst(self) -> Mapping[str, Any]:
965        return self.__dict__
966
967    _state_dict_const: util.immutabledict[str, Any] = util.EMPTY_DICT
968
969    @_state_dict_inst.classlevel
970    def _state_dict(cls) -> Mapping[str, Any]:
971        return cls._state_dict_const
972
973    @classmethod
974    def safe_merge(cls, other: "Options") -> Any:
975        d = other._state_dict()
976
977        # only support a merge with another object of our class
978        # and which does not have attrs that we don't.   otherwise
979        # we risk having state that might not be part of our cache
980        # key strategy
981
982        if (
983            cls is not other.__class__
984            and other._cache_attrs
985            and set(other._cache_attrs).difference(cls._cache_attrs)
986        ):
987            raise TypeError(
988                "other element %r is not empty, is not of type %s, "
989                "and contains attributes not covered here %r"
990                % (
991                    other,
992                    cls,
993                    set(other._cache_attrs).difference(cls._cache_attrs),
994                )
995            )
996        return cls + d
997
998    @classmethod
999    def from_execution_options(
1000        cls,
1001        key: str,
1002        attrs: set[str],
1003        exec_options: Mapping[str, Any],
1004        statement_exec_options: Mapping[str, Any],
1005    ) -> Tuple["Options", Mapping[str, Any]]:
1006        """process Options argument in terms of execution options.
1007
1008
1009        e.g.::
1010
1011            (
1012                load_options,
1013                execution_options,
1014            ) = QueryContext.default_load_options.from_execution_options(
1015                "_sa_orm_load_options",
1016                {"populate_existing", "autoflush", "yield_per"},
1017                execution_options,
1018                statement._execution_options,
1019            )
1020
1021        get back the Options and refresh "_sa_orm_load_options" in the
1022        exec options dict w/ the Options as well
1023
1024        """
1025
1026        # common case is that no options we are looking for are
1027        # in either dictionary, so cancel for that first
1028        check_argnames = attrs.intersection(
1029            set(exec_options).union(statement_exec_options)
1030        )
1031
1032        existing_options = exec_options.get(key, cls)
1033
1034        if check_argnames:
1035            result = {}
1036            for argname in check_argnames:
1037                local = "_" + argname
1038                if argname in exec_options:
1039                    result[local] = exec_options[argname]
1040                elif argname in statement_exec_options:
1041                    result[local] = statement_exec_options[argname]
1042
1043            new_options = existing_options + result
1044            exec_options = util.immutabledict(exec_options).merge_with(
1045                {key: new_options}
1046            )
1047            return new_options, exec_options
1048
1049        else:
1050            return existing_options, exec_options
1051
1052    if TYPE_CHECKING:
1053
1054        def __getattr__(self, key: str) -> Any: ...
1055
1056        def __setattr__(self, key: str, value: Any) -> None: ...
1057
1058        def __delattr__(self, key: str) -> None: ...
1059
1060
1061class CacheableOptions(Options, HasCacheKey):
1062    __slots__ = ()
1063
1064    @hybridmethod
1065    def _gen_cache_key_inst(
1066        self, anon_map: Any, bindparams: List[BindParameter[Any]]
1067    ) -> Optional[Tuple[Any]]:
1068        return HasCacheKey._gen_cache_key(self, anon_map, bindparams)
1069
1070    @_gen_cache_key_inst.classlevel
1071    def _gen_cache_key(
1072        cls, anon_map: "anon_map", bindparams: List[BindParameter[Any]]
1073    ) -> Tuple[CacheableOptions, Any]:
1074        return (cls, ())
1075
1076    @hybridmethod
1077    def _generate_cache_key(self) -> Optional[CacheKey]:
1078        return HasCacheKey._generate_cache_key_for_object(self)
1079
1080
1081class ExecutableOption(HasCopyInternals):
1082    __slots__ = ()
1083
1084    _annotations: _ImmutableExecuteOptions = util.EMPTY_DICT
1085
1086    __visit_name__: str = "executable_option"
1087
1088    _is_has_cache_key: bool = False
1089
1090    _is_core: bool = True
1091
1092    def _clone(self, **kw):
1093        """Create a shallow copy of this ExecutableOption."""
1094        c = self.__class__.__new__(self.__class__)
1095        c.__dict__ = dict(self.__dict__)  # type: ignore
1096        return c
1097
1098
1099class Executable(roles.StatementRole):
1100    """Mark a :class:`_expression.ClauseElement` as supporting execution.
1101
1102    :class:`.Executable` is a superclass for all "statement" types
1103    of objects, including :func:`select`, :func:`delete`, :func:`update`,
1104    :func:`insert`, :func:`text`.
1105
1106    """
1107
1108    supports_execution: bool = True
1109    _execution_options: _ImmutableExecuteOptions = util.EMPTY_DICT
1110    _is_default_generator: bool = False
1111    _with_options: Tuple[ExecutableOption, ...] = ()
1112    _with_context_options: Tuple[
1113        Tuple[Callable[[CompileState], None], Any], ...
1114    ] = ()
1115    _compile_options: Optional[Union[Type[CacheableOptions], CacheableOptions]]
1116
1117    _executable_traverse_internals = [
1118        ("_with_options", InternalTraversal.dp_executable_options),
1119        (
1120            "_with_context_options",
1121            ExtendedInternalTraversal.dp_with_context_options,
1122        ),
1123        ("_propagate_attrs", ExtendedInternalTraversal.dp_propagate_attrs),
1124    ]
1125
1126    is_select: bool = False
1127    is_from_statement: bool = False
1128    is_update: bool = False
1129    is_insert: bool = False
1130    is_text: bool = False
1131    is_delete: bool = False
1132    is_dml: bool = False
1133
1134    if TYPE_CHECKING:
1135        __visit_name__: str
1136
1137        def _compile_w_cache(
1138            self,
1139            dialect: Dialect,
1140            *,
1141            compiled_cache: Optional[CompiledCacheType],
1142            column_keys: List[str],
1143            for_executemany: bool = False,
1144            schema_translate_map: Optional[SchemaTranslateMapType] = None,
1145            **kw: Any,
1146        ) -> Tuple[
1147            Compiled, Optional[Sequence[BindParameter[Any]]], CacheStats
1148        ]: ...
1149
1150        def _execute_on_connection(
1151            self,
1152            connection: Connection,
1153            distilled_params: _CoreMultiExecuteParams,
1154            execution_options: CoreExecuteOptionsParameter,
1155        ) -> CursorResult[Any]: ...
1156
1157        def _execute_on_scalar(
1158            self,
1159            connection: Connection,
1160            distilled_params: _CoreMultiExecuteParams,
1161            execution_options: CoreExecuteOptionsParameter,
1162        ) -> Any: ...
1163
1164    @util.ro_non_memoized_property
1165    def _all_selected_columns(self) -> _SelectIterable:
1166        raise NotImplementedError()
1167
1168    @property
1169    def _effective_plugin_target(self) -> str:
1170        return self.__visit_name__
1171
1172    @_generative
1173    def options(self, *options: ExecutableOption) -> Self:
1174        """Apply options to this statement.
1175
1176        In the general sense, options are any kind of Python object
1177        that can be interpreted by the SQL compiler for the statement.
1178        These options can be consumed by specific dialects or specific kinds
1179        of compilers.
1180
1181        The most commonly known kind of option are the ORM level options
1182        that apply "eager load" and other loading behaviors to an ORM
1183        query.   However, options can theoretically be used for many other
1184        purposes.
1185
1186        For background on specific kinds of options for specific kinds of
1187        statements, refer to the documentation for those option objects.
1188
1189        .. versionchanged:: 1.4 - added :meth:`.Executable.options` to
1190           Core statement objects towards the goal of allowing unified
1191           Core / ORM querying capabilities.
1192
1193        .. seealso::
1194
1195            :ref:`loading_columns` - refers to options specific to the usage
1196            of ORM queries
1197
1198            :ref:`relationship_loader_options` - refers to options specific
1199            to the usage of ORM queries
1200

Showing the first 1,200 of 2294 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai