first add files
This commit is contained in:
150
lib/sqlalchemy/sql/__init__.py
Normal file
150
lib/sqlalchemy/sql/__init__.py
Normal file
@@ -0,0 +1,150 @@
|
||||
# sql/__init__.py
|
||||
# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
|
||||
# <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
from .base import Executable
|
||||
from .compiler import COLLECT_CARTESIAN_PRODUCTS
|
||||
from .compiler import FROM_LINTING
|
||||
from .compiler import NO_LINTING
|
||||
from .compiler import WARN_LINTING
|
||||
from .expression import Alias
|
||||
from .expression import alias
|
||||
from .expression import all_
|
||||
from .expression import and_
|
||||
from .expression import any_
|
||||
from .expression import asc
|
||||
from .expression import between
|
||||
from .expression import bindparam
|
||||
from .expression import case
|
||||
from .expression import cast
|
||||
from .expression import ClauseElement
|
||||
from .expression import collate
|
||||
from .expression import column
|
||||
from .expression import ColumnCollection
|
||||
from .expression import ColumnElement
|
||||
from .expression import CompoundSelect
|
||||
from .expression import cte
|
||||
from .expression import Delete
|
||||
from .expression import delete
|
||||
from .expression import desc
|
||||
from .expression import distinct
|
||||
from .expression import except_
|
||||
from .expression import except_all
|
||||
from .expression import exists
|
||||
from .expression import extract
|
||||
from .expression import false
|
||||
from .expression import False_
|
||||
from .expression import FromClause
|
||||
from .expression import func
|
||||
from .expression import funcfilter
|
||||
from .expression import Insert
|
||||
from .expression import insert
|
||||
from .expression import intersect
|
||||
from .expression import intersect_all
|
||||
from .expression import Join
|
||||
from .expression import join
|
||||
from .expression import label
|
||||
from .expression import LABEL_STYLE_DEFAULT
|
||||
from .expression import LABEL_STYLE_DISAMBIGUATE_ONLY
|
||||
from .expression import LABEL_STYLE_NONE
|
||||
from .expression import LABEL_STYLE_TABLENAME_PLUS_COL
|
||||
from .expression import lambda_stmt
|
||||
from .expression import LambdaElement
|
||||
from .expression import lateral
|
||||
from .expression import literal
|
||||
from .expression import literal_column
|
||||
from .expression import modifier
|
||||
from .expression import not_
|
||||
from .expression import null
|
||||
from .expression import nulls_first
|
||||
from .expression import nulls_last
|
||||
from .expression import nullsfirst
|
||||
from .expression import nullslast
|
||||
from .expression import or_
|
||||
from .expression import outerjoin
|
||||
from .expression import outparam
|
||||
from .expression import over
|
||||
from .expression import quoted_name
|
||||
from .expression import Select
|
||||
from .expression import select
|
||||
from .expression import Selectable
|
||||
from .expression import StatementLambdaElement
|
||||
from .expression import Subquery
|
||||
from .expression import subquery
|
||||
from .expression import table
|
||||
from .expression import TableClause
|
||||
from .expression import TableSample
|
||||
from .expression import tablesample
|
||||
from .expression import text
|
||||
from .expression import true
|
||||
from .expression import True_
|
||||
from .expression import tuple_
|
||||
from .expression import type_coerce
|
||||
from .expression import union
|
||||
from .expression import union_all
|
||||
from .expression import Update
|
||||
from .expression import update
|
||||
from .expression import Values
|
||||
from .expression import values
|
||||
from .expression import within_group
|
||||
from .visitors import ClauseVisitor
|
||||
|
||||
|
||||
def __go(lcls):
|
||||
global __all__
|
||||
from .. import util as _sa_util
|
||||
|
||||
import inspect as _inspect
|
||||
|
||||
__all__ = sorted(
|
||||
name
|
||||
for name, obj in lcls.items()
|
||||
if not (name.startswith("_") or _inspect.ismodule(obj))
|
||||
)
|
||||
|
||||
from .annotation import _prepare_annotations
|
||||
from .annotation import Annotated
|
||||
from .elements import AnnotatedColumnElement
|
||||
from .elements import ClauseList
|
||||
from .selectable import AnnotatedFromClause
|
||||
|
||||
# from .traversals import _preconfigure_traversals
|
||||
|
||||
from . import base
|
||||
from . import coercions
|
||||
from . import elements
|
||||
from . import events
|
||||
from . import lambdas
|
||||
from . import selectable
|
||||
from . import schema
|
||||
from . import sqltypes
|
||||
from . import traversals
|
||||
from . import type_api
|
||||
|
||||
base.coercions = elements.coercions = coercions
|
||||
base.elements = elements
|
||||
base.type_api = type_api
|
||||
coercions.elements = elements
|
||||
coercions.lambdas = lambdas
|
||||
coercions.schema = schema
|
||||
coercions.selectable = selectable
|
||||
coercions.sqltypes = sqltypes
|
||||
coercions.traversals = traversals
|
||||
|
||||
_prepare_annotations(ColumnElement, AnnotatedColumnElement)
|
||||
_prepare_annotations(FromClause, AnnotatedFromClause)
|
||||
_prepare_annotations(ClauseList, Annotated)
|
||||
|
||||
# this is expensive at import time; elements that are used can create
|
||||
# their traversals on demand
|
||||
# _preconfigure_traversals(ClauseElement)
|
||||
|
||||
_sa_util.preloaded.import_prefix("sqlalchemy.sql")
|
||||
|
||||
from . import naming
|
||||
|
||||
|
||||
__go(locals())
|
||||
364
lib/sqlalchemy/sql/annotation.py
Normal file
364
lib/sqlalchemy/sql/annotation.py
Normal file
@@ -0,0 +1,364 @@
|
||||
# sql/annotation.py
|
||||
# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
|
||||
# <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""The :class:`.Annotated` class and related routines; creates hash-equivalent
|
||||
copies of SQL constructs which contain context-specific markers and
|
||||
associations.
|
||||
|
||||
"""
|
||||
|
||||
from . import operators
|
||||
from .base import HasCacheKey
|
||||
from .traversals import anon_map
|
||||
from .visitors import InternalTraversal
|
||||
from .. import util
|
||||
|
||||
EMPTY_ANNOTATIONS = util.immutabledict()
|
||||
|
||||
|
||||
class SupportsAnnotations(object):
|
||||
_annotations = EMPTY_ANNOTATIONS
|
||||
|
||||
@util.memoized_property
|
||||
def _annotations_cache_key(self):
|
||||
anon_map_ = anon_map()
|
||||
return (
|
||||
"_annotations",
|
||||
tuple(
|
||||
(
|
||||
key,
|
||||
value._gen_cache_key(anon_map_, [])
|
||||
if isinstance(value, HasCacheKey)
|
||||
else value,
|
||||
)
|
||||
for key, value in [
|
||||
(key, self._annotations[key])
|
||||
for key in sorted(self._annotations)
|
||||
]
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class SupportsCloneAnnotations(SupportsAnnotations):
|
||||
|
||||
_clone_annotations_traverse_internals = [
|
||||
("_annotations", InternalTraversal.dp_annotations_key)
|
||||
]
|
||||
|
||||
def _annotate(self, values):
|
||||
"""return a copy of this ClauseElement with annotations
|
||||
updated by the given dictionary.
|
||||
|
||||
"""
|
||||
new = self._clone()
|
||||
new._annotations = new._annotations.union(values)
|
||||
new.__dict__.pop("_annotations_cache_key", None)
|
||||
new.__dict__.pop("_generate_cache_key", None)
|
||||
return new
|
||||
|
||||
def _with_annotations(self, values):
|
||||
"""return a copy of this ClauseElement with annotations
|
||||
replaced by the given dictionary.
|
||||
|
||||
"""
|
||||
new = self._clone()
|
||||
new._annotations = util.immutabledict(values)
|
||||
new.__dict__.pop("_annotations_cache_key", None)
|
||||
new.__dict__.pop("_generate_cache_key", None)
|
||||
return new
|
||||
|
||||
def _deannotate(self, values=None, clone=False):
|
||||
"""return a copy of this :class:`_expression.ClauseElement`
|
||||
with annotations
|
||||
removed.
|
||||
|
||||
:param values: optional tuple of individual values
|
||||
to remove.
|
||||
|
||||
"""
|
||||
if clone or self._annotations:
|
||||
# clone is used when we are also copying
|
||||
# the expression for a deep deannotation
|
||||
new = self._clone()
|
||||
new._annotations = util.immutabledict()
|
||||
new.__dict__.pop("_annotations_cache_key", None)
|
||||
return new
|
||||
else:
|
||||
return self
|
||||
|
||||
|
||||
class SupportsWrappingAnnotations(SupportsAnnotations):
|
||||
def _annotate(self, values):
|
||||
"""return a copy of this ClauseElement with annotations
|
||||
updated by the given dictionary.
|
||||
|
||||
"""
|
||||
return Annotated(self, values)
|
||||
|
||||
def _with_annotations(self, values):
|
||||
"""return a copy of this ClauseElement with annotations
|
||||
replaced by the given dictionary.
|
||||
|
||||
"""
|
||||
return Annotated(self, values)
|
||||
|
||||
def _deannotate(self, values=None, clone=False):
|
||||
"""return a copy of this :class:`_expression.ClauseElement`
|
||||
with annotations
|
||||
removed.
|
||||
|
||||
:param values: optional tuple of individual values
|
||||
to remove.
|
||||
|
||||
"""
|
||||
if clone:
|
||||
s = self._clone()
|
||||
return s
|
||||
else:
|
||||
return self
|
||||
|
||||
|
||||
class Annotated(object):
|
||||
"""clones a SupportsAnnotated and applies an 'annotations' dictionary.
|
||||
|
||||
Unlike regular clones, this clone also mimics __hash__() and
|
||||
__cmp__() of the original element so that it takes its place
|
||||
in hashed collections.
|
||||
|
||||
A reference to the original element is maintained, for the important
|
||||
reason of keeping its hash value current. When GC'ed, the
|
||||
hash value may be reused, causing conflicts.
|
||||
|
||||
.. note:: The rationale for Annotated producing a brand new class,
|
||||
rather than placing the functionality directly within ClauseElement,
|
||||
is **performance**. The __hash__() method is absent on plain
|
||||
ClauseElement which leads to significantly reduced function call
|
||||
overhead, as the use of sets and dictionaries against ClauseElement
|
||||
objects is prevalent, but most are not "annotated".
|
||||
|
||||
"""
|
||||
|
||||
_is_column_operators = False
|
||||
|
||||
def __new__(cls, *args):
|
||||
if not args:
|
||||
# clone constructor
|
||||
return object.__new__(cls)
|
||||
else:
|
||||
element, values = args
|
||||
# pull appropriate subclass from registry of annotated
|
||||
# classes
|
||||
try:
|
||||
cls = annotated_classes[element.__class__]
|
||||
except KeyError:
|
||||
cls = _new_annotation_type(element.__class__, cls)
|
||||
return object.__new__(cls)
|
||||
|
||||
def __init__(self, element, values):
|
||||
self.__dict__ = element.__dict__.copy()
|
||||
self.__dict__.pop("_annotations_cache_key", None)
|
||||
self.__dict__.pop("_generate_cache_key", None)
|
||||
self.__element = element
|
||||
self._annotations = util.immutabledict(values)
|
||||
self._hash = hash(element)
|
||||
|
||||
def _annotate(self, values):
|
||||
_values = self._annotations.union(values)
|
||||
return self._with_annotations(_values)
|
||||
|
||||
def _with_annotations(self, values):
|
||||
clone = self.__class__.__new__(self.__class__)
|
||||
clone.__dict__ = self.__dict__.copy()
|
||||
clone.__dict__.pop("_annotations_cache_key", None)
|
||||
clone.__dict__.pop("_generate_cache_key", None)
|
||||
clone._annotations = values
|
||||
return clone
|
||||
|
||||
def _deannotate(self, values=None, clone=True):
|
||||
if values is None:
|
||||
return self.__element
|
||||
else:
|
||||
return self._with_annotations(
|
||||
util.immutabledict(
|
||||
{
|
||||
key: value
|
||||
for key, value in self._annotations.items()
|
||||
if key not in values
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
def _compiler_dispatch(self, visitor, **kw):
|
||||
return self.__element.__class__._compiler_dispatch(self, visitor, **kw)
|
||||
|
||||
@property
|
||||
def _constructor(self):
|
||||
return self.__element._constructor
|
||||
|
||||
def _clone(self, **kw):
|
||||
clone = self.__element._clone(**kw)
|
||||
if clone is self.__element:
|
||||
# detect immutable, don't change anything
|
||||
return self
|
||||
else:
|
||||
# update the clone with any changes that have occurred
|
||||
# to this object's __dict__.
|
||||
clone.__dict__.update(self.__dict__)
|
||||
return self.__class__(clone, self._annotations)
|
||||
|
||||
def __reduce__(self):
|
||||
return self.__class__, (self.__element, self._annotations)
|
||||
|
||||
def __hash__(self):
|
||||
return self._hash
|
||||
|
||||
def __eq__(self, other):
|
||||
if self._is_column_operators:
|
||||
return self.__element.__class__.__eq__(self, other)
|
||||
else:
|
||||
return hash(other) == hash(self)
|
||||
|
||||
@property
|
||||
def entity_namespace(self):
|
||||
if "entity_namespace" in self._annotations:
|
||||
return self._annotations["entity_namespace"].entity_namespace
|
||||
else:
|
||||
return self.__element.entity_namespace
|
||||
|
||||
|
||||
# hard-generate Annotated subclasses. this technique
|
||||
# is used instead of on-the-fly types (i.e. type.__new__())
|
||||
# so that the resulting objects are pickleable; additionally, other
|
||||
# decisions can be made up front about the type of object being annotated
|
||||
# just once per class rather than per-instance.
|
||||
annotated_classes = {}
|
||||
|
||||
|
||||
def _deep_annotate(
|
||||
element, annotations, exclude=None, detect_subquery_cols=False
|
||||
):
|
||||
"""Deep copy the given ClauseElement, annotating each element
|
||||
with the given annotations dictionary.
|
||||
|
||||
Elements within the exclude collection will be cloned but not annotated.
|
||||
|
||||
"""
|
||||
|
||||
# annotated objects hack the __hash__() method so if we want to
|
||||
# uniquely process them we have to use id()
|
||||
|
||||
cloned_ids = {}
|
||||
|
||||
def clone(elem, **kw):
|
||||
kw["detect_subquery_cols"] = detect_subquery_cols
|
||||
id_ = id(elem)
|
||||
|
||||
if id_ in cloned_ids:
|
||||
return cloned_ids[id_]
|
||||
|
||||
if (
|
||||
exclude
|
||||
and hasattr(elem, "proxy_set")
|
||||
and elem.proxy_set.intersection(exclude)
|
||||
):
|
||||
newelem = elem._clone(clone=clone, **kw)
|
||||
elif annotations != elem._annotations:
|
||||
if detect_subquery_cols and elem._is_immutable:
|
||||
newelem = elem._clone(clone=clone, **kw)._annotate(annotations)
|
||||
else:
|
||||
newelem = elem._annotate(annotations)
|
||||
else:
|
||||
newelem = elem
|
||||
newelem._copy_internals(clone=clone)
|
||||
cloned_ids[id_] = newelem
|
||||
return newelem
|
||||
|
||||
if element is not None:
|
||||
element = clone(element)
|
||||
clone = None # remove gc cycles
|
||||
return element
|
||||
|
||||
|
||||
def _deep_deannotate(element, values=None):
|
||||
"""Deep copy the given element, removing annotations."""
|
||||
|
||||
cloned = {}
|
||||
|
||||
def clone(elem, **kw):
|
||||
if values:
|
||||
key = id(elem)
|
||||
else:
|
||||
key = elem
|
||||
|
||||
if key not in cloned:
|
||||
newelem = elem._deannotate(values=values, clone=True)
|
||||
newelem._copy_internals(clone=clone)
|
||||
cloned[key] = newelem
|
||||
return newelem
|
||||
else:
|
||||
return cloned[key]
|
||||
|
||||
if element is not None:
|
||||
element = clone(element)
|
||||
clone = None # remove gc cycles
|
||||
return element
|
||||
|
||||
|
||||
def _shallow_annotate(element, annotations):
|
||||
"""Annotate the given ClauseElement and copy its internals so that
|
||||
internal objects refer to the new annotated object.
|
||||
|
||||
Basically used to apply a "don't traverse" annotation to a
|
||||
selectable, without digging throughout the whole
|
||||
structure wasting time.
|
||||
"""
|
||||
element = element._annotate(annotations)
|
||||
element._copy_internals()
|
||||
return element
|
||||
|
||||
|
||||
def _new_annotation_type(cls, base_cls):
|
||||
if issubclass(cls, Annotated):
|
||||
return cls
|
||||
elif cls in annotated_classes:
|
||||
return annotated_classes[cls]
|
||||
|
||||
for super_ in cls.__mro__:
|
||||
# check if an Annotated subclass more specific than
|
||||
# the given base_cls is already registered, such
|
||||
# as AnnotatedColumnElement.
|
||||
if super_ in annotated_classes:
|
||||
base_cls = annotated_classes[super_]
|
||||
break
|
||||
|
||||
annotated_classes[cls] = anno_cls = type(
|
||||
"Annotated%s" % cls.__name__, (base_cls, cls), {}
|
||||
)
|
||||
globals()["Annotated%s" % cls.__name__] = anno_cls
|
||||
|
||||
if "_traverse_internals" in cls.__dict__:
|
||||
anno_cls._traverse_internals = list(cls._traverse_internals) + [
|
||||
("_annotations", InternalTraversal.dp_annotations_key)
|
||||
]
|
||||
elif cls.__dict__.get("inherit_cache", False):
|
||||
anno_cls._traverse_internals = list(cls._traverse_internals) + [
|
||||
("_annotations", InternalTraversal.dp_annotations_key)
|
||||
]
|
||||
|
||||
# some classes include this even if they have traverse_internals
|
||||
# e.g. BindParameter, add it if present.
|
||||
if cls.__dict__.get("inherit_cache", False):
|
||||
anno_cls.inherit_cache = True
|
||||
|
||||
anno_cls._is_column_operators = issubclass(cls, operators.ColumnOperators)
|
||||
|
||||
return anno_cls
|
||||
|
||||
|
||||
def _prepare_annotations(target_hierarchy, base_cls):
|
||||
for cls in util.walk_subclasses(target_hierarchy):
|
||||
_new_annotation_type(cls, base_cls)
|
||||
1702
lib/sqlalchemy/sql/base.py
Normal file
1702
lib/sqlalchemy/sql/base.py
Normal file
File diff suppressed because it is too large
Load Diff
1096
lib/sqlalchemy/sql/coercions.py
Normal file
1096
lib/sqlalchemy/sql/coercions.py
Normal file
File diff suppressed because it is too large
Load Diff
5525
lib/sqlalchemy/sql/compiler.py
Normal file
5525
lib/sqlalchemy/sql/compiler.py
Normal file
File diff suppressed because it is too large
Load Diff
1091
lib/sqlalchemy/sql/crud.py
Normal file
1091
lib/sqlalchemy/sql/crud.py
Normal file
File diff suppressed because it is too large
Load Diff
1341
lib/sqlalchemy/sql/ddl.py
Normal file
1341
lib/sqlalchemy/sql/ddl.py
Normal file
File diff suppressed because it is too large
Load Diff
360
lib/sqlalchemy/sql/default_comparator.py
Normal file
360
lib/sqlalchemy/sql/default_comparator.py
Normal file
@@ -0,0 +1,360 @@
|
||||
# sql/default_comparator.py
|
||||
# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
|
||||
# <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""Default implementation of SQL comparison operations.
|
||||
"""
|
||||
|
||||
|
||||
from . import coercions
|
||||
from . import operators
|
||||
from . import roles
|
||||
from . import type_api
|
||||
from .elements import and_
|
||||
from .elements import BinaryExpression
|
||||
from .elements import ClauseList
|
||||
from .elements import collate
|
||||
from .elements import CollectionAggregate
|
||||
from .elements import False_
|
||||
from .elements import Null
|
||||
from .elements import or_
|
||||
from .elements import True_
|
||||
from .elements import UnaryExpression
|
||||
from .. import exc
|
||||
from .. import util
|
||||
|
||||
|
||||
def _boolean_compare(
|
||||
expr,
|
||||
op,
|
||||
obj,
|
||||
negate=None,
|
||||
reverse=False,
|
||||
_python_is_types=(util.NoneType, bool),
|
||||
_any_all_expr=False,
|
||||
result_type=None,
|
||||
**kwargs
|
||||
):
|
||||
|
||||
if result_type is None:
|
||||
result_type = type_api.BOOLEANTYPE
|
||||
|
||||
if isinstance(obj, _python_is_types + (Null, True_, False_)):
|
||||
# allow x ==/!= True/False to be treated as a literal.
|
||||
# this comes out to "== / != true/false" or "1/0" if those
|
||||
# constants aren't supported and works on all platforms
|
||||
if op in (operators.eq, operators.ne) and isinstance(
|
||||
obj, (bool, True_, False_)
|
||||
):
|
||||
return BinaryExpression(
|
||||
expr,
|
||||
coercions.expect(roles.ConstExprRole, obj),
|
||||
op,
|
||||
type_=result_type,
|
||||
negate=negate,
|
||||
modifiers=kwargs,
|
||||
)
|
||||
elif op in (
|
||||
operators.is_distinct_from,
|
||||
operators.is_not_distinct_from,
|
||||
):
|
||||
return BinaryExpression(
|
||||
expr,
|
||||
coercions.expect(roles.ConstExprRole, obj),
|
||||
op,
|
||||
type_=result_type,
|
||||
negate=negate,
|
||||
modifiers=kwargs,
|
||||
)
|
||||
elif _any_all_expr:
|
||||
obj = coercions.expect(
|
||||
roles.ConstExprRole, element=obj, operator=op, expr=expr
|
||||
)
|
||||
else:
|
||||
# all other None uses IS, IS NOT
|
||||
if op in (operators.eq, operators.is_):
|
||||
return BinaryExpression(
|
||||
expr,
|
||||
coercions.expect(roles.ConstExprRole, obj),
|
||||
operators.is_,
|
||||
negate=operators.is_not,
|
||||
type_=result_type,
|
||||
)
|
||||
elif op in (operators.ne, operators.is_not):
|
||||
return BinaryExpression(
|
||||
expr,
|
||||
coercions.expect(roles.ConstExprRole, obj),
|
||||
operators.is_not,
|
||||
negate=operators.is_,
|
||||
type_=result_type,
|
||||
)
|
||||
else:
|
||||
raise exc.ArgumentError(
|
||||
"Only '=', '!=', 'is_()', 'is_not()', "
|
||||
"'is_distinct_from()', 'is_not_distinct_from()' "
|
||||
"operators can be used with None/True/False"
|
||||
)
|
||||
else:
|
||||
obj = coercions.expect(
|
||||
roles.BinaryElementRole, element=obj, operator=op, expr=expr
|
||||
)
|
||||
|
||||
if reverse:
|
||||
return BinaryExpression(
|
||||
obj, expr, op, type_=result_type, negate=negate, modifiers=kwargs
|
||||
)
|
||||
else:
|
||||
return BinaryExpression(
|
||||
expr, obj, op, type_=result_type, negate=negate, modifiers=kwargs
|
||||
)
|
||||
|
||||
|
||||
def _custom_op_operate(expr, op, obj, reverse=False, result_type=None, **kw):
|
||||
if result_type is None:
|
||||
if op.return_type:
|
||||
result_type = op.return_type
|
||||
elif op.is_comparison:
|
||||
result_type = type_api.BOOLEANTYPE
|
||||
|
||||
return _binary_operate(
|
||||
expr, op, obj, reverse=reverse, result_type=result_type, **kw
|
||||
)
|
||||
|
||||
|
||||
def _binary_operate(expr, op, obj, reverse=False, result_type=None, **kw):
|
||||
obj = coercions.expect(
|
||||
roles.BinaryElementRole, obj, expr=expr, operator=op
|
||||
)
|
||||
|
||||
if reverse:
|
||||
left, right = obj, expr
|
||||
else:
|
||||
left, right = expr, obj
|
||||
|
||||
if result_type is None:
|
||||
op, result_type = left.comparator._adapt_expression(
|
||||
op, right.comparator
|
||||
)
|
||||
|
||||
return BinaryExpression(left, right, op, type_=result_type, modifiers=kw)
|
||||
|
||||
|
||||
def _conjunction_operate(expr, op, other, **kw):
|
||||
if op is operators.and_:
|
||||
return and_(expr, other)
|
||||
elif op is operators.or_:
|
||||
return or_(expr, other)
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def _scalar(expr, op, fn, **kw):
|
||||
return fn(expr)
|
||||
|
||||
|
||||
def _in_impl(expr, op, seq_or_selectable, negate_op, **kw):
|
||||
seq_or_selectable = coercions.expect(
|
||||
roles.InElementRole, seq_or_selectable, expr=expr, operator=op
|
||||
)
|
||||
if "in_ops" in seq_or_selectable._annotations:
|
||||
op, negate_op = seq_or_selectable._annotations["in_ops"]
|
||||
|
||||
return _boolean_compare(
|
||||
expr, op, seq_or_selectable, negate=negate_op, **kw
|
||||
)
|
||||
|
||||
|
||||
def _getitem_impl(expr, op, other, **kw):
|
||||
if (
|
||||
isinstance(expr.type, type_api.INDEXABLE)
|
||||
or isinstance(expr.type, type_api.TypeDecorator)
|
||||
and isinstance(expr.type.impl, type_api.INDEXABLE)
|
||||
):
|
||||
other = coercions.expect(
|
||||
roles.BinaryElementRole, other, expr=expr, operator=op
|
||||
)
|
||||
return _binary_operate(expr, op, other, **kw)
|
||||
else:
|
||||
_unsupported_impl(expr, op, other, **kw)
|
||||
|
||||
|
||||
def _unsupported_impl(expr, op, *arg, **kw):
|
||||
raise NotImplementedError(
|
||||
"Operator '%s' is not supported on " "this expression" % op.__name__
|
||||
)
|
||||
|
||||
|
||||
def _inv_impl(expr, op, **kw):
|
||||
"""See :meth:`.ColumnOperators.__inv__`."""
|
||||
|
||||
# undocumented element currently used by the ORM for
|
||||
# relationship.contains()
|
||||
if hasattr(expr, "negation_clause"):
|
||||
return expr.negation_clause
|
||||
else:
|
||||
return expr._negate()
|
||||
|
||||
|
||||
def _neg_impl(expr, op, **kw):
|
||||
"""See :meth:`.ColumnOperators.__neg__`."""
|
||||
return UnaryExpression(expr, operator=operators.neg, type_=expr.type)
|
||||
|
||||
|
||||
def _match_impl(expr, op, other, **kw):
|
||||
"""See :meth:`.ColumnOperators.match`."""
|
||||
|
||||
return _boolean_compare(
|
||||
expr,
|
||||
operators.match_op,
|
||||
coercions.expect(
|
||||
roles.BinaryElementRole,
|
||||
other,
|
||||
expr=expr,
|
||||
operator=operators.match_op,
|
||||
),
|
||||
result_type=type_api.MATCHTYPE,
|
||||
negate=operators.not_match_op
|
||||
if op is operators.match_op
|
||||
else operators.match_op,
|
||||
**kw
|
||||
)
|
||||
|
||||
|
||||
def _distinct_impl(expr, op, **kw):
|
||||
"""See :meth:`.ColumnOperators.distinct`."""
|
||||
return UnaryExpression(
|
||||
expr, operator=operators.distinct_op, type_=expr.type
|
||||
)
|
||||
|
||||
|
||||
def _between_impl(expr, op, cleft, cright, **kw):
|
||||
"""See :meth:`.ColumnOperators.between`."""
|
||||
return BinaryExpression(
|
||||
expr,
|
||||
ClauseList(
|
||||
coercions.expect(
|
||||
roles.BinaryElementRole,
|
||||
cleft,
|
||||
expr=expr,
|
||||
operator=operators.and_,
|
||||
),
|
||||
coercions.expect(
|
||||
roles.BinaryElementRole,
|
||||
cright,
|
||||
expr=expr,
|
||||
operator=operators.and_,
|
||||
),
|
||||
operator=operators.and_,
|
||||
group=False,
|
||||
group_contents=False,
|
||||
),
|
||||
op,
|
||||
negate=operators.not_between_op
|
||||
if op is operators.between_op
|
||||
else operators.between_op,
|
||||
modifiers=kw,
|
||||
)
|
||||
|
||||
|
||||
def _collate_impl(expr, op, other, **kw):
|
||||
return collate(expr, other)
|
||||
|
||||
|
||||
def _regexp_match_impl(expr, op, pattern, flags, **kw):
|
||||
if flags is not None:
|
||||
flags = coercions.expect(
|
||||
roles.BinaryElementRole,
|
||||
flags,
|
||||
expr=expr,
|
||||
operator=operators.regexp_replace_op,
|
||||
)
|
||||
return _boolean_compare(
|
||||
expr,
|
||||
op,
|
||||
pattern,
|
||||
flags=flags,
|
||||
negate=operators.not_regexp_match_op
|
||||
if op is operators.regexp_match_op
|
||||
else operators.regexp_match_op,
|
||||
**kw
|
||||
)
|
||||
|
||||
|
||||
def _regexp_replace_impl(expr, op, pattern, replacement, flags, **kw):
|
||||
replacement = coercions.expect(
|
||||
roles.BinaryElementRole,
|
||||
replacement,
|
||||
expr=expr,
|
||||
operator=operators.regexp_replace_op,
|
||||
)
|
||||
if flags is not None:
|
||||
flags = coercions.expect(
|
||||
roles.BinaryElementRole,
|
||||
flags,
|
||||
expr=expr,
|
||||
operator=operators.regexp_replace_op,
|
||||
)
|
||||
return _binary_operate(
|
||||
expr, op, pattern, replacement=replacement, flags=flags, **kw
|
||||
)
|
||||
|
||||
|
||||
# a mapping of operators with the method they use, along with
|
||||
# their negated operator for comparison operators
|
||||
operator_lookup = {
|
||||
"and_": (_conjunction_operate,),
|
||||
"or_": (_conjunction_operate,),
|
||||
"inv": (_inv_impl,),
|
||||
"add": (_binary_operate,),
|
||||
"mul": (_binary_operate,),
|
||||
"sub": (_binary_operate,),
|
||||
"div": (_binary_operate,),
|
||||
"mod": (_binary_operate,),
|
||||
"truediv": (_binary_operate,),
|
||||
"custom_op": (_custom_op_operate,),
|
||||
"json_path_getitem_op": (_binary_operate,),
|
||||
"json_getitem_op": (_binary_operate,),
|
||||
"concat_op": (_binary_operate,),
|
||||
"any_op": (_scalar, CollectionAggregate._create_any),
|
||||
"all_op": (_scalar, CollectionAggregate._create_all),
|
||||
"lt": (_boolean_compare, operators.ge),
|
||||
"le": (_boolean_compare, operators.gt),
|
||||
"ne": (_boolean_compare, operators.eq),
|
||||
"gt": (_boolean_compare, operators.le),
|
||||
"ge": (_boolean_compare, operators.lt),
|
||||
"eq": (_boolean_compare, operators.ne),
|
||||
"is_distinct_from": (_boolean_compare, operators.is_not_distinct_from),
|
||||
"is_not_distinct_from": (_boolean_compare, operators.is_distinct_from),
|
||||
"like_op": (_boolean_compare, operators.not_like_op),
|
||||
"ilike_op": (_boolean_compare, operators.not_ilike_op),
|
||||
"not_like_op": (_boolean_compare, operators.like_op),
|
||||
"not_ilike_op": (_boolean_compare, operators.ilike_op),
|
||||
"contains_op": (_boolean_compare, operators.not_contains_op),
|
||||
"startswith_op": (_boolean_compare, operators.not_startswith_op),
|
||||
"endswith_op": (_boolean_compare, operators.not_endswith_op),
|
||||
"desc_op": (_scalar, UnaryExpression._create_desc),
|
||||
"asc_op": (_scalar, UnaryExpression._create_asc),
|
||||
"nulls_first_op": (_scalar, UnaryExpression._create_nulls_first),
|
||||
"nulls_last_op": (_scalar, UnaryExpression._create_nulls_last),
|
||||
"in_op": (_in_impl, operators.not_in_op),
|
||||
"not_in_op": (_in_impl, operators.in_op),
|
||||
"is_": (_boolean_compare, operators.is_),
|
||||
"is_not": (_boolean_compare, operators.is_not),
|
||||
"collate": (_collate_impl,),
|
||||
"match_op": (_match_impl,),
|
||||
"not_match_op": (_match_impl,),
|
||||
"distinct_op": (_distinct_impl,),
|
||||
"between_op": (_between_impl,),
|
||||
"not_between_op": (_between_impl,),
|
||||
"neg": (_neg_impl,),
|
||||
"getitem": (_getitem_impl,),
|
||||
"lshift": (_unsupported_impl,),
|
||||
"rshift": (_unsupported_impl,),
|
||||
"contains": (_unsupported_impl,),
|
||||
"regexp_match_op": (_regexp_match_impl,),
|
||||
"not_regexp_match_op": (_regexp_match_impl,),
|
||||
"regexp_replace_op": (_regexp_replace_impl,),
|
||||
}
|
||||
1514
lib/sqlalchemy/sql/dml.py
Normal file
1514
lib/sqlalchemy/sql/dml.py
Normal file
File diff suppressed because it is too large
Load Diff
5415
lib/sqlalchemy/sql/elements.py
Normal file
5415
lib/sqlalchemy/sql/elements.py
Normal file
File diff suppressed because it is too large
Load Diff
331
lib/sqlalchemy/sql/events.py
Normal file
331
lib/sqlalchemy/sql/events.py
Normal file
@@ -0,0 +1,331 @@
|
||||
# sqlalchemy/sql/events.py
|
||||
# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
|
||||
# <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
from .base import SchemaEventTarget
|
||||
from .. import event
|
||||
|
||||
|
||||
class DDLEvents(event.Events):
|
||||
"""
|
||||
Define event listeners for schema objects,
|
||||
that is, :class:`.SchemaItem` and other :class:`.SchemaEventTarget`
|
||||
subclasses, including :class:`_schema.MetaData`, :class:`_schema.Table`,
|
||||
:class:`_schema.Column`.
|
||||
|
||||
:class:`_schema.MetaData` and :class:`_schema.Table` support events
|
||||
specifically regarding when CREATE and DROP
|
||||
DDL is emitted to the database.
|
||||
|
||||
Attachment events are also provided to customize
|
||||
behavior whenever a child schema element is associated
|
||||
with a parent, such as, when a :class:`_schema.Column` is associated
|
||||
with its :class:`_schema.Table`, when a
|
||||
:class:`_schema.ForeignKeyConstraint`
|
||||
is associated with a :class:`_schema.Table`, etc.
|
||||
|
||||
Example using the ``after_create`` event::
|
||||
|
||||
from sqlalchemy import event
|
||||
from sqlalchemy import Table, Column, Metadata, Integer
|
||||
|
||||
m = MetaData()
|
||||
some_table = Table('some_table', m, Column('data', Integer))
|
||||
|
||||
def after_create(target, connection, **kw):
|
||||
connection.execute(text(
|
||||
"ALTER TABLE %s SET name=foo_%s" % (target.name, target.name)
|
||||
))
|
||||
|
||||
event.listen(some_table, "after_create", after_create)
|
||||
|
||||
DDL events integrate closely with the
|
||||
:class:`.DDL` class and the :class:`.DDLElement` hierarchy
|
||||
of DDL clause constructs, which are themselves appropriate
|
||||
as listener callables::
|
||||
|
||||
from sqlalchemy import DDL
|
||||
event.listen(
|
||||
some_table,
|
||||
"after_create",
|
||||
DDL("ALTER TABLE %(table)s SET name=foo_%(table)s")
|
||||
)
|
||||
|
||||
The methods here define the name of an event as well
|
||||
as the names of members that are passed to listener
|
||||
functions.
|
||||
|
||||
For all :class:`.DDLEvent` events, the ``propagate=True`` keyword argument
|
||||
will ensure that a given event handler is propagated to copies of the
|
||||
object, which are made when using the :meth:`_schema.Table.to_metadata`
|
||||
method::
|
||||
|
||||
from sqlalchemy import DDL
|
||||
event.listen(
|
||||
some_table,
|
||||
"after_create",
|
||||
DDL("ALTER TABLE %(table)s SET name=foo_%(table)s"),
|
||||
propagate=True
|
||||
)
|
||||
|
||||
new_table = some_table.to_metadata(new_metadata)
|
||||
|
||||
The above :class:`.DDL` object will also be associated with the
|
||||
:class:`_schema.Table` object represented by ``new_table``.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:ref:`event_toplevel`
|
||||
|
||||
:class:`.DDLElement`
|
||||
|
||||
:class:`.DDL`
|
||||
|
||||
:ref:`schema_ddl_sequences`
|
||||
|
||||
"""
|
||||
|
||||
_target_class_doc = "SomeSchemaClassOrObject"
|
||||
_dispatch_target = SchemaEventTarget
|
||||
|
||||
def before_create(self, target, connection, **kw):
|
||||
r"""Called before CREATE statements are emitted.
|
||||
|
||||
:param target: the :class:`_schema.MetaData` or :class:`_schema.Table`
|
||||
object which is the target of the event.
|
||||
:param connection: the :class:`_engine.Connection` where the
|
||||
CREATE statement or statements will be emitted.
|
||||
:param \**kw: additional keyword arguments relevant
|
||||
to the event. The contents of this dictionary
|
||||
may vary across releases, and include the
|
||||
list of tables being generated for a metadata-level
|
||||
event, the checkfirst flag, and other
|
||||
elements used by internal events.
|
||||
|
||||
:func:`.event.listen` accepts the ``propagate=True``
|
||||
modifier for this event; when True, the listener function will
|
||||
be established for any copies made of the target object,
|
||||
i.e. those copies that are generated when
|
||||
:meth:`_schema.Table.to_metadata` is used.
|
||||
|
||||
:func:`.event.listen` accepts the ``insert=True``
|
||||
modifier for this event; when True, the listener function will
|
||||
be prepended to the internal list of events upon discovery, and execute
|
||||
before registered listener functions that do not pass this argument.
|
||||
|
||||
"""
|
||||
|
||||
def after_create(self, target, connection, **kw):
|
||||
r"""Called after CREATE statements are emitted.
|
||||
|
||||
:param target: the :class:`_schema.MetaData` or :class:`_schema.Table`
|
||||
object which is the target of the event.
|
||||
:param connection: the :class:`_engine.Connection` where the
|
||||
CREATE statement or statements have been emitted.
|
||||
:param \**kw: additional keyword arguments relevant
|
||||
to the event. The contents of this dictionary
|
||||
may vary across releases, and include the
|
||||
list of tables being generated for a metadata-level
|
||||
event, the checkfirst flag, and other
|
||||
elements used by internal events.
|
||||
|
||||
:func:`.event.listen` also accepts the ``propagate=True``
|
||||
modifier for this event; when True, the listener function will
|
||||
be established for any copies made of the target object,
|
||||
i.e. those copies that are generated when
|
||||
:meth:`_schema.Table.to_metadata` is used.
|
||||
|
||||
"""
|
||||
|
||||
def before_drop(self, target, connection, **kw):
|
||||
r"""Called before DROP statements are emitted.
|
||||
|
||||
:param target: the :class:`_schema.MetaData` or :class:`_schema.Table`
|
||||
object which is the target of the event.
|
||||
:param connection: the :class:`_engine.Connection` where the
|
||||
DROP statement or statements will be emitted.
|
||||
:param \**kw: additional keyword arguments relevant
|
||||
to the event. The contents of this dictionary
|
||||
may vary across releases, and include the
|
||||
list of tables being generated for a metadata-level
|
||||
event, the checkfirst flag, and other
|
||||
elements used by internal events.
|
||||
|
||||
:func:`.event.listen` also accepts the ``propagate=True``
|
||||
modifier for this event; when True, the listener function will
|
||||
be established for any copies made of the target object,
|
||||
i.e. those copies that are generated when
|
||||
:meth:`_schema.Table.to_metadata` is used.
|
||||
|
||||
"""
|
||||
|
||||
def after_drop(self, target, connection, **kw):
|
||||
r"""Called after DROP statements are emitted.
|
||||
|
||||
:param target: the :class:`_schema.MetaData` or :class:`_schema.Table`
|
||||
object which is the target of the event.
|
||||
:param connection: the :class:`_engine.Connection` where the
|
||||
DROP statement or statements have been emitted.
|
||||
:param \**kw: additional keyword arguments relevant
|
||||
to the event. The contents of this dictionary
|
||||
may vary across releases, and include the
|
||||
list of tables being generated for a metadata-level
|
||||
event, the checkfirst flag, and other
|
||||
elements used by internal events.
|
||||
|
||||
:func:`.event.listen` also accepts the ``propagate=True``
|
||||
modifier for this event; when True, the listener function will
|
||||
be established for any copies made of the target object,
|
||||
i.e. those copies that are generated when
|
||||
:meth:`_schema.Table.to_metadata` is used.
|
||||
|
||||
"""
|
||||
|
||||
def before_parent_attach(self, target, parent):
|
||||
"""Called before a :class:`.SchemaItem` is associated with
|
||||
a parent :class:`.SchemaItem`.
|
||||
|
||||
:param target: the target object
|
||||
:param parent: the parent to which the target is being attached.
|
||||
|
||||
:func:`.event.listen` also accepts the ``propagate=True``
|
||||
modifier for this event; when True, the listener function will
|
||||
be established for any copies made of the target object,
|
||||
i.e. those copies that are generated when
|
||||
:meth:`_schema.Table.to_metadata` is used.
|
||||
|
||||
"""
|
||||
|
||||
def after_parent_attach(self, target, parent):
|
||||
"""Called after a :class:`.SchemaItem` is associated with
|
||||
a parent :class:`.SchemaItem`.
|
||||
|
||||
:param target: the target object
|
||||
:param parent: the parent to which the target is being attached.
|
||||
|
||||
:func:`.event.listen` also accepts the ``propagate=True``
|
||||
modifier for this event; when True, the listener function will
|
||||
be established for any copies made of the target object,
|
||||
i.e. those copies that are generated when
|
||||
:meth:`_schema.Table.to_metadata` is used.
|
||||
|
||||
"""
|
||||
|
||||
def _sa_event_column_added_to_pk_constraint(self, const, col):
|
||||
"""internal event hook used for primary key naming convention
|
||||
updates.
|
||||
|
||||
"""
|
||||
|
||||
def column_reflect(self, inspector, table, column_info):
|
||||
"""Called for each unit of 'column info' retrieved when
|
||||
a :class:`_schema.Table` is being reflected.
|
||||
|
||||
This event is most easily used by applying it to a specific
|
||||
:class:`_schema.MetaData` instance, where it will take effect for
|
||||
all :class:`_schema.Table` objects within that
|
||||
:class:`_schema.MetaData` that undergo reflection::
|
||||
|
||||
metadata = MetaData()
|
||||
|
||||
@event.listens_for(metadata, 'column_reflect')
|
||||
def receive_column_reflect(inspector, table, column_info):
|
||||
# receives for all Table objects that are reflected
|
||||
# under this MetaData
|
||||
|
||||
|
||||
# will use the above event hook
|
||||
my_table = Table("my_table", metadata, autoload_with=some_engine)
|
||||
|
||||
|
||||
.. versionadded:: 1.4.0b2 The :meth:`_events.DDLEvents.column_reflect`
|
||||
hook may now be applied to a :class:`_schema.MetaData` object as
|
||||
well as the :class:`_schema.MetaData` class itself where it will
|
||||
take place for all :class:`_schema.Table` objects associated with
|
||||
the targeted :class:`_schema.MetaData`.
|
||||
|
||||
It may also be applied to the :class:`_schema.Table` class across
|
||||
the board::
|
||||
|
||||
from sqlalchemy import Table
|
||||
|
||||
@event.listens_for(Table, 'column_reflect')
|
||||
def receive_column_reflect(inspector, table, column_info):
|
||||
# receives for all Table objects that are reflected
|
||||
|
||||
It can also be applied to a specific :class:`_schema.Table` at the
|
||||
point that one is being reflected using the
|
||||
:paramref:`_schema.Table.listeners` parameter::
|
||||
|
||||
t1 = Table(
|
||||
"my_table",
|
||||
autoload_with=some_engine,
|
||||
listeners=[
|
||||
('column_reflect', receive_column_reflect)
|
||||
]
|
||||
)
|
||||
|
||||
A future release will allow it to be associated with a specific
|
||||
:class:`_schema.MetaData` object as well.
|
||||
|
||||
The dictionary of column information as returned by the
|
||||
dialect is passed, and can be modified. The dictionary
|
||||
is that returned in each element of the list returned
|
||||
by :meth:`.reflection.Inspector.get_columns`:
|
||||
|
||||
* ``name`` - the column's name, is applied to the
|
||||
:paramref:`_schema.Column.name` parameter
|
||||
|
||||
* ``type`` - the type of this column, which should be an instance
|
||||
of :class:`~sqlalchemy.types.TypeEngine`, is applied to the
|
||||
:paramref:`_schema.Column.type` parameter
|
||||
|
||||
* ``nullable`` - boolean flag if the column is NULL or NOT NULL,
|
||||
is applied to the :paramref:`_schema.Column.nullable` parameter
|
||||
|
||||
* ``default`` - the column's server default value. This is
|
||||
normally specified as a plain string SQL expression, however the
|
||||
event can pass a :class:`.FetchedValue`, :class:`.DefaultClause`,
|
||||
or :func:`_expression.text` object as well. Is applied to the
|
||||
:paramref:`_schema.Column.server_default` parameter
|
||||
|
||||
The event is called before any action is taken against
|
||||
this dictionary, and the contents can be modified; the following
|
||||
additional keys may be added to the dictionary to further modify
|
||||
how the :class:`_schema.Column` is constructed:
|
||||
|
||||
|
||||
* ``key`` - the string key that will be used to access this
|
||||
:class:`_schema.Column` in the ``.c`` collection; will be applied
|
||||
to the :paramref:`_schema.Column.key` parameter. Is also used
|
||||
for ORM mapping. See the section
|
||||
:ref:`mapper_automated_reflection_schemes` for an example.
|
||||
|
||||
* ``quote`` - force or un-force quoting on the column name;
|
||||
is applied to the :paramref:`_schema.Column.quote` parameter.
|
||||
|
||||
* ``info`` - a dictionary of arbitrary data to follow along with
|
||||
the :class:`_schema.Column`, is applied to the
|
||||
:paramref:`_schema.Column.info` parameter.
|
||||
|
||||
:func:`.event.listen` also accepts the ``propagate=True``
|
||||
modifier for this event; when True, the listener function will
|
||||
be established for any copies made of the target object,
|
||||
i.e. those copies that are generated when
|
||||
:meth:`_schema.Table.to_metadata` is used.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:ref:`mapper_automated_reflection_schemes` -
|
||||
in the ORM mapping documentation
|
||||
|
||||
:ref:`automap_intercepting_columns` -
|
||||
in the :ref:`automap_toplevel` documentation
|
||||
|
||||
:ref:`metadata_reflection_dbagnostic_types` - in
|
||||
the :ref:`metadata_reflection_toplevel` documentation
|
||||
|
||||
"""
|
||||
278
lib/sqlalchemy/sql/expression.py
Normal file
278
lib/sqlalchemy/sql/expression.py
Normal file
@@ -0,0 +1,278 @@
|
||||
# sql/expression.py
|
||||
# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
|
||||
# <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""Defines the public namespace for SQL expression constructs.
|
||||
|
||||
Prior to version 0.9, this module contained all of "elements", "dml",
|
||||
"default_comparator" and "selectable". The module was broken up
|
||||
and most "factory" functions were moved to be grouped with their associated
|
||||
class.
|
||||
|
||||
"""
|
||||
|
||||
__all__ = [
|
||||
"Alias",
|
||||
"AliasedReturnsRows",
|
||||
"any_",
|
||||
"all_",
|
||||
"CacheKey",
|
||||
"ClauseElement",
|
||||
"ColumnCollection",
|
||||
"ColumnElement",
|
||||
"CompoundSelect",
|
||||
"Delete",
|
||||
"FromClause",
|
||||
"Insert",
|
||||
"Join",
|
||||
"Lateral",
|
||||
"LambdaElement",
|
||||
"StatementLambdaElement",
|
||||
"Select",
|
||||
"Selectable",
|
||||
"TableClause",
|
||||
"TableValuedAlias",
|
||||
"Update",
|
||||
"Values",
|
||||
"alias",
|
||||
"and_",
|
||||
"asc",
|
||||
"between",
|
||||
"bindparam",
|
||||
"case",
|
||||
"cast",
|
||||
"column",
|
||||
"custom_op",
|
||||
"cte",
|
||||
"delete",
|
||||
"desc",
|
||||
"distinct",
|
||||
"except_",
|
||||
"except_all",
|
||||
"exists",
|
||||
"extract",
|
||||
"func",
|
||||
"modifier",
|
||||
"collate",
|
||||
"insert",
|
||||
"intersect",
|
||||
"intersect_all",
|
||||
"join",
|
||||
"label",
|
||||
"lateral",
|
||||
"lambda_stmt",
|
||||
"literal",
|
||||
"literal_column",
|
||||
"not_",
|
||||
"null",
|
||||
"nulls_first",
|
||||
"nulls_last",
|
||||
"or_",
|
||||
"outparam",
|
||||
"outerjoin",
|
||||
"over",
|
||||
"select",
|
||||
"table",
|
||||
"text",
|
||||
"tuple_",
|
||||
"type_coerce",
|
||||
"quoted_name",
|
||||
"union",
|
||||
"union_all",
|
||||
"update",
|
||||
"quoted_name",
|
||||
"within_group",
|
||||
"Subquery",
|
||||
"TableSample",
|
||||
"tablesample",
|
||||
"values",
|
||||
]
|
||||
|
||||
|
||||
from .base import _from_objects
|
||||
from .base import _select_iterables
|
||||
from .base import ColumnCollection
|
||||
from .base import Executable
|
||||
from .base import PARSE_AUTOCOMMIT
|
||||
from .dml import Delete
|
||||
from .dml import Insert
|
||||
from .dml import Update
|
||||
from .dml import UpdateBase
|
||||
from .dml import ValuesBase
|
||||
from .elements import _truncated_label
|
||||
from .elements import between
|
||||
from .elements import BinaryExpression
|
||||
from .elements import BindParameter
|
||||
from .elements import BooleanClauseList
|
||||
from .elements import Case
|
||||
from .elements import Cast
|
||||
from .elements import ClauseElement
|
||||
from .elements import ClauseList
|
||||
from .elements import collate
|
||||
from .elements import CollectionAggregate
|
||||
from .elements import ColumnClause
|
||||
from .elements import ColumnElement
|
||||
from .elements import Extract
|
||||
from .elements import False_
|
||||
from .elements import FunctionFilter
|
||||
from .elements import Grouping
|
||||
from .elements import Label
|
||||
from .elements import literal
|
||||
from .elements import literal_column
|
||||
from .elements import not_
|
||||
from .elements import Null
|
||||
from .elements import outparam
|
||||
from .elements import Over
|
||||
from .elements import quoted_name
|
||||
from .elements import ReleaseSavepointClause
|
||||
from .elements import RollbackToSavepointClause
|
||||
from .elements import SavepointClause
|
||||
from .elements import TextClause
|
||||
from .elements import True_
|
||||
from .elements import Tuple
|
||||
from .elements import TypeClause
|
||||
from .elements import TypeCoerce
|
||||
from .elements import UnaryExpression
|
||||
from .elements import WithinGroup
|
||||
from .functions import func
|
||||
from .functions import Function
|
||||
from .functions import FunctionElement
|
||||
from .functions import modifier
|
||||
from .lambdas import lambda_stmt
|
||||
from .lambdas import LambdaElement
|
||||
from .lambdas import StatementLambdaElement
|
||||
from .operators import ColumnOperators
|
||||
from .operators import custom_op
|
||||
from .operators import Operators
|
||||
from .selectable import Alias
|
||||
from .selectable import AliasedReturnsRows
|
||||
from .selectable import CompoundSelect
|
||||
from .selectable import CTE
|
||||
from .selectable import Exists
|
||||
from .selectable import FromClause
|
||||
from .selectable import FromGrouping
|
||||
from .selectable import GenerativeSelect
|
||||
from .selectable import HasCTE
|
||||
from .selectable import HasPrefixes
|
||||
from .selectable import HasSuffixes
|
||||
from .selectable import Join
|
||||
from .selectable import LABEL_STYLE_DEFAULT
|
||||
from .selectable import LABEL_STYLE_DISAMBIGUATE_ONLY
|
||||
from .selectable import LABEL_STYLE_NONE
|
||||
from .selectable import LABEL_STYLE_TABLENAME_PLUS_COL
|
||||
from .selectable import Lateral
|
||||
from .selectable import ReturnsRows
|
||||
from .selectable import ScalarSelect
|
||||
from .selectable import Select
|
||||
from .selectable import Selectable
|
||||
from .selectable import SelectBase
|
||||
from .selectable import Subquery
|
||||
from .selectable import subquery
|
||||
from .selectable import TableClause
|
||||
from .selectable import TableSample
|
||||
from .selectable import TableValuedAlias
|
||||
from .selectable import TextAsFrom
|
||||
from .selectable import TextualSelect
|
||||
from .selectable import Values
|
||||
from .traversals import CacheKey
|
||||
from .visitors import Visitable
|
||||
from ..util.langhelpers import public_factory
|
||||
|
||||
# factory functions - these pull class-bound constructors and classmethods
|
||||
# from SQL elements and selectables into public functions. This allows
|
||||
# the functions to be available in the sqlalchemy.sql.* namespace and
|
||||
# to be auto-cross-documenting from the function to the class itself.
|
||||
|
||||
all_ = public_factory(CollectionAggregate._create_all, ".sql.expression.all_")
|
||||
any_ = public_factory(CollectionAggregate._create_any, ".sql.expression.any_")
|
||||
and_ = public_factory(BooleanClauseList.and_, ".sql.expression.and_")
|
||||
alias = public_factory(Alias._factory, ".sql.expression.alias")
|
||||
tablesample = public_factory(
|
||||
TableSample._factory, ".sql.expression.tablesample"
|
||||
)
|
||||
lateral = public_factory(Lateral._factory, ".sql.expression.lateral")
|
||||
or_ = public_factory(BooleanClauseList.or_, ".sql.expression.or_")
|
||||
bindparam = public_factory(BindParameter, ".sql.expression.bindparam")
|
||||
select = public_factory(Select._create, ".sql.expression.select")
|
||||
text = public_factory(TextClause._create_text, ".sql.expression.text")
|
||||
table = public_factory(TableClause, ".sql.expression.table")
|
||||
column = public_factory(ColumnClause, ".sql.expression.column")
|
||||
over = public_factory(Over, ".sql.expression.over")
|
||||
within_group = public_factory(WithinGroup, ".sql.expression.within_group")
|
||||
label = public_factory(Label, ".sql.expression.label")
|
||||
case = public_factory(Case, ".sql.expression.case")
|
||||
cast = public_factory(Cast, ".sql.expression.cast")
|
||||
cte = public_factory(CTE._factory, ".sql.expression.cte")
|
||||
values = public_factory(Values, ".sql.expression.values")
|
||||
extract = public_factory(Extract, ".sql.expression.extract")
|
||||
tuple_ = public_factory(Tuple, ".sql.expression.tuple_")
|
||||
except_ = public_factory(
|
||||
CompoundSelect._create_except, ".sql.expression.except_"
|
||||
)
|
||||
except_all = public_factory(
|
||||
CompoundSelect._create_except_all, ".sql.expression.except_all"
|
||||
)
|
||||
intersect = public_factory(
|
||||
CompoundSelect._create_intersect, ".sql.expression.intersect"
|
||||
)
|
||||
intersect_all = public_factory(
|
||||
CompoundSelect._create_intersect_all, ".sql.expression.intersect_all"
|
||||
)
|
||||
union = public_factory(CompoundSelect._create_union, ".sql.expression.union")
|
||||
union_all = public_factory(
|
||||
CompoundSelect._create_union_all, ".sql.expression.union_all"
|
||||
)
|
||||
exists = public_factory(Exists, ".sql.expression.exists")
|
||||
nulls_first = public_factory(
|
||||
UnaryExpression._create_nulls_first, ".sql.expression.nulls_first"
|
||||
)
|
||||
nullsfirst = nulls_first # deprecated 1.4; see #5435
|
||||
nulls_last = public_factory(
|
||||
UnaryExpression._create_nulls_last, ".sql.expression.nulls_last"
|
||||
)
|
||||
nullslast = nulls_last # deprecated 1.4; see #5435
|
||||
asc = public_factory(UnaryExpression._create_asc, ".sql.expression.asc")
|
||||
desc = public_factory(UnaryExpression._create_desc, ".sql.expression.desc")
|
||||
distinct = public_factory(
|
||||
UnaryExpression._create_distinct, ".sql.expression.distinct"
|
||||
)
|
||||
type_coerce = public_factory(TypeCoerce, ".sql.expression.type_coerce")
|
||||
true = public_factory(True_._instance, ".sql.expression.true")
|
||||
false = public_factory(False_._instance, ".sql.expression.false")
|
||||
null = public_factory(Null._instance, ".sql.expression.null")
|
||||
join = public_factory(Join._create_join, ".sql.expression.join")
|
||||
outerjoin = public_factory(Join._create_outerjoin, ".sql.expression.outerjoin")
|
||||
insert = public_factory(Insert, ".sql.expression.insert")
|
||||
update = public_factory(Update, ".sql.expression.update")
|
||||
delete = public_factory(Delete, ".sql.expression.delete")
|
||||
funcfilter = public_factory(FunctionFilter, ".sql.expression.funcfilter")
|
||||
|
||||
|
||||
# internal functions still being called from tests and the ORM,
|
||||
# these might be better off in some other namespace
|
||||
|
||||
|
||||
# old names for compatibility
|
||||
_Executable = Executable
|
||||
_BindParamClause = BindParameter
|
||||
_Label = Label
|
||||
_SelectBase = SelectBase
|
||||
_BinaryExpression = BinaryExpression
|
||||
_Cast = Cast
|
||||
_Null = Null
|
||||
_False = False_
|
||||
_True = True_
|
||||
_TextClause = TextClause
|
||||
_UnaryExpression = UnaryExpression
|
||||
_Case = Case
|
||||
_Tuple = Tuple
|
||||
_Over = Over
|
||||
_TypeClause = TypeClause
|
||||
_Extract = Extract
|
||||
_Exists = Exists
|
||||
_Grouping = Grouping
|
||||
_FromGrouping = FromGrouping
|
||||
_ScalarSelect = ScalarSelect
|
||||
1575
lib/sqlalchemy/sql/functions.py
Normal file
1575
lib/sqlalchemy/sql/functions.py
Normal file
File diff suppressed because it is too large
Load Diff
1314
lib/sqlalchemy/sql/lambdas.py
Normal file
1314
lib/sqlalchemy/sql/lambdas.py
Normal file
File diff suppressed because it is too large
Load Diff
210
lib/sqlalchemy/sql/naming.py
Normal file
210
lib/sqlalchemy/sql/naming.py
Normal file
@@ -0,0 +1,210 @@
|
||||
# sqlalchemy/naming.py
|
||||
# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
|
||||
# <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""Establish constraint and index naming conventions.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from . import events # noqa
|
||||
from .elements import _NONE_NAME
|
||||
from .elements import conv
|
||||
from .schema import CheckConstraint
|
||||
from .schema import Column
|
||||
from .schema import Constraint
|
||||
from .schema import ForeignKeyConstraint
|
||||
from .schema import Index
|
||||
from .schema import PrimaryKeyConstraint
|
||||
from .schema import Table
|
||||
from .schema import UniqueConstraint
|
||||
from .. import event
|
||||
from .. import exc
|
||||
|
||||
|
||||
class ConventionDict(object):
|
||||
def __init__(self, const, table, convention):
|
||||
self.const = const
|
||||
self._is_fk = isinstance(const, ForeignKeyConstraint)
|
||||
self.table = table
|
||||
self.convention = convention
|
||||
self._const_name = const.name
|
||||
|
||||
def _key_table_name(self):
|
||||
return self.table.name
|
||||
|
||||
def _column_X(self, idx, attrname):
|
||||
if self._is_fk:
|
||||
try:
|
||||
fk = self.const.elements[idx]
|
||||
except IndexError:
|
||||
return ""
|
||||
else:
|
||||
return getattr(fk.parent, attrname)
|
||||
else:
|
||||
cols = list(self.const.columns)
|
||||
try:
|
||||
col = cols[idx]
|
||||
except IndexError:
|
||||
return ""
|
||||
else:
|
||||
return getattr(col, attrname)
|
||||
|
||||
def _key_constraint_name(self):
|
||||
if self._const_name in (None, _NONE_NAME):
|
||||
raise exc.InvalidRequestError(
|
||||
"Naming convention including "
|
||||
"%(constraint_name)s token requires that "
|
||||
"constraint is explicitly named."
|
||||
)
|
||||
if not isinstance(self._const_name, conv):
|
||||
self.const.name = None
|
||||
return self._const_name
|
||||
|
||||
def _key_column_X_key(self, idx):
|
||||
# note this method was missing before
|
||||
# [ticket:3989], meaning tokens like ``%(column_0_key)s`` weren't
|
||||
# working even though documented.
|
||||
return self._column_X(idx, "key")
|
||||
|
||||
def _key_column_X_name(self, idx):
|
||||
return self._column_X(idx, "name")
|
||||
|
||||
def _key_column_X_label(self, idx):
|
||||
return self._column_X(idx, "_ddl_label")
|
||||
|
||||
def _key_referred_table_name(self):
|
||||
fk = self.const.elements[0]
|
||||
refs = fk.target_fullname.split(".")
|
||||
if len(refs) == 3:
|
||||
refschema, reftable, refcol = refs
|
||||
else:
|
||||
reftable, refcol = refs
|
||||
return reftable
|
||||
|
||||
def _key_referred_column_X_name(self, idx):
|
||||
fk = self.const.elements[idx]
|
||||
# note that before [ticket:3989], this method was returning
|
||||
# the specification for the :class:`.ForeignKey` itself, which normally
|
||||
# would be using the ``.key`` of the column, not the name.
|
||||
return fk.column.name
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key in self.convention:
|
||||
return self.convention[key](self.const, self.table)
|
||||
elif hasattr(self, "_key_%s" % key):
|
||||
return getattr(self, "_key_%s" % key)()
|
||||
else:
|
||||
col_template = re.match(r".*_?column_(\d+)(_?N)?_.+", key)
|
||||
if col_template:
|
||||
idx = col_template.group(1)
|
||||
multiples = col_template.group(2)
|
||||
|
||||
if multiples:
|
||||
if self._is_fk:
|
||||
elems = self.const.elements
|
||||
else:
|
||||
elems = list(self.const.columns)
|
||||
tokens = []
|
||||
for idx, elem in enumerate(elems):
|
||||
attr = "_key_" + key.replace("0" + multiples, "X")
|
||||
try:
|
||||
tokens.append(getattr(self, attr)(idx))
|
||||
except AttributeError:
|
||||
raise KeyError(key)
|
||||
sep = "_" if multiples.startswith("_") else ""
|
||||
return sep.join(tokens)
|
||||
else:
|
||||
attr = "_key_" + key.replace(idx, "X")
|
||||
idx = int(idx)
|
||||
if hasattr(self, attr):
|
||||
return getattr(self, attr)(idx)
|
||||
raise KeyError(key)
|
||||
|
||||
|
||||
_prefix_dict = {
|
||||
Index: "ix",
|
||||
PrimaryKeyConstraint: "pk",
|
||||
CheckConstraint: "ck",
|
||||
UniqueConstraint: "uq",
|
||||
ForeignKeyConstraint: "fk",
|
||||
}
|
||||
|
||||
|
||||
def _get_convention(dict_, key):
|
||||
|
||||
for super_ in key.__mro__:
|
||||
if super_ in _prefix_dict and _prefix_dict[super_] in dict_:
|
||||
return dict_[_prefix_dict[super_]]
|
||||
elif super_ in dict_:
|
||||
return dict_[super_]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def _constraint_name_for_table(const, table):
|
||||
metadata = table.metadata
|
||||
convention = _get_convention(metadata.naming_convention, type(const))
|
||||
|
||||
if isinstance(const.name, conv):
|
||||
return const.name
|
||||
elif (
|
||||
convention is not None
|
||||
and not isinstance(const.name, conv)
|
||||
and (
|
||||
const.name is None
|
||||
or "constraint_name" in convention
|
||||
or const.name is _NONE_NAME
|
||||
)
|
||||
):
|
||||
return conv(
|
||||
convention
|
||||
% ConventionDict(const, table, metadata.naming_convention)
|
||||
)
|
||||
elif convention is _NONE_NAME:
|
||||
return None
|
||||
|
||||
|
||||
@event.listens_for(
|
||||
PrimaryKeyConstraint, "_sa_event_column_added_to_pk_constraint"
|
||||
)
|
||||
def _column_added_to_pk_constraint(pk_constraint, col):
|
||||
if pk_constraint._implicit_generated:
|
||||
# only operate upon the "implicit" pk constraint for now,
|
||||
# as we have to force the name to None to reset it. the
|
||||
# "implicit" constraint will only have a naming convention name
|
||||
# if at all.
|
||||
table = pk_constraint.table
|
||||
pk_constraint.name = None
|
||||
newname = _constraint_name_for_table(pk_constraint, table)
|
||||
if newname:
|
||||
pk_constraint.name = newname
|
||||
|
||||
|
||||
@event.listens_for(Constraint, "after_parent_attach")
|
||||
@event.listens_for(Index, "after_parent_attach")
|
||||
def _constraint_name(const, table):
|
||||
if isinstance(table, Column):
|
||||
# this path occurs for a CheckConstraint linked to a Column
|
||||
|
||||
# for column-attached constraint, set another event
|
||||
# to link the column attached to the table as this constraint
|
||||
# associated with the table.
|
||||
event.listen(
|
||||
table,
|
||||
"after_parent_attach",
|
||||
lambda col, table: _constraint_name(const, table),
|
||||
)
|
||||
|
||||
elif isinstance(table, Table):
|
||||
if isinstance(const.name, conv) or const.name is _NONE_NAME:
|
||||
return
|
||||
|
||||
newname = _constraint_name_for_table(const, table)
|
||||
if newname:
|
||||
const.name = newname
|
||||
1688
lib/sqlalchemy/sql/operators.py
Normal file
1688
lib/sqlalchemy/sql/operators.py
Normal file
File diff suppressed because it is too large
Load Diff
239
lib/sqlalchemy/sql/roles.py
Normal file
239
lib/sqlalchemy/sql/roles.py
Normal file
@@ -0,0 +1,239 @@
|
||||
# sql/roles.py
|
||||
# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
|
||||
# <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
from .. import util
|
||||
|
||||
|
||||
class SQLRole(object):
|
||||
"""Define a "role" within a SQL statement structure.
|
||||
|
||||
Classes within SQL Core participate within SQLRole hierarchies in order
|
||||
to more accurately indicate where they may be used within SQL statements
|
||||
of all types.
|
||||
|
||||
.. versionadded:: 1.4
|
||||
|
||||
"""
|
||||
|
||||
allows_lambda = False
|
||||
uses_inspection = False
|
||||
|
||||
|
||||
class UsesInspection(object):
|
||||
_post_inspect = None
|
||||
uses_inspection = True
|
||||
|
||||
|
||||
class AllowsLambdaRole(object):
|
||||
allows_lambda = True
|
||||
|
||||
|
||||
class HasCacheKeyRole(SQLRole):
|
||||
_role_name = "Cacheable Core or ORM object"
|
||||
|
||||
|
||||
class ExecutableOptionRole(SQLRole):
|
||||
__slots__ = ()
|
||||
_role_name = "ExecutionOption Core or ORM object"
|
||||
|
||||
|
||||
class LiteralValueRole(SQLRole):
|
||||
_role_name = "Literal Python value"
|
||||
|
||||
|
||||
class ColumnArgumentRole(SQLRole):
|
||||
_role_name = "Column expression"
|
||||
|
||||
|
||||
class ColumnArgumentOrKeyRole(ColumnArgumentRole):
|
||||
_role_name = "Column expression or string key"
|
||||
|
||||
|
||||
class StrAsPlainColumnRole(ColumnArgumentRole):
|
||||
_role_name = "Column expression or string key"
|
||||
|
||||
|
||||
class ColumnListRole(SQLRole):
|
||||
"""Elements suitable for forming comma separated lists of expressions."""
|
||||
|
||||
|
||||
class TruncatedLabelRole(SQLRole):
|
||||
_role_name = "String SQL identifier"
|
||||
|
||||
|
||||
class ColumnsClauseRole(AllowsLambdaRole, UsesInspection, ColumnListRole):
|
||||
_role_name = "Column expression or FROM clause"
|
||||
|
||||
@property
|
||||
def _select_iterable(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class LimitOffsetRole(SQLRole):
|
||||
_role_name = "LIMIT / OFFSET expression"
|
||||
|
||||
|
||||
class ByOfRole(ColumnListRole):
|
||||
_role_name = "GROUP BY / OF / etc. expression"
|
||||
|
||||
|
||||
class GroupByRole(AllowsLambdaRole, UsesInspection, ByOfRole):
|
||||
# note there's a special case right now where you can pass a whole
|
||||
# ORM entity to group_by() and it splits out. we may not want to keep
|
||||
# this around
|
||||
|
||||
_role_name = "GROUP BY expression"
|
||||
|
||||
|
||||
class OrderByRole(AllowsLambdaRole, ByOfRole):
|
||||
_role_name = "ORDER BY expression"
|
||||
|
||||
|
||||
class StructuralRole(SQLRole):
|
||||
pass
|
||||
|
||||
|
||||
class StatementOptionRole(StructuralRole):
|
||||
_role_name = "statement sub-expression element"
|
||||
|
||||
|
||||
class OnClauseRole(AllowsLambdaRole, StructuralRole):
|
||||
_role_name = "SQL expression for ON clause"
|
||||
|
||||
|
||||
class WhereHavingRole(OnClauseRole):
|
||||
_role_name = "SQL expression for WHERE/HAVING role"
|
||||
|
||||
|
||||
class ExpressionElementRole(SQLRole):
|
||||
_role_name = "SQL expression element"
|
||||
|
||||
|
||||
class ConstExprRole(ExpressionElementRole):
|
||||
_role_name = "Constant True/False/None expression"
|
||||
|
||||
|
||||
class LabeledColumnExprRole(ExpressionElementRole):
|
||||
pass
|
||||
|
||||
|
||||
class BinaryElementRole(ExpressionElementRole):
|
||||
_role_name = "SQL expression element or literal value"
|
||||
|
||||
|
||||
class InElementRole(SQLRole):
|
||||
_role_name = (
|
||||
"IN expression list, SELECT construct, or bound parameter object"
|
||||
)
|
||||
|
||||
|
||||
class JoinTargetRole(AllowsLambdaRole, UsesInspection, StructuralRole):
|
||||
_role_name = (
|
||||
"Join target, typically a FROM expression, or ORM "
|
||||
"relationship attribute"
|
||||
)
|
||||
|
||||
|
||||
class FromClauseRole(ColumnsClauseRole, JoinTargetRole):
|
||||
_role_name = "FROM expression, such as a Table or alias() object"
|
||||
|
||||
_is_subquery = False
|
||||
|
||||
@property
|
||||
def _hide_froms(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class StrictFromClauseRole(FromClauseRole):
|
||||
# does not allow text() or select() objects
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class AnonymizedFromClauseRole(StrictFromClauseRole):
|
||||
# calls .alias() as a post processor
|
||||
|
||||
def _anonymous_fromclause(self, name=None, flat=False):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class ReturnsRowsRole(SQLRole):
|
||||
_role_name = (
|
||||
"Row returning expression such as a SELECT, a FROM clause, or an "
|
||||
"INSERT/UPDATE/DELETE with RETURNING"
|
||||
)
|
||||
|
||||
|
||||
class StatementRole(SQLRole):
|
||||
_role_name = "Executable SQL or text() construct"
|
||||
|
||||
_propagate_attrs = util.immutabledict()
|
||||
|
||||
|
||||
class SelectStatementRole(StatementRole, ReturnsRowsRole):
|
||||
_role_name = "SELECT construct or equivalent text() construct"
|
||||
|
||||
def subquery(self):
|
||||
raise NotImplementedError(
|
||||
"All SelectStatementRole objects should implement a "
|
||||
".subquery() method."
|
||||
)
|
||||
|
||||
|
||||
class HasCTERole(ReturnsRowsRole):
|
||||
pass
|
||||
|
||||
|
||||
class IsCTERole(SQLRole):
|
||||
_role_name = "CTE object"
|
||||
|
||||
|
||||
class CompoundElementRole(AllowsLambdaRole, SQLRole):
|
||||
"""SELECT statements inside a CompoundSelect, e.g. UNION, EXTRACT, etc."""
|
||||
|
||||
_role_name = (
|
||||
"SELECT construct for inclusion in a UNION or other set construct"
|
||||
)
|
||||
|
||||
|
||||
# TODO: are we using this?
|
||||
class DMLRole(StatementRole):
|
||||
pass
|
||||
|
||||
|
||||
class DMLTableRole(FromClauseRole):
|
||||
_role_name = "subject table for an INSERT, UPDATE or DELETE"
|
||||
|
||||
|
||||
class DMLColumnRole(SQLRole):
|
||||
_role_name = "SET/VALUES column expression or string key"
|
||||
|
||||
|
||||
class DMLSelectRole(SQLRole):
|
||||
"""A SELECT statement embedded in DML, typically INSERT from SELECT"""
|
||||
|
||||
_role_name = "SELECT statement or equivalent textual object"
|
||||
|
||||
|
||||
class DDLRole(StatementRole):
|
||||
pass
|
||||
|
||||
|
||||
class DDLExpressionRole(StructuralRole):
|
||||
_role_name = "SQL expression element for DDL constraint"
|
||||
|
||||
|
||||
class DDLConstraintColumnRole(SQLRole):
|
||||
_role_name = "String column name or column expression for DDL constraint"
|
||||
|
||||
|
||||
class DDLReferredColumnRole(DDLConstraintColumnRole):
|
||||
_role_name = (
|
||||
"String column name or Column object for DDL foreign key constraint"
|
||||
)
|
||||
5268
lib/sqlalchemy/sql/schema.py
Normal file
5268
lib/sqlalchemy/sql/schema.py
Normal file
File diff suppressed because it is too large
Load Diff
6946
lib/sqlalchemy/sql/selectable.py
Normal file
6946
lib/sqlalchemy/sql/selectable.py
Normal file
File diff suppressed because it is too large
Load Diff
3351
lib/sqlalchemy/sql/sqltypes.py
Normal file
3351
lib/sqlalchemy/sql/sqltypes.py
Normal file
File diff suppressed because it is too large
Load Diff
1559
lib/sqlalchemy/sql/traversals.py
Normal file
1559
lib/sqlalchemy/sql/traversals.py
Normal file
File diff suppressed because it is too large
Load Diff
1974
lib/sqlalchemy/sql/type_api.py
Normal file
1974
lib/sqlalchemy/sql/type_api.py
Normal file
File diff suppressed because it is too large
Load Diff
1120
lib/sqlalchemy/sql/util.py
Normal file
1120
lib/sqlalchemy/sql/util.py
Normal file
File diff suppressed because it is too large
Load Diff
852
lib/sqlalchemy/sql/visitors.py
Normal file
852
lib/sqlalchemy/sql/visitors.py
Normal file
@@ -0,0 +1,852 @@
|
||||
# sql/visitors.py
|
||||
# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
|
||||
# <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""Visitor/traversal interface and library functions.
|
||||
|
||||
SQLAlchemy schema and expression constructs rely on a Python-centric
|
||||
version of the classic "visitor" pattern as the primary way in which
|
||||
they apply functionality. The most common use of this pattern
|
||||
is statement compilation, where individual expression classes match
|
||||
up to rendering methods that produce a string result. Beyond this,
|
||||
the visitor system is also used to inspect expressions for various
|
||||
information and patterns, as well as for the purposes of applying
|
||||
transformations to expressions.
|
||||
|
||||
Examples of how the visit system is used can be seen in the source code
|
||||
of for example the ``sqlalchemy.sql.util`` and the ``sqlalchemy.sql.compiler``
|
||||
modules. Some background on clause adaption is also at
|
||||
https://techspot.zzzeek.org/2008/01/23/expression-transformations/ .
|
||||
|
||||
"""
|
||||
|
||||
from collections import deque
|
||||
import itertools
|
||||
import operator
|
||||
|
||||
from .. import exc
|
||||
from .. import util
|
||||
from ..util import langhelpers
|
||||
from ..util import symbol
|
||||
|
||||
__all__ = [
|
||||
"iterate",
|
||||
"traverse_using",
|
||||
"traverse",
|
||||
"cloned_traverse",
|
||||
"replacement_traverse",
|
||||
"Traversible",
|
||||
"TraversibleType",
|
||||
"ExternalTraversal",
|
||||
"InternalTraversal",
|
||||
]
|
||||
|
||||
|
||||
def _generate_compiler_dispatch(cls):
|
||||
"""Generate a _compiler_dispatch() external traversal on classes with a
|
||||
__visit_name__ attribute.
|
||||
|
||||
"""
|
||||
visit_name = cls.__visit_name__
|
||||
|
||||
if "_compiler_dispatch" in cls.__dict__:
|
||||
# class has a fixed _compiler_dispatch() method.
|
||||
# copy it to "original" so that we can get it back if
|
||||
# sqlalchemy.ext.compiles overrides it.
|
||||
cls._original_compiler_dispatch = cls._compiler_dispatch
|
||||
return
|
||||
|
||||
if not isinstance(visit_name, util.compat.string_types):
|
||||
raise exc.InvalidRequestError(
|
||||
"__visit_name__ on class %s must be a string at the class level"
|
||||
% cls.__name__
|
||||
)
|
||||
|
||||
name = "visit_%s" % visit_name
|
||||
getter = operator.attrgetter(name)
|
||||
|
||||
def _compiler_dispatch(self, visitor, **kw):
|
||||
"""Look for an attribute named "visit_<visit_name>" on the
|
||||
visitor, and call it with the same kw params.
|
||||
|
||||
"""
|
||||
try:
|
||||
meth = getter(visitor)
|
||||
except AttributeError as err:
|
||||
return visitor.visit_unsupported_compilation(self, err, **kw)
|
||||
|
||||
else:
|
||||
return meth(self, **kw)
|
||||
|
||||
cls._compiler_dispatch = (
|
||||
cls._original_compiler_dispatch
|
||||
) = _compiler_dispatch
|
||||
|
||||
|
||||
class TraversibleType(type):
|
||||
"""Metaclass which assigns dispatch attributes to various kinds of
|
||||
"visitable" classes.
|
||||
|
||||
Attributes include:
|
||||
|
||||
* The ``_compiler_dispatch`` method, corresponding to ``__visit_name__``.
|
||||
This is called "external traversal" because the caller of each visit()
|
||||
method is responsible for sub-traversing the inner elements of each
|
||||
object. This is appropriate for string compilers and other traversals
|
||||
that need to call upon the inner elements in a specific pattern.
|
||||
|
||||
* internal traversal collections ``_children_traversal``,
|
||||
``_cache_key_traversal``, ``_copy_internals_traversal``, generated from
|
||||
an optional ``_traverse_internals`` collection of symbols which comes
|
||||
from the :class:`.InternalTraversal` list of symbols. This is called
|
||||
"internal traversal" MARKMARK
|
||||
|
||||
"""
|
||||
|
||||
def __init__(cls, clsname, bases, clsdict):
|
||||
if clsname != "Traversible":
|
||||
if "__visit_name__" in clsdict:
|
||||
_generate_compiler_dispatch(cls)
|
||||
|
||||
super(TraversibleType, cls).__init__(clsname, bases, clsdict)
|
||||
|
||||
|
||||
class Traversible(util.with_metaclass(TraversibleType)):
|
||||
"""Base class for visitable objects, applies the
|
||||
:class:`.visitors.TraversibleType` metaclass.
|
||||
|
||||
"""
|
||||
|
||||
def __class_getitem__(cls, key):
|
||||
# allow generic classes in py3.9+
|
||||
return cls
|
||||
|
||||
@util.preload_module("sqlalchemy.sql.traversals")
|
||||
def get_children(self, omit_attrs=(), **kw):
|
||||
r"""Return immediate child :class:`.visitors.Traversible`
|
||||
elements of this :class:`.visitors.Traversible`.
|
||||
|
||||
This is used for visit traversal.
|
||||
|
||||
\**kw may contain flags that change the collection that is
|
||||
returned, for example to return a subset of items in order to
|
||||
cut down on larger traversals, or to return child items from a
|
||||
different context (such as schema-level collections instead of
|
||||
clause-level).
|
||||
|
||||
"""
|
||||
|
||||
traversals = util.preloaded.sql_traversals
|
||||
|
||||
try:
|
||||
traverse_internals = self._traverse_internals
|
||||
except AttributeError:
|
||||
# user-defined classes may not have a _traverse_internals
|
||||
return []
|
||||
|
||||
dispatch = traversals._get_children.run_generated_dispatch
|
||||
return itertools.chain.from_iterable(
|
||||
meth(obj, **kw)
|
||||
for attrname, obj, meth in dispatch(
|
||||
self, traverse_internals, "_generated_get_children_traversal"
|
||||
)
|
||||
if attrname not in omit_attrs and obj is not None
|
||||
)
|
||||
|
||||
|
||||
class _InternalTraversalType(type):
|
||||
def __init__(cls, clsname, bases, clsdict):
|
||||
if cls.__name__ in ("InternalTraversal", "ExtendedInternalTraversal"):
|
||||
lookup = {}
|
||||
for key, sym in clsdict.items():
|
||||
if key.startswith("dp_"):
|
||||
visit_key = key.replace("dp_", "visit_")
|
||||
sym_name = sym.name
|
||||
assert sym_name not in lookup, sym_name
|
||||
lookup[sym] = lookup[sym_name] = visit_key
|
||||
if hasattr(cls, "_dispatch_lookup"):
|
||||
lookup.update(cls._dispatch_lookup)
|
||||
cls._dispatch_lookup = lookup
|
||||
|
||||
super(_InternalTraversalType, cls).__init__(clsname, bases, clsdict)
|
||||
|
||||
|
||||
def _generate_dispatcher(visitor, internal_dispatch, method_name):
|
||||
names = []
|
||||
for attrname, visit_sym in internal_dispatch:
|
||||
meth = visitor.dispatch(visit_sym)
|
||||
if meth:
|
||||
visit_name = ExtendedInternalTraversal._dispatch_lookup[visit_sym]
|
||||
names.append((attrname, visit_name))
|
||||
|
||||
code = (
|
||||
(" return [\n")
|
||||
+ (
|
||||
", \n".join(
|
||||
" (%r, self.%s, visitor.%s)"
|
||||
% (attrname, attrname, visit_name)
|
||||
for attrname, visit_name in names
|
||||
)
|
||||
)
|
||||
+ ("\n ]\n")
|
||||
)
|
||||
meth_text = ("def %s(self, visitor):\n" % method_name) + code + "\n"
|
||||
# print(meth_text)
|
||||
return langhelpers._exec_code_in_env(meth_text, {}, method_name)
|
||||
|
||||
|
||||
class InternalTraversal(util.with_metaclass(_InternalTraversalType, object)):
|
||||
r"""Defines visitor symbols used for internal traversal.
|
||||
|
||||
The :class:`.InternalTraversal` class is used in two ways. One is that
|
||||
it can serve as the superclass for an object that implements the
|
||||
various visit methods of the class. The other is that the symbols
|
||||
themselves of :class:`.InternalTraversal` are used within
|
||||
the ``_traverse_internals`` collection. Such as, the :class:`.Case`
|
||||
object defines ``_traverse_internals`` as ::
|
||||
|
||||
_traverse_internals = [
|
||||
("value", InternalTraversal.dp_clauseelement),
|
||||
("whens", InternalTraversal.dp_clauseelement_tuples),
|
||||
("else_", InternalTraversal.dp_clauseelement),
|
||||
]
|
||||
|
||||
Above, the :class:`.Case` class indicates its internal state as the
|
||||
attributes named ``value``, ``whens``, and ``else_``. They each
|
||||
link to an :class:`.InternalTraversal` method which indicates the type
|
||||
of datastructure referred towards.
|
||||
|
||||
Using the ``_traverse_internals`` structure, objects of type
|
||||
:class:`.InternalTraversible` will have the following methods automatically
|
||||
implemented:
|
||||
|
||||
* :meth:`.Traversible.get_children`
|
||||
|
||||
* :meth:`.Traversible._copy_internals`
|
||||
|
||||
* :meth:`.Traversible._gen_cache_key`
|
||||
|
||||
Subclasses can also implement these methods directly, particularly for the
|
||||
:meth:`.Traversible._copy_internals` method, when special steps
|
||||
are needed.
|
||||
|
||||
.. versionadded:: 1.4
|
||||
|
||||
"""
|
||||
|
||||
def dispatch(self, visit_symbol):
|
||||
"""Given a method from :class:`.InternalTraversal`, return the
|
||||
corresponding method on a subclass.
|
||||
|
||||
"""
|
||||
name = self._dispatch_lookup[visit_symbol]
|
||||
return getattr(self, name, None)
|
||||
|
||||
def run_generated_dispatch(
|
||||
self, target, internal_dispatch, generate_dispatcher_name
|
||||
):
|
||||
try:
|
||||
dispatcher = target.__class__.__dict__[generate_dispatcher_name]
|
||||
except KeyError:
|
||||
# most of the dispatchers are generated up front
|
||||
# in sqlalchemy/sql/__init__.py ->
|
||||
# traversals.py-> _preconfigure_traversals().
|
||||
# this block will generate any remaining dispatchers.
|
||||
dispatcher = self.generate_dispatch(
|
||||
target.__class__, internal_dispatch, generate_dispatcher_name
|
||||
)
|
||||
return dispatcher(target, self)
|
||||
|
||||
def generate_dispatch(
|
||||
self, target_cls, internal_dispatch, generate_dispatcher_name
|
||||
):
|
||||
dispatcher = _generate_dispatcher(
|
||||
self, internal_dispatch, generate_dispatcher_name
|
||||
)
|
||||
# assert isinstance(target_cls, type)
|
||||
setattr(target_cls, generate_dispatcher_name, dispatcher)
|
||||
return dispatcher
|
||||
|
||||
dp_has_cache_key = symbol("HC")
|
||||
"""Visit a :class:`.HasCacheKey` object."""
|
||||
|
||||
dp_has_cache_key_list = symbol("HL")
|
||||
"""Visit a list of :class:`.HasCacheKey` objects."""
|
||||
|
||||
dp_clauseelement = symbol("CE")
|
||||
"""Visit a :class:`_expression.ClauseElement` object."""
|
||||
|
||||
dp_fromclause_canonical_column_collection = symbol("FC")
|
||||
"""Visit a :class:`_expression.FromClause` object in the context of the
|
||||
``columns`` attribute.
|
||||
|
||||
The column collection is "canonical", meaning it is the originally
|
||||
defined location of the :class:`.ColumnClause` objects. Right now
|
||||
this means that the object being visited is a
|
||||
:class:`_expression.TableClause`
|
||||
or :class:`_schema.Table` object only.
|
||||
|
||||
"""
|
||||
|
||||
dp_clauseelement_tuples = symbol("CTS")
|
||||
"""Visit a list of tuples which contain :class:`_expression.ClauseElement`
|
||||
objects.
|
||||
|
||||
"""
|
||||
|
||||
dp_clauseelement_list = symbol("CL")
|
||||
"""Visit a list of :class:`_expression.ClauseElement` objects.
|
||||
|
||||
"""
|
||||
|
||||
dp_clauseelement_tuple = symbol("CT")
|
||||
"""Visit a tuple of :class:`_expression.ClauseElement` objects.
|
||||
|
||||
"""
|
||||
|
||||
dp_executable_options = symbol("EO")
|
||||
|
||||
dp_with_context_options = symbol("WC")
|
||||
|
||||
dp_fromclause_ordered_set = symbol("CO")
|
||||
"""Visit an ordered set of :class:`_expression.FromClause` objects. """
|
||||
|
||||
dp_string = symbol("S")
|
||||
"""Visit a plain string value.
|
||||
|
||||
Examples include table and column names, bound parameter keys, special
|
||||
keywords such as "UNION", "UNION ALL".
|
||||
|
||||
The string value is considered to be significant for cache key
|
||||
generation.
|
||||
|
||||
"""
|
||||
|
||||
dp_string_list = symbol("SL")
|
||||
"""Visit a list of strings."""
|
||||
|
||||
dp_anon_name = symbol("AN")
|
||||
"""Visit a potentially "anonymized" string value.
|
||||
|
||||
The string value is considered to be significant for cache key
|
||||
generation.
|
||||
|
||||
"""
|
||||
|
||||
dp_boolean = symbol("B")
|
||||
"""Visit a boolean value.
|
||||
|
||||
The boolean value is considered to be significant for cache key
|
||||
generation.
|
||||
|
||||
"""
|
||||
|
||||
dp_operator = symbol("O")
|
||||
"""Visit an operator.
|
||||
|
||||
The operator is a function from the :mod:`sqlalchemy.sql.operators`
|
||||
module.
|
||||
|
||||
The operator value is considered to be significant for cache key
|
||||
generation.
|
||||
|
||||
"""
|
||||
|
||||
dp_type = symbol("T")
|
||||
"""Visit a :class:`.TypeEngine` object
|
||||
|
||||
The type object is considered to be significant for cache key
|
||||
generation.
|
||||
|
||||
"""
|
||||
|
||||
dp_plain_dict = symbol("PD")
|
||||
"""Visit a dictionary with string keys.
|
||||
|
||||
The keys of the dictionary should be strings, the values should
|
||||
be immutable and hashable. The dictionary is considered to be
|
||||
significant for cache key generation.
|
||||
|
||||
"""
|
||||
|
||||
dp_dialect_options = symbol("DO")
|
||||
"""Visit a dialect options structure."""
|
||||
|
||||
dp_string_clauseelement_dict = symbol("CD")
|
||||
"""Visit a dictionary of string keys to :class:`_expression.ClauseElement`
|
||||
objects.
|
||||
|
||||
"""
|
||||
|
||||
dp_string_multi_dict = symbol("MD")
|
||||
"""Visit a dictionary of string keys to values which may either be
|
||||
plain immutable/hashable or :class:`.HasCacheKey` objects.
|
||||
|
||||
"""
|
||||
|
||||
dp_annotations_key = symbol("AK")
|
||||
"""Visit the _annotations_cache_key element.
|
||||
|
||||
This is a dictionary of additional information about a ClauseElement
|
||||
that modifies its role. It should be included when comparing or caching
|
||||
objects, however generating this key is relatively expensive. Visitors
|
||||
should check the "_annotations" dict for non-None first before creating
|
||||
this key.
|
||||
|
||||
"""
|
||||
|
||||
dp_plain_obj = symbol("PO")
|
||||
"""Visit a plain python object.
|
||||
|
||||
The value should be immutable and hashable, such as an integer.
|
||||
The value is considered to be significant for cache key generation.
|
||||
|
||||
"""
|
||||
|
||||
dp_named_ddl_element = symbol("DD")
|
||||
"""Visit a simple named DDL element.
|
||||
|
||||
The current object used by this method is the :class:`.Sequence`.
|
||||
|
||||
The object is only considered to be important for cache key generation
|
||||
as far as its name, but not any other aspects of it.
|
||||
|
||||
"""
|
||||
|
||||
dp_prefix_sequence = symbol("PS")
|
||||
"""Visit the sequence represented by :class:`_expression.HasPrefixes`
|
||||
or :class:`_expression.HasSuffixes`.
|
||||
|
||||
"""
|
||||
|
||||
dp_table_hint_list = symbol("TH")
|
||||
"""Visit the ``_hints`` collection of a :class:`_expression.Select`
|
||||
object.
|
||||
|
||||
"""
|
||||
|
||||
dp_setup_join_tuple = symbol("SJ")
|
||||
|
||||
dp_memoized_select_entities = symbol("ME")
|
||||
|
||||
dp_statement_hint_list = symbol("SH")
|
||||
"""Visit the ``_statement_hints`` collection of a
|
||||
:class:`_expression.Select`
|
||||
object.
|
||||
|
||||
"""
|
||||
|
||||
dp_unknown_structure = symbol("UK")
|
||||
"""Visit an unknown structure.
|
||||
|
||||
"""
|
||||
|
||||
dp_dml_ordered_values = symbol("DML_OV")
|
||||
"""Visit the values() ordered tuple list of an
|
||||
:class:`_expression.Update` object."""
|
||||
|
||||
dp_dml_values = symbol("DML_V")
|
||||
"""Visit the values() dictionary of a :class:`.ValuesBase`
|
||||
(e.g. Insert or Update) object.
|
||||
|
||||
"""
|
||||
|
||||
dp_dml_multi_values = symbol("DML_MV")
|
||||
"""Visit the values() multi-valued list of dictionaries of an
|
||||
:class:`_expression.Insert` object.
|
||||
|
||||
"""
|
||||
|
||||
dp_propagate_attrs = symbol("PA")
|
||||
"""Visit the propagate attrs dict. This hardcodes to the particular
|
||||
elements we care about right now."""
|
||||
|
||||
|
||||
class ExtendedInternalTraversal(InternalTraversal):
|
||||
"""Defines additional symbols that are useful in caching applications.
|
||||
|
||||
Traversals for :class:`_expression.ClauseElement` objects only need to use
|
||||
those symbols present in :class:`.InternalTraversal`. However, for
|
||||
additional caching use cases within the ORM, symbols dealing with the
|
||||
:class:`.HasCacheKey` class are added here.
|
||||
|
||||
"""
|
||||
|
||||
dp_ignore = symbol("IG")
|
||||
"""Specify an object that should be ignored entirely.
|
||||
|
||||
This currently applies function call argument caching where some
|
||||
arguments should not be considered to be part of a cache key.
|
||||
|
||||
"""
|
||||
|
||||
dp_inspectable = symbol("IS")
|
||||
"""Visit an inspectable object where the return value is a
|
||||
:class:`.HasCacheKey` object."""
|
||||
|
||||
dp_multi = symbol("M")
|
||||
"""Visit an object that may be a :class:`.HasCacheKey` or may be a
|
||||
plain hashable object."""
|
||||
|
||||
dp_multi_list = symbol("MT")
|
||||
"""Visit a tuple containing elements that may be :class:`.HasCacheKey` or
|
||||
may be a plain hashable object."""
|
||||
|
||||
dp_has_cache_key_tuples = symbol("HT")
|
||||
"""Visit a list of tuples which contain :class:`.HasCacheKey`
|
||||
objects.
|
||||
|
||||
"""
|
||||
|
||||
dp_inspectable_list = symbol("IL")
|
||||
"""Visit a list of inspectable objects which upon inspection are
|
||||
HasCacheKey objects."""
|
||||
|
||||
|
||||
class ExternalTraversal(object):
|
||||
"""Base class for visitor objects which can traverse externally using
|
||||
the :func:`.visitors.traverse` function.
|
||||
|
||||
Direct usage of the :func:`.visitors.traverse` function is usually
|
||||
preferred.
|
||||
|
||||
"""
|
||||
|
||||
__traverse_options__ = {}
|
||||
|
||||
def traverse_single(self, obj, **kw):
|
||||
for v in self.visitor_iterator:
|
||||
meth = getattr(v, "visit_%s" % obj.__visit_name__, None)
|
||||
if meth:
|
||||
return meth(obj, **kw)
|
||||
|
||||
def iterate(self, obj):
|
||||
"""Traverse the given expression structure, returning an iterator
|
||||
of all elements.
|
||||
|
||||
"""
|
||||
return iterate(obj, self.__traverse_options__)
|
||||
|
||||
def traverse(self, obj):
|
||||
"""Traverse and visit the given expression structure."""
|
||||
|
||||
return traverse(obj, self.__traverse_options__, self._visitor_dict)
|
||||
|
||||
@util.memoized_property
|
||||
def _visitor_dict(self):
|
||||
visitors = {}
|
||||
|
||||
for name in dir(self):
|
||||
if name.startswith("visit_"):
|
||||
visitors[name[6:]] = getattr(self, name)
|
||||
return visitors
|
||||
|
||||
@property
|
||||
def visitor_iterator(self):
|
||||
"""Iterate through this visitor and each 'chained' visitor."""
|
||||
|
||||
v = self
|
||||
while v:
|
||||
yield v
|
||||
v = getattr(v, "_next", None)
|
||||
|
||||
def chain(self, visitor):
|
||||
"""'Chain' an additional ClauseVisitor onto this ClauseVisitor.
|
||||
|
||||
The chained visitor will receive all visit events after this one.
|
||||
|
||||
"""
|
||||
tail = list(self.visitor_iterator)[-1]
|
||||
tail._next = visitor
|
||||
return self
|
||||
|
||||
|
||||
class CloningExternalTraversal(ExternalTraversal):
|
||||
"""Base class for visitor objects which can traverse using
|
||||
the :func:`.visitors.cloned_traverse` function.
|
||||
|
||||
Direct usage of the :func:`.visitors.cloned_traverse` function is usually
|
||||
preferred.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def copy_and_process(self, list_):
|
||||
"""Apply cloned traversal to the given list of elements, and return
|
||||
the new list.
|
||||
|
||||
"""
|
||||
return [self.traverse(x) for x in list_]
|
||||
|
||||
def traverse(self, obj):
|
||||
"""Traverse and visit the given expression structure."""
|
||||
|
||||
return cloned_traverse(
|
||||
obj, self.__traverse_options__, self._visitor_dict
|
||||
)
|
||||
|
||||
|
||||
class ReplacingExternalTraversal(CloningExternalTraversal):
|
||||
"""Base class for visitor objects which can traverse using
|
||||
the :func:`.visitors.replacement_traverse` function.
|
||||
|
||||
Direct usage of the :func:`.visitors.replacement_traverse` function is
|
||||
usually preferred.
|
||||
|
||||
"""
|
||||
|
||||
def replace(self, elem):
|
||||
"""Receive pre-copied elements during a cloning traversal.
|
||||
|
||||
If the method returns a new element, the element is used
|
||||
instead of creating a simple copy of the element. Traversal
|
||||
will halt on the newly returned element if it is re-encountered.
|
||||
"""
|
||||
return None
|
||||
|
||||
def traverse(self, obj):
|
||||
"""Traverse and visit the given expression structure."""
|
||||
|
||||
def replace(elem):
|
||||
for v in self.visitor_iterator:
|
||||
e = v.replace(elem)
|
||||
if e is not None:
|
||||
return e
|
||||
|
||||
return replacement_traverse(obj, self.__traverse_options__, replace)
|
||||
|
||||
|
||||
# backwards compatibility
|
||||
Visitable = Traversible
|
||||
VisitableType = TraversibleType
|
||||
ClauseVisitor = ExternalTraversal
|
||||
CloningVisitor = CloningExternalTraversal
|
||||
ReplacingCloningVisitor = ReplacingExternalTraversal
|
||||
|
||||
|
||||
def iterate(obj, opts=util.immutabledict()):
|
||||
r"""Traverse the given expression structure, returning an iterator.
|
||||
|
||||
Traversal is configured to be breadth-first.
|
||||
|
||||
The central API feature used by the :func:`.visitors.iterate`
|
||||
function is the
|
||||
:meth:`_expression.ClauseElement.get_children` method of
|
||||
:class:`_expression.ClauseElement` objects. This method should return all
|
||||
the :class:`_expression.ClauseElement` objects which are associated with a
|
||||
particular :class:`_expression.ClauseElement` object. For example, a
|
||||
:class:`.Case` structure will refer to a series of
|
||||
:class:`_expression.ColumnElement` objects within its "whens" and "else\_"
|
||||
member variables.
|
||||
|
||||
:param obj: :class:`_expression.ClauseElement` structure to be traversed
|
||||
|
||||
:param opts: dictionary of iteration options. This dictionary is usually
|
||||
empty in modern usage.
|
||||
|
||||
"""
|
||||
yield obj
|
||||
children = obj.get_children(**opts)
|
||||
|
||||
if not children:
|
||||
return
|
||||
|
||||
stack = deque([children])
|
||||
while stack:
|
||||
t_iterator = stack.popleft()
|
||||
for t in t_iterator:
|
||||
yield t
|
||||
stack.append(t.get_children(**opts))
|
||||
|
||||
|
||||
def traverse_using(iterator, obj, visitors):
|
||||
"""Visit the given expression structure using the given iterator of
|
||||
objects.
|
||||
|
||||
:func:`.visitors.traverse_using` is usually called internally as the result
|
||||
of the :func:`.visitors.traverse` function.
|
||||
|
||||
:param iterator: an iterable or sequence which will yield
|
||||
:class:`_expression.ClauseElement`
|
||||
structures; the iterator is assumed to be the
|
||||
product of the :func:`.visitors.iterate` function.
|
||||
|
||||
:param obj: the :class:`_expression.ClauseElement`
|
||||
that was used as the target of the
|
||||
:func:`.iterate` function.
|
||||
|
||||
:param visitors: dictionary of visit functions. See :func:`.traverse`
|
||||
for details on this dictionary.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:func:`.traverse`
|
||||
|
||||
|
||||
"""
|
||||
for target in iterator:
|
||||
meth = visitors.get(target.__visit_name__, None)
|
||||
if meth:
|
||||
meth(target)
|
||||
return obj
|
||||
|
||||
|
||||
def traverse(obj, opts, visitors):
|
||||
"""Traverse and visit the given expression structure using the default
|
||||
iterator.
|
||||
|
||||
e.g.::
|
||||
|
||||
from sqlalchemy.sql import visitors
|
||||
|
||||
stmt = select(some_table).where(some_table.c.foo == 'bar')
|
||||
|
||||
def visit_bindparam(bind_param):
|
||||
print("found bound value: %s" % bind_param.value)
|
||||
|
||||
visitors.traverse(stmt, {}, {"bindparam": visit_bindparam})
|
||||
|
||||
The iteration of objects uses the :func:`.visitors.iterate` function,
|
||||
which does a breadth-first traversal using a stack.
|
||||
|
||||
:param obj: :class:`_expression.ClauseElement` structure to be traversed
|
||||
|
||||
:param opts: dictionary of iteration options. This dictionary is usually
|
||||
empty in modern usage.
|
||||
|
||||
:param visitors: dictionary of visit functions. The dictionary should
|
||||
have strings as keys, each of which would correspond to the
|
||||
``__visit_name__`` of a particular kind of SQL expression object, and
|
||||
callable functions as values, each of which represents a visitor function
|
||||
for that kind of object.
|
||||
|
||||
"""
|
||||
return traverse_using(iterate(obj, opts), obj, visitors)
|
||||
|
||||
|
||||
def cloned_traverse(obj, opts, visitors):
|
||||
"""Clone the given expression structure, allowing modifications by
|
||||
visitors.
|
||||
|
||||
Traversal usage is the same as that of :func:`.visitors.traverse`.
|
||||
The visitor functions present in the ``visitors`` dictionary may also
|
||||
modify the internals of the given structure as the traversal proceeds.
|
||||
|
||||
The central API feature used by the :func:`.visitors.cloned_traverse`
|
||||
and :func:`.visitors.replacement_traverse` functions, in addition to the
|
||||
:meth:`_expression.ClauseElement.get_children`
|
||||
function that is used to achieve
|
||||
the iteration, is the :meth:`_expression.ClauseElement._copy_internals`
|
||||
method.
|
||||
For a :class:`_expression.ClauseElement`
|
||||
structure to support cloning and replacement
|
||||
traversals correctly, it needs to be able to pass a cloning function into
|
||||
its internal members in order to make copies of them.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:func:`.visitors.traverse`
|
||||
|
||||
:func:`.visitors.replacement_traverse`
|
||||
|
||||
"""
|
||||
|
||||
cloned = {}
|
||||
stop_on = set(opts.get("stop_on", []))
|
||||
|
||||
def deferred_copy_internals(obj):
|
||||
return cloned_traverse(obj, opts, visitors)
|
||||
|
||||
def clone(elem, **kw):
|
||||
if elem in stop_on:
|
||||
return elem
|
||||
else:
|
||||
if id(elem) not in cloned:
|
||||
|
||||
if "replace" in kw:
|
||||
newelem = kw["replace"](elem)
|
||||
if newelem is not None:
|
||||
cloned[id(elem)] = newelem
|
||||
return newelem
|
||||
|
||||
cloned[id(elem)] = newelem = elem._clone(clone=clone, **kw)
|
||||
newelem._copy_internals(clone=clone, **kw)
|
||||
meth = visitors.get(newelem.__visit_name__, None)
|
||||
if meth:
|
||||
meth(newelem)
|
||||
return cloned[id(elem)]
|
||||
|
||||
if obj is not None:
|
||||
obj = clone(
|
||||
obj, deferred_copy_internals=deferred_copy_internals, **opts
|
||||
)
|
||||
clone = None # remove gc cycles
|
||||
return obj
|
||||
|
||||
|
||||
def replacement_traverse(obj, opts, replace):
|
||||
"""Clone the given expression structure, allowing element
|
||||
replacement by a given replacement function.
|
||||
|
||||
This function is very similar to the :func:`.visitors.cloned_traverse`
|
||||
function, except instead of being passed a dictionary of visitors, all
|
||||
elements are unconditionally passed into the given replace function.
|
||||
The replace function then has the option to return an entirely new object
|
||||
which will replace the one given. If it returns ``None``, then the object
|
||||
is kept in place.
|
||||
|
||||
The difference in usage between :func:`.visitors.cloned_traverse` and
|
||||
:func:`.visitors.replacement_traverse` is that in the former case, an
|
||||
already-cloned object is passed to the visitor function, and the visitor
|
||||
function can then manipulate the internal state of the object.
|
||||
In the case of the latter, the visitor function should only return an
|
||||
entirely different object, or do nothing.
|
||||
|
||||
The use case for :func:`.visitors.replacement_traverse` is that of
|
||||
replacing a FROM clause inside of a SQL structure with a different one,
|
||||
as is a common use case within the ORM.
|
||||
|
||||
"""
|
||||
|
||||
cloned = {}
|
||||
stop_on = {id(x) for x in opts.get("stop_on", [])}
|
||||
|
||||
def deferred_copy_internals(obj):
|
||||
return replacement_traverse(obj, opts, replace)
|
||||
|
||||
def clone(elem, **kw):
|
||||
if (
|
||||
id(elem) in stop_on
|
||||
or "no_replacement_traverse" in elem._annotations
|
||||
):
|
||||
return elem
|
||||
else:
|
||||
newelem = replace(elem)
|
||||
if newelem is not None:
|
||||
stop_on.add(id(newelem))
|
||||
return newelem
|
||||
else:
|
||||
# base "already seen" on id(), not hash, so that we don't
|
||||
# replace an Annotated element with its non-annotated one, and
|
||||
# vice versa
|
||||
id_elem = id(elem)
|
||||
if id_elem not in cloned:
|
||||
if "replace" in kw:
|
||||
newelem = kw["replace"](elem)
|
||||
if newelem is not None:
|
||||
cloned[id_elem] = newelem
|
||||
return newelem
|
||||
|
||||
cloned[id_elem] = newelem = elem._clone(**kw)
|
||||
newelem._copy_internals(clone=clone, **kw)
|
||||
return cloned[id_elem]
|
||||
|
||||
if obj is not None:
|
||||
obj = clone(
|
||||
obj, deferred_copy_internals=deferred_copy_internals, **opts
|
||||
)
|
||||
clone = None # remove gc cycles
|
||||
return obj
|
||||
Reference in New Issue
Block a user