SQLAlchemy 2.1.0rc1__cp315-cp315-win_amd64.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.
Files changed (276) hide show
  1. sqlalchemy/__init__.py +299 -0
  2. sqlalchemy/connectors/__init__.py +18 -0
  3. sqlalchemy/connectors/aioodbc.py +171 -0
  4. sqlalchemy/connectors/asyncio.py +476 -0
  5. sqlalchemy/connectors/pyodbc.py +248 -0
  6. sqlalchemy/dialects/__init__.py +62 -0
  7. sqlalchemy/dialects/_typing.py +29 -0
  8. sqlalchemy/dialects/mssql/__init__.py +88 -0
  9. sqlalchemy/dialects/mssql/aioodbc.py +63 -0
  10. sqlalchemy/dialects/mssql/base.py +4833 -0
  11. sqlalchemy/dialects/mssql/information_schema.py +345 -0
  12. sqlalchemy/dialects/mssql/json.py +140 -0
  13. sqlalchemy/dialects/mssql/mssqlpython.py +242 -0
  14. sqlalchemy/dialects/mssql/provision.py +196 -0
  15. sqlalchemy/dialects/mssql/pymssql.py +130 -0
  16. sqlalchemy/dialects/mssql/pyodbc.py +697 -0
  17. sqlalchemy/dialects/mysql/__init__.py +106 -0
  18. sqlalchemy/dialects/mysql/_mariadb_shim.py +312 -0
  19. sqlalchemy/dialects/mysql/aiomysql.py +260 -0
  20. sqlalchemy/dialects/mysql/asyncmy.py +241 -0
  21. sqlalchemy/dialects/mysql/base.py +3896 -0
  22. sqlalchemy/dialects/mysql/cymysql.py +107 -0
  23. sqlalchemy/dialects/mysql/dml.py +279 -0
  24. sqlalchemy/dialects/mysql/enumerated.py +277 -0
  25. sqlalchemy/dialects/mysql/expression.py +146 -0
  26. sqlalchemy/dialects/mysql/json.py +92 -0
  27. sqlalchemy/dialects/mysql/mariadb.py +67 -0
  28. sqlalchemy/dialects/mysql/mariadbconnector.py +314 -0
  29. sqlalchemy/dialects/mysql/mysqlconnector.py +291 -0
  30. sqlalchemy/dialects/mysql/mysqldb.py +318 -0
  31. sqlalchemy/dialects/mysql/provision.py +153 -0
  32. sqlalchemy/dialects/mysql/pymysql.py +188 -0
  33. sqlalchemy/dialects/mysql/pyodbc.py +157 -0
  34. sqlalchemy/dialects/mysql/reflection.py +724 -0
  35. sqlalchemy/dialects/mysql/reserved_words.py +570 -0
  36. sqlalchemy/dialects/mysql/types.py +845 -0
  37. sqlalchemy/dialects/oracle/__init__.py +85 -0
  38. sqlalchemy/dialects/oracle/base.py +3847 -0
  39. sqlalchemy/dialects/oracle/cx_oracle.py +1736 -0
  40. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  41. sqlalchemy/dialects/oracle/json.py +157 -0
  42. sqlalchemy/dialects/oracle/oracledb.py +898 -0
  43. sqlalchemy/dialects/oracle/provision.py +288 -0
  44. sqlalchemy/dialects/oracle/types.py +367 -0
  45. sqlalchemy/dialects/oracle/vector.py +366 -0
  46. sqlalchemy/dialects/postgresql/__init__.py +170 -0
  47. sqlalchemy/dialects/postgresql/_psycopg_common.py +232 -0
  48. sqlalchemy/dialects/postgresql/array.py +534 -0
  49. sqlalchemy/dialects/postgresql/asyncpg.py +1318 -0
  50. sqlalchemy/dialects/postgresql/base.py +5935 -0
  51. sqlalchemy/dialects/postgresql/bitstring.py +327 -0
  52. sqlalchemy/dialects/postgresql/dml.py +360 -0
  53. sqlalchemy/dialects/postgresql/ext.py +599 -0
  54. sqlalchemy/dialects/postgresql/hstore.py +422 -0
  55. sqlalchemy/dialects/postgresql/json.py +411 -0
  56. sqlalchemy/dialects/postgresql/named_types.py +535 -0
  57. sqlalchemy/dialects/postgresql/operators.py +129 -0
  58. sqlalchemy/dialects/postgresql/pg8000.py +655 -0
  59. sqlalchemy/dialects/postgresql/pg_catalog.py +345 -0
  60. sqlalchemy/dialects/postgresql/provision.py +202 -0
  61. sqlalchemy/dialects/postgresql/psycopg.py +800 -0
  62. sqlalchemy/dialects/postgresql/psycopg2.py +860 -0
  63. sqlalchemy/dialects/postgresql/psycopg2cffi.py +62 -0
  64. sqlalchemy/dialects/postgresql/ranges.py +1002 -0
  65. sqlalchemy/dialects/postgresql/types.py +388 -0
  66. sqlalchemy/dialects/sqlite/__init__.py +59 -0
  67. sqlalchemy/dialects/sqlite/aiosqlite.py +375 -0
  68. sqlalchemy/dialects/sqlite/base.py +3103 -0
  69. sqlalchemy/dialects/sqlite/dml.py +314 -0
  70. sqlalchemy/dialects/sqlite/json.py +134 -0
  71. sqlalchemy/dialects/sqlite/provision.py +237 -0
  72. sqlalchemy/dialects/sqlite/pysqlcipher.py +166 -0
  73. sqlalchemy/dialects/sqlite/pysqlite.py +959 -0
  74. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  75. sqlalchemy/engine/__init__.py +62 -0
  76. sqlalchemy/engine/_processors_cy.cp315-win_amd64.pyd +0 -0
  77. sqlalchemy/engine/_processors_cy.py +92 -0
  78. sqlalchemy/engine/_result_cy.cp315-win_amd64.pyd +0 -0
  79. sqlalchemy/engine/_result_cy.py +711 -0
  80. sqlalchemy/engine/_row_cy.cp315-win_amd64.pyd +0 -0
  81. sqlalchemy/engine/_row_cy.py +232 -0
  82. sqlalchemy/engine/_util_cy.cp315-win_amd64.pyd +0 -0
  83. sqlalchemy/engine/_util_cy.py +136 -0
  84. sqlalchemy/engine/base.py +3357 -0
  85. sqlalchemy/engine/characteristics.py +155 -0
  86. sqlalchemy/engine/create.py +877 -0
  87. sqlalchemy/engine/cursor.py +2425 -0
  88. sqlalchemy/engine/default.py +2627 -0
  89. sqlalchemy/engine/events.py +965 -0
  90. sqlalchemy/engine/interfaces.py +3636 -0
  91. sqlalchemy/engine/mock.py +133 -0
  92. sqlalchemy/engine/processors.py +83 -0
  93. sqlalchemy/engine/reflection.py +2141 -0
  94. sqlalchemy/engine/result.py +2012 -0
  95. sqlalchemy/engine/row.py +397 -0
  96. sqlalchemy/engine/strategies.py +16 -0
  97. sqlalchemy/engine/url.py +922 -0
  98. sqlalchemy/engine/util.py +164 -0
  99. sqlalchemy/event/__init__.py +26 -0
  100. sqlalchemy/event/api.py +220 -0
  101. sqlalchemy/event/attr.py +675 -0
  102. sqlalchemy/event/base.py +473 -0
  103. sqlalchemy/event/legacy.py +259 -0
  104. sqlalchemy/event/registry.py +391 -0
  105. sqlalchemy/events.py +17 -0
  106. sqlalchemy/exc.py +939 -0
  107. sqlalchemy/ext/__init__.py +10 -0
  108. sqlalchemy/ext/associationproxy.py +2073 -0
  109. sqlalchemy/ext/asyncio/__init__.py +29 -0
  110. sqlalchemy/ext/asyncio/base.py +281 -0
  111. sqlalchemy/ext/asyncio/engine.py +1487 -0
  112. sqlalchemy/ext/asyncio/exc.py +21 -0
  113. sqlalchemy/ext/asyncio/result.py +994 -0
  114. sqlalchemy/ext/asyncio/scoping.py +1679 -0
  115. sqlalchemy/ext/asyncio/session.py +2006 -0
  116. sqlalchemy/ext/automap.py +1702 -0
  117. sqlalchemy/ext/baked.py +558 -0
  118. sqlalchemy/ext/compiler.py +601 -0
  119. sqlalchemy/ext/declarative/__init__.py +65 -0
  120. sqlalchemy/ext/declarative/extensions.py +561 -0
  121. sqlalchemy/ext/horizontal_shard.py +481 -0
  122. sqlalchemy/ext/hybrid.py +1877 -0
  123. sqlalchemy/ext/indexable.py +364 -0
  124. sqlalchemy/ext/instrumentation.py +450 -0
  125. sqlalchemy/ext/mutable.py +1081 -0
  126. sqlalchemy/ext/orderinglist.py +440 -0
  127. sqlalchemy/ext/serializer.py +184 -0
  128. sqlalchemy/future/__init__.py +17 -0
  129. sqlalchemy/future/engine.py +15 -0
  130. sqlalchemy/inspection.py +188 -0
  131. sqlalchemy/log.py +279 -0
  132. sqlalchemy/orm/__init__.py +176 -0
  133. sqlalchemy/orm/_orm_constructors.py +2694 -0
  134. sqlalchemy/orm/_typing.py +180 -0
  135. sqlalchemy/orm/attributes.py +2868 -0
  136. sqlalchemy/orm/base.py +991 -0
  137. sqlalchemy/orm/bulk_persistence.py +2168 -0
  138. sqlalchemy/orm/clsregistry.py +630 -0
  139. sqlalchemy/orm/collections.py +1569 -0
  140. sqlalchemy/orm/context.py +3475 -0
  141. sqlalchemy/orm/decl_api.py +2283 -0
  142. sqlalchemy/orm/decl_base.py +2320 -0
  143. sqlalchemy/orm/dependency.py +1306 -0
  144. sqlalchemy/orm/descriptor_props.py +1183 -0
  145. sqlalchemy/orm/dynamic.py +306 -0
  146. sqlalchemy/orm/evaluator.py +378 -0
  147. sqlalchemy/orm/events.py +3387 -0
  148. sqlalchemy/orm/exc.py +237 -0
  149. sqlalchemy/orm/identity.py +302 -0
  150. sqlalchemy/orm/instrumentation.py +749 -0
  151. sqlalchemy/orm/interfaces.py +1595 -0
  152. sqlalchemy/orm/loading.py +1712 -0
  153. sqlalchemy/orm/mapped_collection.py +557 -0
  154. sqlalchemy/orm/mapper.py +4465 -0
  155. sqlalchemy/orm/path_registry.py +907 -0
  156. sqlalchemy/orm/persistence.py +1790 -0
  157. sqlalchemy/orm/properties.py +972 -0
  158. sqlalchemy/orm/query.py +3528 -0
  159. sqlalchemy/orm/relationships.py +3608 -0
  160. sqlalchemy/orm/scoping.py +2233 -0
  161. sqlalchemy/orm/session.py +5468 -0
  162. sqlalchemy/orm/state.py +1175 -0
  163. sqlalchemy/orm/state_changes.py +196 -0
  164. sqlalchemy/orm/strategies.py +3552 -0
  165. sqlalchemy/orm/strategy_options.py +2648 -0
  166. sqlalchemy/orm/sync.py +164 -0
  167. sqlalchemy/orm/unitofwork.py +797 -0
  168. sqlalchemy/orm/util.py +2461 -0
  169. sqlalchemy/orm/writeonly.py +701 -0
  170. sqlalchemy/pool/__init__.py +41 -0
  171. sqlalchemy/pool/base.py +1540 -0
  172. sqlalchemy/pool/events.py +375 -0
  173. sqlalchemy/pool/impl.py +583 -0
  174. sqlalchemy/py.typed +0 -0
  175. sqlalchemy/schema.py +75 -0
  176. sqlalchemy/sql/__init__.py +156 -0
  177. sqlalchemy/sql/_annotated_cols.py +402 -0
  178. sqlalchemy/sql/_cache_key_cy.cp315-win_amd64.pyd +0 -0
  179. sqlalchemy/sql/_cache_key_cy.py +363 -0
  180. sqlalchemy/sql/_dml_constructors.py +132 -0
  181. sqlalchemy/sql/_elements_constructors.py +2190 -0
  182. sqlalchemy/sql/_orm_types.py +19 -0
  183. sqlalchemy/sql/_selectable_constructors.py +840 -0
  184. sqlalchemy/sql/_typing.py +500 -0
  185. sqlalchemy/sql/_util_cy.cp315-win_amd64.pyd +0 -0
  186. sqlalchemy/sql/_util_cy.pxd +11 -0
  187. sqlalchemy/sql/_util_cy.py +127 -0
  188. sqlalchemy/sql/annotation.py +590 -0
  189. sqlalchemy/sql/base.py +2702 -0
  190. sqlalchemy/sql/cache_key.py +915 -0
  191. sqlalchemy/sql/coercions.py +1373 -0
  192. sqlalchemy/sql/compiler.py +8453 -0
  193. sqlalchemy/sql/crud.py +1816 -0
  194. sqlalchemy/sql/ddl.py +1962 -0
  195. sqlalchemy/sql/default_comparator.py +660 -0
  196. sqlalchemy/sql/dml.py +2018 -0
  197. sqlalchemy/sql/elements.py +6057 -0
  198. sqlalchemy/sql/events.py +458 -0
  199. sqlalchemy/sql/expression.py +171 -0
  200. sqlalchemy/sql/functions.py +2380 -0
  201. sqlalchemy/sql/lambdas.py +1442 -0
  202. sqlalchemy/sql/naming.py +204 -0
  203. sqlalchemy/sql/operators.py +2909 -0
  204. sqlalchemy/sql/roles.py +332 -0
  205. sqlalchemy/sql/schema.py +7075 -0
  206. sqlalchemy/sql/selectable.py +7634 -0
  207. sqlalchemy/sql/sqltypes.py +4130 -0
  208. sqlalchemy/sql/traversals.py +1041 -0
  209. sqlalchemy/sql/type_api.py +2450 -0
  210. sqlalchemy/sql/util.py +1496 -0
  211. sqlalchemy/sql/visitors.py +1153 -0
  212. sqlalchemy/testing/__init__.py +97 -0
  213. sqlalchemy/testing/assertions.py +1007 -0
  214. sqlalchemy/testing/assertsql.py +519 -0
  215. sqlalchemy/testing/asyncio.py +128 -0
  216. sqlalchemy/testing/cancellation.py +237 -0
  217. sqlalchemy/testing/config.py +440 -0
  218. sqlalchemy/testing/engines.py +482 -0
  219. sqlalchemy/testing/entities.py +117 -0
  220. sqlalchemy/testing/exclusions.py +501 -0
  221. sqlalchemy/testing/fixtures/__init__.py +30 -0
  222. sqlalchemy/testing/fixtures/base.py +426 -0
  223. sqlalchemy/testing/fixtures/mypy.py +247 -0
  224. sqlalchemy/testing/fixtures/orm.py +227 -0
  225. sqlalchemy/testing/fixtures/sql.py +538 -0
  226. sqlalchemy/testing/pickleable.py +155 -0
  227. sqlalchemy/testing/plugin/__init__.py +6 -0
  228. sqlalchemy/testing/plugin/bootstrap.py +50 -0
  229. sqlalchemy/testing/plugin/plugin_base.py +828 -0
  230. sqlalchemy/testing/plugin/pytestplugin.py +896 -0
  231. sqlalchemy/testing/profiles_file.py +350 -0
  232. sqlalchemy/testing/profiling.py +294 -0
  233. sqlalchemy/testing/provision.py +633 -0
  234. sqlalchemy/testing/requirements.py +1971 -0
  235. sqlalchemy/testing/schema.py +198 -0
  236. sqlalchemy/testing/suite/__init__.py +19 -0
  237. sqlalchemy/testing/suite/test_cte.py +237 -0
  238. sqlalchemy/testing/suite/test_ddl.py +420 -0
  239. sqlalchemy/testing/suite/test_dialect.py +776 -0
  240. sqlalchemy/testing/suite/test_insert.py +630 -0
  241. sqlalchemy/testing/suite/test_reflection.py +3815 -0
  242. sqlalchemy/testing/suite/test_results.py +660 -0
  243. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  244. sqlalchemy/testing/suite/test_select.py +2112 -0
  245. sqlalchemy/testing/suite/test_sequence.py +317 -0
  246. sqlalchemy/testing/suite/test_table_via_select.py +686 -0
  247. sqlalchemy/testing/suite/test_types.py +2271 -0
  248. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  249. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  250. sqlalchemy/testing/util.py +575 -0
  251. sqlalchemy/testing/warnings.py +52 -0
  252. sqlalchemy/types.py +75 -0
  253. sqlalchemy/util/__init__.py +165 -0
  254. sqlalchemy/util/_collections.py +688 -0
  255. sqlalchemy/util/_collections_cy.cp315-win_amd64.pyd +0 -0
  256. sqlalchemy/util/_collections_cy.pxd +8 -0
  257. sqlalchemy/util/_collections_cy.py +516 -0
  258. sqlalchemy/util/_has_cython.py +48 -0
  259. sqlalchemy/util/_immutabledict_cy.cp315-win_amd64.pyd +0 -0
  260. sqlalchemy/util/_immutabledict_cy.py +240 -0
  261. sqlalchemy/util/compat.py +298 -0
  262. sqlalchemy/util/concurrency.py +272 -0
  263. sqlalchemy/util/cython.py +95 -0
  264. sqlalchemy/util/deprecations.py +401 -0
  265. sqlalchemy/util/langhelpers.py +2797 -0
  266. sqlalchemy/util/preloaded.py +153 -0
  267. sqlalchemy/util/queue.py +304 -0
  268. sqlalchemy/util/tool_support.py +202 -0
  269. sqlalchemy/util/topological.py +120 -0
  270. sqlalchemy/util/typing.py +709 -0
  271. sqlalchemy-2.1.0rc1.dist-info/METADATA +270 -0
  272. sqlalchemy-2.1.0rc1.dist-info/RECORD +276 -0
  273. sqlalchemy-2.1.0rc1.dist-info/WHEEL +5 -0
  274. sqlalchemy-2.1.0rc1.dist-info/licenses/AUTHORS +30 -0
  275. sqlalchemy-2.1.0rc1.dist-info/licenses/LICENSE +19 -0
  276. 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)