Team Ai
Datasetpublic

codekingpro/portable-devtools

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

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

codekingpro/portable-devtools · Team Ai