SQLAlchemy 2.1.0rc1__cp315-cp315-win32.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sqlalchemy/__init__.py +299 -0
- sqlalchemy/connectors/__init__.py +18 -0
- sqlalchemy/connectors/aioodbc.py +171 -0
- sqlalchemy/connectors/asyncio.py +476 -0
- sqlalchemy/connectors/pyodbc.py +248 -0
- sqlalchemy/dialects/__init__.py +62 -0
- sqlalchemy/dialects/_typing.py +29 -0
- sqlalchemy/dialects/mssql/__init__.py +88 -0
- sqlalchemy/dialects/mssql/aioodbc.py +63 -0
- sqlalchemy/dialects/mssql/base.py +4833 -0
- sqlalchemy/dialects/mssql/information_schema.py +345 -0
- sqlalchemy/dialects/mssql/json.py +140 -0
- sqlalchemy/dialects/mssql/mssqlpython.py +242 -0
- sqlalchemy/dialects/mssql/provision.py +196 -0
- sqlalchemy/dialects/mssql/pymssql.py +130 -0
- sqlalchemy/dialects/mssql/pyodbc.py +697 -0
- sqlalchemy/dialects/mysql/__init__.py +106 -0
- sqlalchemy/dialects/mysql/_mariadb_shim.py +312 -0
- sqlalchemy/dialects/mysql/aiomysql.py +260 -0
- sqlalchemy/dialects/mysql/asyncmy.py +241 -0
- sqlalchemy/dialects/mysql/base.py +3896 -0
- sqlalchemy/dialects/mysql/cymysql.py +107 -0
- sqlalchemy/dialects/mysql/dml.py +279 -0
- sqlalchemy/dialects/mysql/enumerated.py +277 -0
- sqlalchemy/dialects/mysql/expression.py +146 -0
- sqlalchemy/dialects/mysql/json.py +92 -0
- sqlalchemy/dialects/mysql/mariadb.py +67 -0
- sqlalchemy/dialects/mysql/mariadbconnector.py +314 -0
- sqlalchemy/dialects/mysql/mysqlconnector.py +291 -0
- sqlalchemy/dialects/mysql/mysqldb.py +318 -0
- sqlalchemy/dialects/mysql/provision.py +153 -0
- sqlalchemy/dialects/mysql/pymysql.py +188 -0
- sqlalchemy/dialects/mysql/pyodbc.py +157 -0
- sqlalchemy/dialects/mysql/reflection.py +724 -0
- sqlalchemy/dialects/mysql/reserved_words.py +570 -0
- sqlalchemy/dialects/mysql/types.py +845 -0
- sqlalchemy/dialects/oracle/__init__.py +85 -0
- sqlalchemy/dialects/oracle/base.py +3847 -0
- sqlalchemy/dialects/oracle/cx_oracle.py +1736 -0
- sqlalchemy/dialects/oracle/dictionary.py +507 -0
- sqlalchemy/dialects/oracle/json.py +157 -0
- sqlalchemy/dialects/oracle/oracledb.py +898 -0
- sqlalchemy/dialects/oracle/provision.py +288 -0
- sqlalchemy/dialects/oracle/types.py +367 -0
- sqlalchemy/dialects/oracle/vector.py +366 -0
- sqlalchemy/dialects/postgresql/__init__.py +170 -0
- sqlalchemy/dialects/postgresql/_psycopg_common.py +232 -0
- sqlalchemy/dialects/postgresql/array.py +534 -0
- sqlalchemy/dialects/postgresql/asyncpg.py +1318 -0
- sqlalchemy/dialects/postgresql/base.py +5935 -0
- sqlalchemy/dialects/postgresql/bitstring.py +327 -0
- sqlalchemy/dialects/postgresql/dml.py +360 -0
- sqlalchemy/dialects/postgresql/ext.py +599 -0
- sqlalchemy/dialects/postgresql/hstore.py +422 -0
- sqlalchemy/dialects/postgresql/json.py +411 -0
- sqlalchemy/dialects/postgresql/named_types.py +535 -0
- sqlalchemy/dialects/postgresql/operators.py +129 -0
- sqlalchemy/dialects/postgresql/pg8000.py +655 -0
- sqlalchemy/dialects/postgresql/pg_catalog.py +345 -0
- sqlalchemy/dialects/postgresql/provision.py +202 -0
- sqlalchemy/dialects/postgresql/psycopg.py +800 -0
- sqlalchemy/dialects/postgresql/psycopg2.py +860 -0
- sqlalchemy/dialects/postgresql/psycopg2cffi.py +62 -0
- sqlalchemy/dialects/postgresql/ranges.py +1002 -0
- sqlalchemy/dialects/postgresql/types.py +388 -0
- sqlalchemy/dialects/sqlite/__init__.py +59 -0
- sqlalchemy/dialects/sqlite/aiosqlite.py +375 -0
- sqlalchemy/dialects/sqlite/base.py +3103 -0
- sqlalchemy/dialects/sqlite/dml.py +314 -0
- sqlalchemy/dialects/sqlite/json.py +134 -0
- sqlalchemy/dialects/sqlite/provision.py +237 -0
- sqlalchemy/dialects/sqlite/pysqlcipher.py +166 -0
- sqlalchemy/dialects/sqlite/pysqlite.py +959 -0
- sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
- sqlalchemy/engine/__init__.py +62 -0
- sqlalchemy/engine/_processors_cy.cp315-win32.pyd +0 -0
- sqlalchemy/engine/_processors_cy.py +92 -0
- sqlalchemy/engine/_result_cy.cp315-win32.pyd +0 -0
- sqlalchemy/engine/_result_cy.py +711 -0
- sqlalchemy/engine/_row_cy.cp315-win32.pyd +0 -0
- sqlalchemy/engine/_row_cy.py +232 -0
- sqlalchemy/engine/_util_cy.cp315-win32.pyd +0 -0
- sqlalchemy/engine/_util_cy.py +136 -0
- sqlalchemy/engine/base.py +3357 -0
- sqlalchemy/engine/characteristics.py +155 -0
- sqlalchemy/engine/create.py +877 -0
- sqlalchemy/engine/cursor.py +2425 -0
- sqlalchemy/engine/default.py +2627 -0
- sqlalchemy/engine/events.py +965 -0
- sqlalchemy/engine/interfaces.py +3636 -0
- sqlalchemy/engine/mock.py +133 -0
- sqlalchemy/engine/processors.py +83 -0
- sqlalchemy/engine/reflection.py +2141 -0
- sqlalchemy/engine/result.py +2012 -0
- sqlalchemy/engine/row.py +397 -0
- sqlalchemy/engine/strategies.py +16 -0
- sqlalchemy/engine/url.py +922 -0
- sqlalchemy/engine/util.py +164 -0
- sqlalchemy/event/__init__.py +26 -0
- sqlalchemy/event/api.py +220 -0
- sqlalchemy/event/attr.py +675 -0
- sqlalchemy/event/base.py +473 -0
- sqlalchemy/event/legacy.py +259 -0
- sqlalchemy/event/registry.py +391 -0
- sqlalchemy/events.py +17 -0
- sqlalchemy/exc.py +939 -0
- sqlalchemy/ext/__init__.py +10 -0
- sqlalchemy/ext/associationproxy.py +2073 -0
- sqlalchemy/ext/asyncio/__init__.py +29 -0
- sqlalchemy/ext/asyncio/base.py +281 -0
- sqlalchemy/ext/asyncio/engine.py +1487 -0
- sqlalchemy/ext/asyncio/exc.py +21 -0
- sqlalchemy/ext/asyncio/result.py +994 -0
- sqlalchemy/ext/asyncio/scoping.py +1679 -0
- sqlalchemy/ext/asyncio/session.py +2006 -0
- sqlalchemy/ext/automap.py +1702 -0
- sqlalchemy/ext/baked.py +558 -0
- sqlalchemy/ext/compiler.py +601 -0
- sqlalchemy/ext/declarative/__init__.py +65 -0
- sqlalchemy/ext/declarative/extensions.py +561 -0
- sqlalchemy/ext/horizontal_shard.py +481 -0
- sqlalchemy/ext/hybrid.py +1877 -0
- sqlalchemy/ext/indexable.py +364 -0
- sqlalchemy/ext/instrumentation.py +450 -0
- sqlalchemy/ext/mutable.py +1081 -0
- sqlalchemy/ext/orderinglist.py +440 -0
- sqlalchemy/ext/serializer.py +184 -0
- sqlalchemy/future/__init__.py +17 -0
- sqlalchemy/future/engine.py +15 -0
- sqlalchemy/inspection.py +188 -0
- sqlalchemy/log.py +279 -0
- sqlalchemy/orm/__init__.py +176 -0
- sqlalchemy/orm/_orm_constructors.py +2694 -0
- sqlalchemy/orm/_typing.py +180 -0
- sqlalchemy/orm/attributes.py +2868 -0
- sqlalchemy/orm/base.py +991 -0
- sqlalchemy/orm/bulk_persistence.py +2168 -0
- sqlalchemy/orm/clsregistry.py +630 -0
- sqlalchemy/orm/collections.py +1569 -0
- sqlalchemy/orm/context.py +3475 -0
- sqlalchemy/orm/decl_api.py +2283 -0
- sqlalchemy/orm/decl_base.py +2320 -0
- sqlalchemy/orm/dependency.py +1306 -0
- sqlalchemy/orm/descriptor_props.py +1183 -0
- sqlalchemy/orm/dynamic.py +306 -0
- sqlalchemy/orm/evaluator.py +378 -0
- sqlalchemy/orm/events.py +3387 -0
- sqlalchemy/orm/exc.py +237 -0
- sqlalchemy/orm/identity.py +302 -0
- sqlalchemy/orm/instrumentation.py +749 -0
- sqlalchemy/orm/interfaces.py +1595 -0
- sqlalchemy/orm/loading.py +1712 -0
- sqlalchemy/orm/mapped_collection.py +557 -0
- sqlalchemy/orm/mapper.py +4465 -0
- sqlalchemy/orm/path_registry.py +907 -0
- sqlalchemy/orm/persistence.py +1790 -0
- sqlalchemy/orm/properties.py +972 -0
- sqlalchemy/orm/query.py +3528 -0
- sqlalchemy/orm/relationships.py +3608 -0
- sqlalchemy/orm/scoping.py +2233 -0
- sqlalchemy/orm/session.py +5468 -0
- sqlalchemy/orm/state.py +1175 -0
- sqlalchemy/orm/state_changes.py +196 -0
- sqlalchemy/orm/strategies.py +3552 -0
- sqlalchemy/orm/strategy_options.py +2648 -0
- sqlalchemy/orm/sync.py +164 -0
- sqlalchemy/orm/unitofwork.py +797 -0
- sqlalchemy/orm/util.py +2461 -0
- sqlalchemy/orm/writeonly.py +701 -0
- sqlalchemy/pool/__init__.py +41 -0
- sqlalchemy/pool/base.py +1540 -0
- sqlalchemy/pool/events.py +375 -0
- sqlalchemy/pool/impl.py +583 -0
- sqlalchemy/py.typed +0 -0
- sqlalchemy/schema.py +75 -0
- sqlalchemy/sql/__init__.py +156 -0
- sqlalchemy/sql/_annotated_cols.py +402 -0
- sqlalchemy/sql/_cache_key_cy.cp315-win32.pyd +0 -0
- sqlalchemy/sql/_cache_key_cy.py +363 -0
- sqlalchemy/sql/_dml_constructors.py +132 -0
- sqlalchemy/sql/_elements_constructors.py +2190 -0
- sqlalchemy/sql/_orm_types.py +19 -0
- sqlalchemy/sql/_selectable_constructors.py +840 -0
- sqlalchemy/sql/_typing.py +500 -0
- sqlalchemy/sql/_util_cy.cp315-win32.pyd +0 -0
- sqlalchemy/sql/_util_cy.pxd +11 -0
- sqlalchemy/sql/_util_cy.py +127 -0
- sqlalchemy/sql/annotation.py +590 -0
- sqlalchemy/sql/base.py +2702 -0
- sqlalchemy/sql/cache_key.py +915 -0
- sqlalchemy/sql/coercions.py +1373 -0
- sqlalchemy/sql/compiler.py +8453 -0
- sqlalchemy/sql/crud.py +1816 -0
- sqlalchemy/sql/ddl.py +1962 -0
- sqlalchemy/sql/default_comparator.py +660 -0
- sqlalchemy/sql/dml.py +2018 -0
- sqlalchemy/sql/elements.py +6057 -0
- sqlalchemy/sql/events.py +458 -0
- sqlalchemy/sql/expression.py +171 -0
- sqlalchemy/sql/functions.py +2380 -0
- sqlalchemy/sql/lambdas.py +1442 -0
- sqlalchemy/sql/naming.py +204 -0
- sqlalchemy/sql/operators.py +2909 -0
- sqlalchemy/sql/roles.py +332 -0
- sqlalchemy/sql/schema.py +7075 -0
- sqlalchemy/sql/selectable.py +7634 -0
- sqlalchemy/sql/sqltypes.py +4130 -0
- sqlalchemy/sql/traversals.py +1041 -0
- sqlalchemy/sql/type_api.py +2450 -0
- sqlalchemy/sql/util.py +1496 -0
- sqlalchemy/sql/visitors.py +1153 -0
- sqlalchemy/testing/__init__.py +97 -0
- sqlalchemy/testing/assertions.py +1007 -0
- sqlalchemy/testing/assertsql.py +519 -0
- sqlalchemy/testing/asyncio.py +128 -0
- sqlalchemy/testing/cancellation.py +237 -0
- sqlalchemy/testing/config.py +440 -0
- sqlalchemy/testing/engines.py +482 -0
- sqlalchemy/testing/entities.py +117 -0
- sqlalchemy/testing/exclusions.py +501 -0
- sqlalchemy/testing/fixtures/__init__.py +30 -0
- sqlalchemy/testing/fixtures/base.py +426 -0
- sqlalchemy/testing/fixtures/mypy.py +247 -0
- sqlalchemy/testing/fixtures/orm.py +227 -0
- sqlalchemy/testing/fixtures/sql.py +538 -0
- sqlalchemy/testing/pickleable.py +155 -0
- sqlalchemy/testing/plugin/__init__.py +6 -0
- sqlalchemy/testing/plugin/bootstrap.py +50 -0
- sqlalchemy/testing/plugin/plugin_base.py +828 -0
- sqlalchemy/testing/plugin/pytestplugin.py +896 -0
- sqlalchemy/testing/profiles_file.py +350 -0
- sqlalchemy/testing/profiling.py +294 -0
- sqlalchemy/testing/provision.py +633 -0
- sqlalchemy/testing/requirements.py +1971 -0
- sqlalchemy/testing/schema.py +198 -0
- sqlalchemy/testing/suite/__init__.py +19 -0
- sqlalchemy/testing/suite/test_cte.py +237 -0
- sqlalchemy/testing/suite/test_ddl.py +420 -0
- sqlalchemy/testing/suite/test_dialect.py +776 -0
- sqlalchemy/testing/suite/test_insert.py +630 -0
- sqlalchemy/testing/suite/test_reflection.py +3815 -0
- sqlalchemy/testing/suite/test_results.py +660 -0
- sqlalchemy/testing/suite/test_rowcount.py +258 -0
- sqlalchemy/testing/suite/test_select.py +2112 -0
- sqlalchemy/testing/suite/test_sequence.py +317 -0
- sqlalchemy/testing/suite/test_table_via_select.py +686 -0
- sqlalchemy/testing/suite/test_types.py +2271 -0
- sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
- sqlalchemy/testing/suite/test_update_delete.py +139 -0
- sqlalchemy/testing/util.py +575 -0
- sqlalchemy/testing/warnings.py +52 -0
- sqlalchemy/types.py +75 -0
- sqlalchemy/util/__init__.py +165 -0
- sqlalchemy/util/_collections.py +688 -0
- sqlalchemy/util/_collections_cy.cp315-win32.pyd +0 -0
- sqlalchemy/util/_collections_cy.pxd +8 -0
- sqlalchemy/util/_collections_cy.py +516 -0
- sqlalchemy/util/_has_cython.py +48 -0
- sqlalchemy/util/_immutabledict_cy.cp315-win32.pyd +0 -0
- sqlalchemy/util/_immutabledict_cy.py +240 -0
- sqlalchemy/util/compat.py +298 -0
- sqlalchemy/util/concurrency.py +272 -0
- sqlalchemy/util/cython.py +95 -0
- sqlalchemy/util/deprecations.py +401 -0
- sqlalchemy/util/langhelpers.py +2797 -0
- sqlalchemy/util/preloaded.py +153 -0
- sqlalchemy/util/queue.py +304 -0
- sqlalchemy/util/tool_support.py +202 -0
- sqlalchemy/util/topological.py +120 -0
- sqlalchemy/util/typing.py +709 -0
- sqlalchemy-2.1.0rc1.dist-info/METADATA +270 -0
- sqlalchemy-2.1.0rc1.dist-info/RECORD +276 -0
- sqlalchemy-2.1.0rc1.dist-info/WHEEL +5 -0
- sqlalchemy-2.1.0rc1.dist-info/licenses/AUTHORS +30 -0
- sqlalchemy-2.1.0rc1.dist-info/licenses/LICENSE +19 -0
- sqlalchemy-2.1.0rc1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
# sql/_cache_key_cy.py
|
|
2
|
+
# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
|
|
3
|
+
# <see AUTHORS file>
|
|
4
|
+
#
|
|
5
|
+
# This module is part of SQLAlchemy and is released under
|
|
6
|
+
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
|
7
|
+
"""Cython implementation of the cache key traversal.
|
|
8
|
+
|
|
9
|
+
The traversal for a single class is represented as a :class:`.ClassAttrs`
|
|
10
|
+
holding one :class:`.AttrInfo` per attribute that participates in the cache
|
|
11
|
+
key. :class:`.AttrInfo` records the attribute name together with an integer
|
|
12
|
+
``kind`` identifying its handler, so that when compiled the per-attribute
|
|
13
|
+
dispatch in :meth:`.BaseHasCacheKey._gen_cache_key` is a comparison against a
|
|
14
|
+
C integer, rather than the chain of Python ``is`` comparisons against
|
|
15
|
+
singleton handler objects that the pure Python traversal loop has to run per
|
|
16
|
+
attribute, per cache key.
|
|
17
|
+
|
|
18
|
+
Traversal kinds that have no inline handler are represented by
|
|
19
|
+
:class:`.AttrInfoMethod`, which additionally carries the Python level visit
|
|
20
|
+
function to call; these are dispatched under the ``_PY_METHOD`` kind.
|
|
21
|
+
|
|
22
|
+
Recursion into child elements calls ``_gen_cache_key()`` on the child, which
|
|
23
|
+
is this compiled method for the classes that inherit it and the Python level
|
|
24
|
+
override otherwise.
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
# mypy: disable-error-code="untyped-decorator"
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
from collections.abc import Sequence
|
|
33
|
+
from enum import Enum
|
|
34
|
+
from typing import Any
|
|
35
|
+
from typing import Callable
|
|
36
|
+
from typing import Dict
|
|
37
|
+
from typing import List
|
|
38
|
+
from typing import Optional
|
|
39
|
+
from typing import Tuple
|
|
40
|
+
from typing import TYPE_CHECKING
|
|
41
|
+
|
|
42
|
+
from .visitors import InternalTraversal
|
|
43
|
+
from .. import util
|
|
44
|
+
|
|
45
|
+
if TYPE_CHECKING:
|
|
46
|
+
from .elements import BindParameter
|
|
47
|
+
from .visitors import _TraverseInternalsType
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# START GENERATED CYTHON IMPORT
|
|
51
|
+
# This section is automatically generated by the script tools/cython_imports.py
|
|
52
|
+
try:
|
|
53
|
+
# NOTE: the cython compiler needs this "import cython" in the file, it
|
|
54
|
+
# can't be only "from sqlalchemy.util import cython" with the fallback
|
|
55
|
+
# in that module
|
|
56
|
+
import cython
|
|
57
|
+
except ModuleNotFoundError:
|
|
58
|
+
from sqlalchemy.util import cython
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _is_compiled() -> bool:
|
|
62
|
+
"""Utility function to indicate if this module is compiled or not."""
|
|
63
|
+
return cython.compiled # type: ignore[no-any-return,unused-ignore]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# END GENERATED CYTHON IMPORT
|
|
67
|
+
|
|
68
|
+
if cython.compiled:
|
|
69
|
+
from cython.cimports.sqlalchemy.sql._util_cy import anon_map
|
|
70
|
+
else:
|
|
71
|
+
from ._util_cy import anon_map
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class BaseHasCacheKey:
|
|
75
|
+
__slots__ = ()
|
|
76
|
+
|
|
77
|
+
@cython.boundscheck(False)
|
|
78
|
+
@cython.wraparound(False)
|
|
79
|
+
def _gen_cache_key(
|
|
80
|
+
self, am: anon_map, bindparams: List[BindParameter[Any]]
|
|
81
|
+
) -> Optional[Tuple[Any, ...]]:
|
|
82
|
+
"""return an optional cache key.
|
|
83
|
+
|
|
84
|
+
The cache key is a tuple which can contain any series of
|
|
85
|
+
objects that are hashable and also identifies
|
|
86
|
+
this object uniquely within the presence of a larger SQL expression
|
|
87
|
+
or statement, for the purposes of caching the resulting query.
|
|
88
|
+
|
|
89
|
+
The cache key should be based on the SQL compiled structure that would
|
|
90
|
+
ultimately be produced. That is, two structures that are composed in
|
|
91
|
+
exactly the same way should produce the same cache key; any difference
|
|
92
|
+
in the structures that would affect the SQL string or the type handlers
|
|
93
|
+
should result in a different cache key.
|
|
94
|
+
|
|
95
|
+
If a structure cannot produce a useful cache key, the NO_CACHE
|
|
96
|
+
symbol should be added to the anon_map and the method should
|
|
97
|
+
return None.
|
|
98
|
+
|
|
99
|
+
"""
|
|
100
|
+
cls = self.__class__
|
|
101
|
+
|
|
102
|
+
id_: object
|
|
103
|
+
|
|
104
|
+
if cython.compiled:
|
|
105
|
+
# inline of anon_map.get_anon(), which otherwise costs a Python
|
|
106
|
+
# level call plus a throwaway (id, found) tuple per node
|
|
107
|
+
am_dict = cython.cast(dict, am)
|
|
108
|
+
idself: object = id(self)
|
|
109
|
+
if idself in am_dict:
|
|
110
|
+
return (am_dict[idself], cls)
|
|
111
|
+
id_ = am._add_missing(idself)
|
|
112
|
+
else:
|
|
113
|
+
id_, found = am.get_anon(self)
|
|
114
|
+
if found:
|
|
115
|
+
return (id_, cls)
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
dispatcher = cls.__dict__["_generated_cache_key_traversal"]
|
|
119
|
+
except KeyError:
|
|
120
|
+
# traversals.py -> _preconfigure_traversals()
|
|
121
|
+
# may be used to run these ahead of time, but
|
|
122
|
+
# is not enabled right now.
|
|
123
|
+
# this block will generate any remaining dispatchers.
|
|
124
|
+
dispatcher = cls._generate_cache_attrs() # type: ignore[attr-defined] # noqa: E501
|
|
125
|
+
|
|
126
|
+
if dispatcher is _NO_CACHE:
|
|
127
|
+
am[_NO_CACHE] = True
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
disp_cls: ClassAttrs = dispatcher
|
|
131
|
+
result: list[Any] = [id_, cls]
|
|
132
|
+
elements: tuple[Any, ...] = disp_cls.elements
|
|
133
|
+
element: AttrInfo
|
|
134
|
+
py_method: AttrInfoMethod
|
|
135
|
+
|
|
136
|
+
for element in elements:
|
|
137
|
+
obj = getattr(self, element.name)
|
|
138
|
+
kind: cython.int = element.kind
|
|
139
|
+
if obj is not None:
|
|
140
|
+
if kind == _STATIC_CACHE_KEY:
|
|
141
|
+
sck = obj._static_cache_key
|
|
142
|
+
if sck is _NO_CACHE:
|
|
143
|
+
am[_NO_CACHE] = True
|
|
144
|
+
return None
|
|
145
|
+
result.append(element.name)
|
|
146
|
+
result.append(sck)
|
|
147
|
+
elif kind == _ANON_NAME:
|
|
148
|
+
if isinstance(obj, disp_cls.anon_label):
|
|
149
|
+
obj = obj.apply_map(am) # type: ignore[attr-defined]
|
|
150
|
+
result.append(element.name)
|
|
151
|
+
result.append(obj)
|
|
152
|
+
elif kind == _CALL_GEN_CACHE_KEY:
|
|
153
|
+
result.append(element.name)
|
|
154
|
+
result.append(obj._gen_cache_key(am, bindparams))
|
|
155
|
+
|
|
156
|
+
# remaining cache functions are against
|
|
157
|
+
# Python tuples, dicts, lists, etc. so we can skip
|
|
158
|
+
# if they are empty
|
|
159
|
+
elif obj:
|
|
160
|
+
if kind == _CACHE_IN_PLACE:
|
|
161
|
+
result.append(element.name)
|
|
162
|
+
result.append(obj)
|
|
163
|
+
elif kind == _PROPAGATE_ATTRS:
|
|
164
|
+
result.append(element.name)
|
|
165
|
+
result.append(obj["compile_state_plugin"])
|
|
166
|
+
if ps := obj["plugin_subject"]:
|
|
167
|
+
result.append(ps._gen_cache_key(am, bindparams))
|
|
168
|
+
else:
|
|
169
|
+
result.append(None)
|
|
170
|
+
|
|
171
|
+
elif kind == _ANNOTATIONS_KEY:
|
|
172
|
+
# obj is here is the _annotations dict. Table uses
|
|
173
|
+
# a memoized version of it. however in other cases,
|
|
174
|
+
# we generate it given am as we may be from a
|
|
175
|
+
# Join, Aliased, etc.
|
|
176
|
+
# see #8790
|
|
177
|
+
|
|
178
|
+
if self._gen_static_annotations_cache_key: # type: ignore[attr-defined] # noqa: E501
|
|
179
|
+
result.extend(self._annotations_cache_key) # type: ignore[attr-defined] # noqa: E501
|
|
180
|
+
else:
|
|
181
|
+
result.extend(self._gen_annotations_cache_key(am)) # type: ignore[attr-defined] # noqa: E501
|
|
182
|
+
|
|
183
|
+
elif kind == _CLAUSEELEMENT_LIST:
|
|
184
|
+
result.append(element.name)
|
|
185
|
+
result.append(
|
|
186
|
+
tuple(
|
|
187
|
+
[
|
|
188
|
+
elem._gen_cache_key(am, bindparams)
|
|
189
|
+
for elem in obj
|
|
190
|
+
]
|
|
191
|
+
),
|
|
192
|
+
)
|
|
193
|
+
elif kind == _PY_METHOD:
|
|
194
|
+
py_method = element # type: ignore[assignment]
|
|
195
|
+
result.extend(
|
|
196
|
+
py_method.meth( # type: ignore[operator]
|
|
197
|
+
py_method.name, obj, self, am, bindparams
|
|
198
|
+
)
|
|
199
|
+
)
|
|
200
|
+
else:
|
|
201
|
+
raise NotImplementedError(f"Unknown kind {kind}")
|
|
202
|
+
|
|
203
|
+
return tuple(result)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class CacheConst(Enum):
|
|
207
|
+
NO_CACHE = 0
|
|
208
|
+
PARAMS = 1
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# NOTE: declarations are invisible from python
|
|
212
|
+
_NO_CACHE = cython.declare(object, CacheConst.NO_CACHE)
|
|
213
|
+
_CACHE_IN_PLACE = cython.declare(cython.const[cython.int], 0)
|
|
214
|
+
_CALL_GEN_CACHE_KEY = cython.declare(cython.const[cython.int], 1)
|
|
215
|
+
_STATIC_CACHE_KEY = cython.declare(cython.const[cython.int], 2)
|
|
216
|
+
_PROPAGATE_ATTRS = cython.declare(cython.const[cython.int], 3)
|
|
217
|
+
_ANON_NAME = cython.declare(cython.const[cython.int], 4)
|
|
218
|
+
_ANNOTATIONS_KEY = cython.declare(cython.const[cython.int], 5)
|
|
219
|
+
_CLAUSEELEMENT_LIST = cython.declare(cython.const[cython.int], 6)
|
|
220
|
+
_PY_METHOD = cython.declare(cython.const[cython.int], 7)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class CacheTraverseTarget(Enum):
|
|
224
|
+
"""Handlers that the traversal implements inline.
|
|
225
|
+
|
|
226
|
+
The value of each member is the integer ``kind`` recorded on the
|
|
227
|
+
:class:`.AttrInfo` for an attribute using that handler, so that the
|
|
228
|
+
traversal can switch on a C integer.
|
|
229
|
+
|
|
230
|
+
"""
|
|
231
|
+
|
|
232
|
+
CACHE_IN_PLACE = _CACHE_IN_PLACE
|
|
233
|
+
CALL_GEN_CACHE_KEY = _CALL_GEN_CACHE_KEY
|
|
234
|
+
STATIC_CACHE_KEY = _STATIC_CACHE_KEY
|
|
235
|
+
PROPAGATE_ATTRS = _PROPAGATE_ATTRS
|
|
236
|
+
ANON_NAME = _ANON_NAME
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
# the traversal kind for each handler that _generate_class_attrs() may find.
|
|
240
|
+
# CacheTraverseTarget members carry their own kind as the enum value; the
|
|
241
|
+
# InternalTraversal symbols below have an inline handler but no dedicated
|
|
242
|
+
# CacheTraverseTarget member.
|
|
243
|
+
_attr_info_kind: Dict[Any, int] = {
|
|
244
|
+
**{member: member.value for member in CacheTraverseTarget},
|
|
245
|
+
InternalTraversal.dp_annotations_key: _ANNOTATIONS_KEY,
|
|
246
|
+
InternalTraversal.dp_clauseelement_list: _CLAUSEELEMENT_LIST,
|
|
247
|
+
InternalTraversal.dp_clauseelement_tuple: _CLAUSEELEMENT_LIST,
|
|
248
|
+
InternalTraversal.dp_memoized_select_entities: _CLAUSEELEMENT_LIST,
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
@cython.cclass
|
|
253
|
+
class AttrInfo:
|
|
254
|
+
"""An attribute of a specific kind"""
|
|
255
|
+
|
|
256
|
+
kind: cython.int
|
|
257
|
+
name: str
|
|
258
|
+
|
|
259
|
+
def __init__(self, kind: cython.int, name: str) -> None:
|
|
260
|
+
self.kind = kind
|
|
261
|
+
self.name = name
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
@cython.cclass
|
|
265
|
+
class AttrInfoMethod(AttrInfo):
|
|
266
|
+
"""An attribute that requires a python method."""
|
|
267
|
+
|
|
268
|
+
meth: object
|
|
269
|
+
|
|
270
|
+
def __init__(self, name: str, arg: object) -> None:
|
|
271
|
+
self.kind = _PY_METHOD
|
|
272
|
+
self.name = name
|
|
273
|
+
self.meth = arg
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@cython.cclass
|
|
277
|
+
class ClassAttrs:
|
|
278
|
+
"""The whole cache key traversal for a single class."""
|
|
279
|
+
|
|
280
|
+
elements: tuple[AttrInfo, ...]
|
|
281
|
+
anon_label: type
|
|
282
|
+
|
|
283
|
+
def __init__(self, elements: Sequence[AttrInfo], anon_label: type) -> None:
|
|
284
|
+
self.elements = tuple(elements)
|
|
285
|
+
self.anon_label = anon_label
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
class _BaseCacheKeyTraversal:
|
|
289
|
+
"""Builds the per-class traversal structure that
|
|
290
|
+
:meth:`.BaseHasCacheKey._gen_cache_key` consumes.
|
|
291
|
+
|
|
292
|
+
Mixed in alongside :class:`.HasTraversalDispatch`, which supplies
|
|
293
|
+
:meth:`.dispatch`; the visit symbols themselves are declared by
|
|
294
|
+
:class:`._CacheKeyTraversal` in ``sql/cache_key.py``.
|
|
295
|
+
|
|
296
|
+
"""
|
|
297
|
+
|
|
298
|
+
__slots__ = ()
|
|
299
|
+
|
|
300
|
+
if TYPE_CHECKING:
|
|
301
|
+
|
|
302
|
+
def dispatch(
|
|
303
|
+
self, visit_symbol: InternalTraversal
|
|
304
|
+
) -> Callable[..., Any]: ...
|
|
305
|
+
|
|
306
|
+
def generate_class_attrs(
|
|
307
|
+
self,
|
|
308
|
+
# NOTE: not annotated as "type": cython enforces annotations on a
|
|
309
|
+
# compiled function and rejects subclasses of builtin types, which
|
|
310
|
+
# fails for any class using a metaclass, e.g. _MetaOptions
|
|
311
|
+
target_cls: Any,
|
|
312
|
+
internal_dispatch: _TraverseInternalsType,
|
|
313
|
+
attrname: str,
|
|
314
|
+
) -> ClassAttrs:
|
|
315
|
+
"""Build the :class:`.ClassAttrs` for ``target_cls`` and memoize it
|
|
316
|
+
on the class under ``attrname``.
|
|
317
|
+
|
|
318
|
+
"""
|
|
319
|
+
class_attrs = self._generate_class_attrs(internal_dispatch)
|
|
320
|
+
setattr(target_cls, attrname, class_attrs)
|
|
321
|
+
return class_attrs
|
|
322
|
+
|
|
323
|
+
@util.preload_module("sqlalchemy.sql.elements")
|
|
324
|
+
def _generate_class_attrs(
|
|
325
|
+
self, internal_dispatch: _TraverseInternalsType
|
|
326
|
+
) -> ClassAttrs:
|
|
327
|
+
"""Resolve the handler for every attribute that participates in the
|
|
328
|
+
cache key for a single class.
|
|
329
|
+
|
|
330
|
+
In contrast to :meth:`.HasTraversalDispatch._generate_dispatcher`,
|
|
331
|
+
which emits a function returning a list of
|
|
332
|
+
``(attrname, value, handler)`` triples that a runtime loop then has to
|
|
333
|
+
interpret, this returns a :class:`.ClassAttrs` holding one
|
|
334
|
+
:class:`.AttrInfo` per attribute, with the handler recorded as an
|
|
335
|
+
integer kind. Which handler applies to which attribute is a fact
|
|
336
|
+
that's constant for the class, so it's resolved once here rather than
|
|
337
|
+
being rediscovered by a chain of ``is`` comparisons on every cache key
|
|
338
|
+
generation.
|
|
339
|
+
|
|
340
|
+
Attributes whose handler has no inline form in the traversal are
|
|
341
|
+
recorded as :class:`.AttrInfoMethod`, which carries the visit method
|
|
342
|
+
itself.
|
|
343
|
+
|
|
344
|
+
"""
|
|
345
|
+
elems: List[AttrInfo] = []
|
|
346
|
+
meth: Any
|
|
347
|
+
|
|
348
|
+
for attrname, visit_sym in internal_dispatch:
|
|
349
|
+
meth = self.dispatch(visit_sym)
|
|
350
|
+
if meth is None:
|
|
351
|
+
continue
|
|
352
|
+
|
|
353
|
+
if meth in _attr_info_kind:
|
|
354
|
+
elems.append(AttrInfo(_attr_info_kind[meth], attrname))
|
|
355
|
+
elif isinstance(meth, (CacheTraverseTarget, InternalTraversal)):
|
|
356
|
+
raise NotImplementedError(
|
|
357
|
+
"No cache key traversal generation rule for symbol "
|
|
358
|
+
f"{meth!r}, used by attribute {attrname!r}"
|
|
359
|
+
)
|
|
360
|
+
else:
|
|
361
|
+
elems.append(AttrInfoMethod(attrname, meth))
|
|
362
|
+
|
|
363
|
+
return ClassAttrs(elems, util.preloaded.sql_elements._anonymous_label)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# sql/_dml_constructors.py
|
|
2
|
+
# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
|
|
3
|
+
# <see AUTHORS file>
|
|
4
|
+
#
|
|
5
|
+
# This module is part of SQLAlchemy and is released under
|
|
6
|
+
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
from .dml import Delete
|
|
13
|
+
from .dml import Insert
|
|
14
|
+
from .dml import Update
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from ._typing import _DMLTableArgument
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def insert(table: _DMLTableArgument) -> Insert:
|
|
21
|
+
"""Construct an :class:`_expression.Insert` object.
|
|
22
|
+
|
|
23
|
+
E.g.::
|
|
24
|
+
|
|
25
|
+
from sqlalchemy import insert
|
|
26
|
+
|
|
27
|
+
stmt = insert(user_table).values(name="username", fullname="Full Username")
|
|
28
|
+
|
|
29
|
+
Similar functionality is available via the
|
|
30
|
+
:meth:`_expression.TableClause.insert` method on
|
|
31
|
+
:class:`_schema.Table`.
|
|
32
|
+
|
|
33
|
+
.. seealso::
|
|
34
|
+
|
|
35
|
+
:ref:`tutorial_core_insert` - in the :ref:`unified_tutorial`
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
:param table: :class:`_expression.TableClause`
|
|
39
|
+
which is the subject of the
|
|
40
|
+
insert.
|
|
41
|
+
|
|
42
|
+
:param values: collection of values to be inserted; see
|
|
43
|
+
:meth:`_expression.Insert.values`
|
|
44
|
+
for a description of allowed formats here.
|
|
45
|
+
Can be omitted entirely; a :class:`_expression.Insert` construct
|
|
46
|
+
will also dynamically render the VALUES clause at execution time
|
|
47
|
+
based on the parameters passed to :meth:`_engine.Connection.execute`.
|
|
48
|
+
|
|
49
|
+
:param inline: if True, no attempt will be made to retrieve the
|
|
50
|
+
SQL-generated default values to be provided within the statement;
|
|
51
|
+
in particular,
|
|
52
|
+
this allows SQL expressions to be rendered 'inline' within the
|
|
53
|
+
statement without the need to pre-execute them beforehand; for
|
|
54
|
+
backends that support "returning", this turns off the "implicit
|
|
55
|
+
returning" feature for the statement.
|
|
56
|
+
|
|
57
|
+
If both :paramref:`_expression.insert.values` and compile-time bind
|
|
58
|
+
parameters are present, the compile-time bind parameters override the
|
|
59
|
+
information specified within :paramref:`_expression.insert.values` on a
|
|
60
|
+
per-key basis.
|
|
61
|
+
|
|
62
|
+
The keys within :paramref:`_expression.Insert.values` can be either
|
|
63
|
+
:class:`~sqlalchemy.schema.Column` objects or their string
|
|
64
|
+
identifiers. Each key may reference one of:
|
|
65
|
+
|
|
66
|
+
* a literal data value (i.e. string, number, etc.);
|
|
67
|
+
* a Column object;
|
|
68
|
+
* a SELECT statement.
|
|
69
|
+
|
|
70
|
+
If a ``SELECT`` statement is specified which references this
|
|
71
|
+
``INSERT`` statement's table, the statement will be correlated
|
|
72
|
+
against the ``INSERT`` statement.
|
|
73
|
+
|
|
74
|
+
.. seealso::
|
|
75
|
+
|
|
76
|
+
:ref:`tutorial_core_insert` - in the :ref:`unified_tutorial`
|
|
77
|
+
|
|
78
|
+
""" # noqa: E501
|
|
79
|
+
return Insert(table)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def update(table: _DMLTableArgument) -> Update:
|
|
83
|
+
r"""Construct an :class:`_expression.Update` object.
|
|
84
|
+
|
|
85
|
+
E.g.::
|
|
86
|
+
|
|
87
|
+
from sqlalchemy import update
|
|
88
|
+
|
|
89
|
+
stmt = (
|
|
90
|
+
update(user_table).where(user_table.c.id == 5).values(name="user #5")
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
Similar functionality is available via the
|
|
94
|
+
:meth:`_expression.TableClause.update` method on
|
|
95
|
+
:class:`_schema.Table`.
|
|
96
|
+
|
|
97
|
+
:param table: A :class:`_schema.Table`
|
|
98
|
+
object representing the database
|
|
99
|
+
table to be updated.
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
.. seealso::
|
|
103
|
+
|
|
104
|
+
:ref:`tutorial_core_update_delete` - in the :ref:`unified_tutorial`
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
""" # noqa: E501
|
|
108
|
+
return Update(table)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def delete(table: _DMLTableArgument) -> Delete:
|
|
112
|
+
r"""Construct :class:`_expression.Delete` object.
|
|
113
|
+
|
|
114
|
+
E.g.::
|
|
115
|
+
|
|
116
|
+
from sqlalchemy import delete
|
|
117
|
+
|
|
118
|
+
stmt = delete(user_table).where(user_table.c.id == 5)
|
|
119
|
+
|
|
120
|
+
Similar functionality is available via the
|
|
121
|
+
:meth:`_expression.TableClause.delete` method on
|
|
122
|
+
:class:`_schema.Table`.
|
|
123
|
+
|
|
124
|
+
:param table: The table to delete rows from.
|
|
125
|
+
|
|
126
|
+
.. seealso::
|
|
127
|
+
|
|
128
|
+
:ref:`tutorial_core_update_delete` - in the :ref:`unified_tutorial`
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
"""
|
|
132
|
+
return Delete(table)
|