SQLAlchemy 2.1.0rc1__cp315-cp315-win_arm64.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_arm64.pyd +0 -0
  77. sqlalchemy/engine/_processors_cy.py +92 -0
  78. sqlalchemy/engine/_result_cy.cp315-win_arm64.pyd +0 -0
  79. sqlalchemy/engine/_result_cy.py +711 -0
  80. sqlalchemy/engine/_row_cy.cp315-win_arm64.pyd +0 -0
  81. sqlalchemy/engine/_row_cy.py +232 -0
  82. sqlalchemy/engine/_util_cy.cp315-win_arm64.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_arm64.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_arm64.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_arm64.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_arm64.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,3475 @@
1
+ # orm/context.py
2
+ # Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
3
+ # <see AUTHORS file>
4
+ #
5
+ # This module is part of SQLAlchemy and is released under
6
+ # the MIT License: https://www.opensource.org/licenses/mit-license.php
7
+ # mypy: ignore-errors
8
+
9
+ from __future__ import annotations
10
+
11
+ import collections
12
+ import itertools
13
+ from typing import Any
14
+ from typing import cast
15
+ from typing import Dict
16
+ from typing import Iterable
17
+ from typing import List
18
+ from typing import Optional
19
+ from typing import Set
20
+ from typing import Tuple
21
+ from typing import Type
22
+ from typing import TYPE_CHECKING
23
+ from typing import TypeVar
24
+ from typing import Union
25
+
26
+ from . import attributes
27
+ from . import interfaces
28
+ from . import loading
29
+ from .base import _is_aliased_class
30
+ from .interfaces import ORMColumnDescription
31
+ from .interfaces import ORMColumnsClauseRole
32
+ from .path_registry import PathRegistry
33
+ from .util import _entity_corresponds_to
34
+ from .util import _ORMJoin
35
+ from .util import _TraceAdaptRole
36
+ from .util import AliasedClass
37
+ from .util import Bundle
38
+ from .util import ORMAdapter
39
+ from .util import ORMStatementAdapter
40
+ from .. import exc as sa_exc
41
+ from .. import future
42
+ from .. import inspect
43
+ from .. import sql
44
+ from .. import util
45
+ from ..sql import coercions
46
+ from ..sql import expression
47
+ from ..sql import roles
48
+ from ..sql import util as sql_util
49
+ from ..sql import visitors
50
+ from ..sql._typing import is_dml
51
+ from ..sql._typing import is_insert_update
52
+ from ..sql._typing import is_select_base
53
+ from ..sql.base import _select_iterables
54
+ from ..sql.base import CacheableOptions
55
+ from ..sql.base import CompileState
56
+ from ..sql.base import Executable
57
+ from ..sql.base import ExecutableStatement
58
+ from ..sql.base import Generative
59
+ from ..sql.base import Options
60
+ from ..sql.dml import UpdateBase
61
+ from ..sql.elements import GroupedElement
62
+ from ..sql.elements import TextClause
63
+ from ..sql.selectable import CompoundSelectState
64
+ from ..sql.selectable import LABEL_STYLE_DISAMBIGUATE_ONLY
65
+ from ..sql.selectable import LABEL_STYLE_NONE
66
+ from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL
67
+ from ..sql.selectable import Select
68
+ from ..sql.selectable import SelectLabelStyle
69
+ from ..sql.selectable import SelectState
70
+ from ..sql.selectable import TypedReturnsRows
71
+ from ..sql.visitors import InternalTraversal
72
+ from ..util.typing import TupleAny
73
+ from ..util.typing import TypeVarTuple
74
+ from ..util.typing import Unpack
75
+
76
+ if TYPE_CHECKING:
77
+ from ._typing import _InternalEntityType
78
+ from ._typing import OrmExecuteOptionsParameter
79
+ from .loading import _PostLoad
80
+ from .mapper import Mapper
81
+ from .query import Query
82
+ from .session import _BindArguments
83
+ from .session import Session
84
+ from ..engine import Result
85
+ from ..engine.interfaces import _CoreSingleExecuteParams
86
+ from ..sql._typing import _ColumnsClauseArgument
87
+ from ..sql.compiler import SQLCompiler
88
+ from ..sql.dml import _DMLTableElement
89
+ from ..sql.elements import ColumnElement
90
+ from ..sql.selectable import _JoinTargetElement
91
+ from ..sql.selectable import _LabelConventionCallable
92
+ from ..sql.selectable import _SetupJoinsElement
93
+ from ..sql.selectable import ExecutableReturnsRows
94
+ from ..sql.selectable import SelectBase
95
+ from ..sql.type_api import TypeEngine
96
+
97
+ _T = TypeVar("_T", bound=Any)
98
+ _Ts = TypeVarTuple("_Ts")
99
+ _path_registry = PathRegistry.root
100
+
101
+ LABEL_STYLE_LEGACY_ORM = SelectLabelStyle.LABEL_STYLE_LEGACY_ORM
102
+
103
+
104
+ class QueryContext:
105
+ __slots__ = (
106
+ "top_level_context",
107
+ "compile_state",
108
+ "query",
109
+ "user_passed_query",
110
+ "params",
111
+ "load_options",
112
+ "bind_arguments",
113
+ "execution_options",
114
+ "session",
115
+ "autoflush",
116
+ "populate_existing",
117
+ "invoke_all_eagers",
118
+ "version_check",
119
+ "refresh_state",
120
+ "create_eager_joins",
121
+ "propagated_loader_options",
122
+ "attributes",
123
+ "runid",
124
+ "partials",
125
+ "post_load_paths",
126
+ "identity_token",
127
+ "yield_per",
128
+ "loaders_require_buffering",
129
+ "loaders_require_uniquing",
130
+ )
131
+
132
+ runid: int
133
+ post_load_paths: Dict[PathRegistry, _PostLoad]
134
+ compile_state: _ORMCompileState
135
+
136
+ @property
137
+ def requires_uniquing(self) -> bool:
138
+ return bool(self.compile_state.multi_row_eager_loaders)
139
+
140
+ class default_load_options(Options):
141
+ _only_return_tuples = False
142
+ _populate_existing = False
143
+ _version_check = False
144
+ _invoke_all_eagers = True
145
+ _autoflush = True
146
+ _identity_token = None
147
+ _yield_per = None
148
+ _refresh_state = None
149
+ _lazy_loaded_from = None
150
+ _legacy_uniquing = False
151
+ _sa_top_level_orm_context = None
152
+ _is_user_refresh = False
153
+
154
+ def __init__(
155
+ self,
156
+ compile_state: CompileState,
157
+ statement: Union[
158
+ Select[Unpack[TupleAny]],
159
+ FromStatement[Unpack[TupleAny]],
160
+ UpdateBase,
161
+ ],
162
+ user_passed_query: Union[
163
+ Select[Unpack[TupleAny]],
164
+ FromStatement[Unpack[TupleAny]],
165
+ UpdateBase,
166
+ ],
167
+ params: _CoreSingleExecuteParams,
168
+ session: Session,
169
+ load_options: Union[
170
+ Type[QueryContext.default_load_options],
171
+ QueryContext.default_load_options,
172
+ ],
173
+ execution_options: Optional[OrmExecuteOptionsParameter] = None,
174
+ bind_arguments: Optional[_BindArguments] = None,
175
+ ):
176
+ self.load_options = load_options
177
+ self.execution_options = execution_options or util.EMPTY_DICT
178
+ self.bind_arguments = bind_arguments or util.EMPTY_DICT
179
+ self.compile_state = compile_state
180
+ self.query = statement
181
+
182
+ # the query that the end user passed to Session.execute() or similar.
183
+ # this is usually the same as .query, except in the bulk_persistence
184
+ # routines where a separate FromStatement is manufactured in the
185
+ # compile stage; this allows differentiation in that case.
186
+ self.user_passed_query = user_passed_query
187
+
188
+ self.session = session
189
+ self.loaders_require_buffering = False
190
+ self.loaders_require_uniquing = False
191
+ self.params = params
192
+ self.top_level_context = load_options._sa_top_level_orm_context
193
+
194
+ cached_options = compile_state.select_statement._with_options
195
+ uncached_options = user_passed_query._with_options
196
+
197
+ # see issue #7447 , #8399 for some background
198
+ # propagated loader options will be present on loaded InstanceState
199
+ # objects under state.load_options and are typically used by
200
+ # LazyLoader to apply options to the SELECT statement it emits.
201
+ # For compile state options (i.e. loader strategy options), these
202
+ # need to line up with the ".load_path" attribute which in
203
+ # loader.py is pulled from context.compile_state.current_path.
204
+ # so, this means these options have to be the ones from the
205
+ # *cached* statement that's travelling with compile_state, not the
206
+ # *current* statement which won't match up for an ad-hoc
207
+ # AliasedClass
208
+ self.propagated_loader_options = tuple(
209
+ opt._adapt_cached_option_to_uncached_option(self, uncached_opt)
210
+ for opt, uncached_opt in zip(cached_options, uncached_options)
211
+ if opt.propagate_to_loaders
212
+ )
213
+
214
+ self.attributes = dict(compile_state.attributes)
215
+
216
+ self.autoflush = load_options._autoflush
217
+ self.populate_existing = load_options._populate_existing
218
+ self.invoke_all_eagers = load_options._invoke_all_eagers
219
+ self.version_check = load_options._version_check
220
+ self.refresh_state = load_options._refresh_state
221
+ self.yield_per = load_options._yield_per
222
+ self.identity_token = load_options._identity_token
223
+
224
+ def _get_top_level_context(self) -> QueryContext:
225
+ return self.top_level_context or self
226
+
227
+
228
+ _orm_load_exec_options = util.immutabledict(
229
+ {"_result_disable_adapt_to_context": True}
230
+ )
231
+
232
+
233
+ class _AbstractORMCompileState(CompileState):
234
+ is_dml_returning = False
235
+
236
+ def _init_global_attributes(
237
+ self, statement, compiler, *, toplevel, process_criteria_for_toplevel
238
+ ):
239
+ self.attributes = {}
240
+
241
+ if compiler is None:
242
+ # this is the legacy / testing only ORM _compile_state() use case.
243
+ # there is no need to apply criteria options for this.
244
+ self.global_attributes = {}
245
+ assert toplevel
246
+ return
247
+ else:
248
+ self.global_attributes = ga = compiler._global_attributes
249
+
250
+ if toplevel:
251
+ ga["toplevel_orm"] = True
252
+
253
+ if process_criteria_for_toplevel:
254
+ for opt in statement._with_options:
255
+ if opt._is_criteria_option:
256
+ opt.process_compile_state(self)
257
+
258
+ return
259
+ elif ga.get("toplevel_orm", False):
260
+ return
261
+
262
+ stack_0 = compiler.stack[0]
263
+
264
+ try:
265
+ toplevel_stmt = stack_0["selectable"]
266
+ except KeyError:
267
+ pass
268
+ else:
269
+ for opt in toplevel_stmt._with_options:
270
+ if opt._is_compile_state and opt._is_criteria_option:
271
+ opt.process_compile_state(self)
272
+
273
+ ga["toplevel_orm"] = True
274
+
275
+ @classmethod
276
+ def create_for_statement(
277
+ cls,
278
+ statement: Executable,
279
+ compiler: SQLCompiler,
280
+ **kw: Any,
281
+ ) -> CompileState:
282
+ """Create a context for a statement given a :class:`.Compiler`.
283
+
284
+ This method is always invoked in the context of SQLCompiler.process().
285
+
286
+ For a Select object, this would be invoked from
287
+ SQLCompiler.visit_select(). For the special FromStatement object used
288
+ by Query to indicate "Query.from_statement()", this is called by
289
+ FromStatement._compiler_dispatch() that would be called by
290
+ SQLCompiler.process().
291
+ """
292
+ return super().create_for_statement(statement, compiler, **kw)
293
+
294
+ @classmethod
295
+ def orm_pre_session_exec(
296
+ cls,
297
+ session,
298
+ statement,
299
+ params,
300
+ execution_options,
301
+ bind_arguments,
302
+ is_pre_event,
303
+ ):
304
+ raise NotImplementedError()
305
+
306
+ @classmethod
307
+ def orm_execute_statement(
308
+ cls,
309
+ session,
310
+ statement,
311
+ params,
312
+ execution_options,
313
+ bind_arguments,
314
+ conn,
315
+ ) -> Result:
316
+ result = conn.execute(
317
+ statement, params or {}, execution_options=execution_options
318
+ )
319
+ return cls.orm_setup_cursor_result(
320
+ session,
321
+ statement,
322
+ params,
323
+ execution_options,
324
+ bind_arguments,
325
+ result,
326
+ )
327
+
328
+ @classmethod
329
+ def orm_setup_cursor_result(
330
+ cls,
331
+ session,
332
+ statement,
333
+ params,
334
+ execution_options,
335
+ bind_arguments,
336
+ result,
337
+ ):
338
+ raise NotImplementedError()
339
+
340
+
341
+ class _AutoflushOnlyORMCompileState(_AbstractORMCompileState):
342
+ """ORM compile state that is a passthrough, except for autoflush."""
343
+
344
+ @classmethod
345
+ def orm_pre_session_exec(
346
+ cls,
347
+ session,
348
+ statement,
349
+ params,
350
+ execution_options,
351
+ bind_arguments,
352
+ is_pre_event,
353
+ ):
354
+ # consume result-level load_options. These may have been set up
355
+ # in an ORMExecuteState hook
356
+ (
357
+ load_options,
358
+ execution_options,
359
+ ) = QueryContext.default_load_options.from_execution_options(
360
+ "_sa_orm_load_options",
361
+ {
362
+ "autoflush",
363
+ },
364
+ execution_options,
365
+ statement._execution_options,
366
+ )
367
+
368
+ if not is_pre_event and load_options._autoflush:
369
+ session._autoflush()
370
+
371
+ return statement, execution_options, params
372
+
373
+ @classmethod
374
+ def orm_setup_cursor_result(
375
+ cls,
376
+ session,
377
+ statement,
378
+ params,
379
+ execution_options,
380
+ bind_arguments,
381
+ result,
382
+ ):
383
+ return result
384
+
385
+
386
+ class _ORMCompileState(_AbstractORMCompileState):
387
+ class default_compile_options(CacheableOptions):
388
+ _cache_key_traversal = [
389
+ ("_use_legacy_query_style", InternalTraversal.dp_boolean),
390
+ ("_for_statement", InternalTraversal.dp_boolean),
391
+ ("_bake_ok", InternalTraversal.dp_boolean),
392
+ ("_current_path", InternalTraversal.dp_has_cache_key),
393
+ ("_enable_single_crit", InternalTraversal.dp_boolean),
394
+ ("_enable_eagerloads", InternalTraversal.dp_boolean),
395
+ ("_only_load_props", InternalTraversal.dp_plain_obj),
396
+ ("_set_base_alias", InternalTraversal.dp_boolean),
397
+ ("_for_refresh_state", InternalTraversal.dp_boolean),
398
+ ("_render_for_subquery", InternalTraversal.dp_boolean),
399
+ ("_is_star", InternalTraversal.dp_boolean),
400
+ ]
401
+
402
+ # set to True by default from Query._statement_20(), to indicate
403
+ # the rendered query should look like a legacy ORM query. right
404
+ # now this basically indicates we should use tablename_columnname
405
+ # style labels. Generally indicates the statement originated
406
+ # from a Query object.
407
+ _use_legacy_query_style = False
408
+
409
+ # set *only* when we are coming from the Query.statement
410
+ # accessor, or a Query-level equivalent such as
411
+ # query.subquery(). this supersedes "toplevel".
412
+ _for_statement = False
413
+
414
+ _bake_ok = True
415
+ _current_path = _path_registry
416
+ _enable_single_crit = True
417
+ _enable_eagerloads = True
418
+ _only_load_props = None
419
+ _set_base_alias = False
420
+ _for_refresh_state = False
421
+ _render_for_subquery = False
422
+ _is_star = False
423
+
424
+ attributes: Dict[Any, Any]
425
+ global_attributes: Dict[Any, Any]
426
+
427
+ statement: Union[
428
+ Select[Unpack[TupleAny]], FromStatement[Unpack[TupleAny]], UpdateBase
429
+ ]
430
+ select_statement: Union[
431
+ Select[Unpack[TupleAny]], FromStatement[Unpack[TupleAny]]
432
+ ]
433
+ _entities: List[_QueryEntity]
434
+ _polymorphic_adapters: Dict[_InternalEntityType, ORMAdapter]
435
+ compile_options: Union[
436
+ Type[default_compile_options], default_compile_options
437
+ ]
438
+ _primary_entity: Optional[_QueryEntity]
439
+ use_legacy_query_style: bool
440
+ _label_convention: _LabelConventionCallable
441
+ primary_columns: List[ColumnElement[Any]]
442
+ secondary_columns: List[ColumnElement[Any]]
443
+ dedupe_columns: Set[ColumnElement[Any]]
444
+ create_eager_joins: List[
445
+ # TODO: this structure is set up by JoinedLoader
446
+ TupleAny
447
+ ]
448
+ current_path: PathRegistry = _path_registry
449
+ _has_mapper_entities = False
450
+
451
+ def __init__(self, *arg, **kw):
452
+ raise NotImplementedError()
453
+
454
+ @classmethod
455
+ def create_for_statement(
456
+ cls,
457
+ statement: Executable,
458
+ compiler: SQLCompiler,
459
+ **kw: Any,
460
+ ) -> _ORMCompileState:
461
+ return cls._create_orm_context(
462
+ cast("Union[Select, FromStatement]", statement),
463
+ toplevel=not compiler.stack,
464
+ compiler=compiler,
465
+ **kw,
466
+ )
467
+
468
+ @classmethod
469
+ def _create_orm_context(
470
+ cls,
471
+ statement: Union[Select, FromStatement],
472
+ *,
473
+ toplevel: bool,
474
+ compiler: Optional[SQLCompiler],
475
+ **kw: Any,
476
+ ) -> _ORMCompileState:
477
+ raise NotImplementedError()
478
+
479
+ def _append_dedupe_col_collection(self, obj, col_collection):
480
+ dedupe = self.dedupe_columns
481
+ if obj not in dedupe:
482
+ dedupe.add(obj)
483
+ col_collection.append(obj)
484
+
485
+ @classmethod
486
+ def _column_naming_convention(
487
+ cls, label_style: SelectLabelStyle, legacy: bool
488
+ ) -> _LabelConventionCallable:
489
+ if legacy:
490
+
491
+ def name(col, col_name=None):
492
+ if col_name:
493
+ return col_name
494
+ else:
495
+ return getattr(col, "key")
496
+
497
+ return name
498
+ else:
499
+ return SelectState._column_naming_convention(label_style)
500
+
501
+ @classmethod
502
+ def get_column_descriptions(cls, statement):
503
+ return _column_descriptions(statement)
504
+
505
+ @classmethod
506
+ def orm_pre_session_exec(
507
+ cls,
508
+ session,
509
+ statement,
510
+ params,
511
+ execution_options,
512
+ bind_arguments,
513
+ is_pre_event,
514
+ ):
515
+ # consume result-level load_options. These may have been set up
516
+ # in an ORMExecuteState hook
517
+ (
518
+ load_options,
519
+ execution_options,
520
+ ) = QueryContext.default_load_options.from_execution_options(
521
+ "_sa_orm_load_options",
522
+ {
523
+ "populate_existing",
524
+ "autoflush",
525
+ "yield_per",
526
+ "identity_token",
527
+ "sa_top_level_orm_context",
528
+ },
529
+ execution_options,
530
+ statement._execution_options,
531
+ )
532
+
533
+ # default execution options for ORM results:
534
+ # 1. _result_disable_adapt_to_context=True
535
+ # this will disable the ResultSetMetadata._adapt_to_context()
536
+ # step which we don't need, as we have result processors cached
537
+ # against the original SELECT statement before caching.
538
+
539
+ if "sa_top_level_orm_context" in execution_options:
540
+ ctx = execution_options["sa_top_level_orm_context"]
541
+ execution_options = ctx.query._execution_options.merge_with(
542
+ ctx.execution_options, execution_options
543
+ )
544
+
545
+ if not execution_options:
546
+ execution_options = _orm_load_exec_options
547
+ else:
548
+ execution_options = execution_options.union(_orm_load_exec_options)
549
+
550
+ # would have been placed here by legacy Query only
551
+ if load_options._yield_per:
552
+ execution_options = execution_options.union(
553
+ {"yield_per": load_options._yield_per}
554
+ )
555
+
556
+ if (
557
+ getattr(statement._compile_options, "_current_path", None)
558
+ and len(statement._compile_options._current_path) > 10
559
+ and execution_options.get("compiled_cache", True) is not None
560
+ ):
561
+ execution_options: util.immutabledict[str, Any] = (
562
+ execution_options.union(
563
+ {
564
+ "compiled_cache": None,
565
+ "_cache_disable_reason": "excess depth for "
566
+ "ORM loader options",
567
+ }
568
+ )
569
+ )
570
+
571
+ bind_arguments["clause"] = statement
572
+
573
+ # new in 1.4 - the coercions system is leveraged to allow the
574
+ # "subject" mapper of a statement be propagated to the top
575
+ # as the statement is built. "subject" mapper is the generally
576
+ # standard object used as an identifier for multi-database schemes.
577
+
578
+ # we are here based on the fact that _propagate_attrs contains
579
+ # "compile_state_plugin": "orm". The "plugin_subject"
580
+ # needs to be present as well.
581
+
582
+ try:
583
+ plugin_subject = statement._propagate_attrs["plugin_subject"]
584
+ except KeyError:
585
+ assert False, "statement had 'orm' plugin but no plugin_subject"
586
+ else:
587
+ if plugin_subject:
588
+ bind_arguments["mapper"] = plugin_subject.mapper
589
+
590
+ if not is_pre_event and load_options._autoflush:
591
+ session._autoflush()
592
+
593
+ return statement, execution_options, params
594
+
595
+ @classmethod
596
+ def orm_setup_cursor_result(
597
+ cls,
598
+ session,
599
+ statement,
600
+ params,
601
+ execution_options,
602
+ bind_arguments,
603
+ result,
604
+ ):
605
+ execution_context = result.context
606
+ compile_state = execution_context.compiled.compile_state
607
+
608
+ # cover edge case where ORM entities used in legacy select
609
+ # were passed to session.execute:
610
+ # session.execute(legacy_select([User.id, User.name]))
611
+ # see test_query->test_legacy_tuple_old_select
612
+
613
+ load_options = execution_options.get(
614
+ "_sa_orm_load_options", QueryContext.default_load_options
615
+ )
616
+
617
+ if compile_state.compile_options._is_star:
618
+ return result
619
+
620
+ querycontext = QueryContext(
621
+ compile_state,
622
+ statement,
623
+ statement,
624
+ params,
625
+ session,
626
+ load_options,
627
+ execution_options,
628
+ bind_arguments,
629
+ )
630
+ return loading.instances(result, querycontext)
631
+
632
+ @property
633
+ def _lead_mapper_entities(self):
634
+ """return all _MapperEntity objects in the lead entities collection.
635
+
636
+ Does **not** include entities that have been replaced by
637
+ with_entities(), with_only_columns()
638
+
639
+ """
640
+ return [
641
+ ent for ent in self._entities if isinstance(ent, _MapperEntity)
642
+ ]
643
+
644
+ def _create_with_polymorphic_adapter(self, ext_info, selectable):
645
+ """given MapperEntity or ORMColumnEntity, setup polymorphic loading
646
+ if called for by the Mapper.
647
+
648
+ As of #8168 in 2.0.0rc1, polymorphic adapters, which greatly increase
649
+ the complexity of the query creation process, are not used at all
650
+ except in the quasi-legacy cases of with_polymorphic referring to an
651
+ alias and/or subquery. This would apply to concrete polymorphic
652
+ loading, and joined inheritance where a subquery is
653
+ passed to with_polymorphic (which is completely unnecessary in modern
654
+ use).
655
+
656
+ TODO: What is a "quasi-legacy" case? Do we need this method with
657
+ 2.0 style select() queries or not? Why is with_polymorphic referring
658
+ to an alias or subquery "legacy" ?
659
+
660
+ """
661
+ if (
662
+ not ext_info.is_aliased_class
663
+ and ext_info.mapper.persist_selectable
664
+ not in self._polymorphic_adapters
665
+ ):
666
+ for mp in ext_info.mapper.iterate_to_root():
667
+ self._mapper_loads_polymorphically_with(
668
+ mp,
669
+ ORMAdapter(
670
+ _TraceAdaptRole.WITH_POLYMORPHIC_ADAPTER,
671
+ mp,
672
+ equivalents=mp._equivalent_columns,
673
+ selectable=selectable,
674
+ ),
675
+ )
676
+
677
+ def _mapper_loads_polymorphically_with(self, mapper, adapter):
678
+ for m2 in mapper._with_polymorphic_mappers or [mapper]:
679
+ self._polymorphic_adapters[m2] = adapter
680
+
681
+ for m in m2.iterate_to_root():
682
+ self._polymorphic_adapters[m.local_table] = adapter
683
+
684
+ @classmethod
685
+ def _create_entities_collection(cls, query, legacy):
686
+ raise NotImplementedError(
687
+ "this method only works for ORMSelectCompileState"
688
+ )
689
+
690
+
691
+ class _DMLReturningColFilter:
692
+ """a base for an adapter used for the DML RETURNING cases
693
+
694
+ Has a subset of the interface used by
695
+ :class:`.ORMAdapter` and is used for :class:`._QueryEntity`
696
+ instances to set up their columns as used in RETURNING for a
697
+ DML statement.
698
+
699
+ """
700
+
701
+ __slots__ = ("mapper", "columns", "__weakref__")
702
+
703
+ def __init__(self, target_mapper, immediate_dml_mapper):
704
+ if (
705
+ immediate_dml_mapper is not None
706
+ and target_mapper.local_table
707
+ is not immediate_dml_mapper.local_table
708
+ ):
709
+ # joined inh, or in theory other kinds of multi-table mappings
710
+ self.mapper = immediate_dml_mapper
711
+ else:
712
+ # single inh, normal mappings, etc.
713
+ self.mapper = target_mapper
714
+ self.columns = self.columns = util.WeakPopulateDict(
715
+ self.adapt_check_present # type: ignore
716
+ )
717
+
718
+ def __call__(self, col, as_filter):
719
+ for cc in sql_util._find_columns(col):
720
+ c2 = self.adapt_check_present(cc)
721
+ if c2 is not None:
722
+ return col
723
+ else:
724
+ return None
725
+
726
+ def adapt_check_present(self, col):
727
+ raise NotImplementedError()
728
+
729
+
730
+ class _DMLBulkInsertReturningColFilter(_DMLReturningColFilter):
731
+ """an adapter used for the DML RETURNING case specifically
732
+ for ORM bulk insert (or any hypothetical DML that is splitting out a class
733
+ hierarchy among multiple DML statements....ORM bulk insert is the only
734
+ example right now)
735
+
736
+ its main job is to limit the columns in a RETURNING to only a specific
737
+ mapped table in a hierarchy.
738
+
739
+ """
740
+
741
+ def adapt_check_present(self, col):
742
+ mapper = self.mapper
743
+ prop = mapper._columntoproperty.get(col, None)
744
+ if prop is None:
745
+ return None
746
+ return mapper.local_table.c.corresponding_column(col)
747
+
748
+
749
+ class _DMLUpdateDeleteReturningColFilter(_DMLReturningColFilter):
750
+ """an adapter used for the DML RETURNING case specifically
751
+ for ORM enabled UPDATE/DELETE
752
+
753
+ its main job is to limit the columns in a RETURNING to include
754
+ only direct persisted columns from the immediate selectable, not
755
+ expressions like column_property(), or to also allow columns from other
756
+ mappers for the UPDATE..FROM use case.
757
+
758
+ """
759
+
760
+ def adapt_check_present(self, col):
761
+ mapper = self.mapper
762
+ prop = mapper._columntoproperty.get(col, None)
763
+ if prop is not None:
764
+ # if the col is from the immediate mapper, only return a persisted
765
+ # column, not any kind of column_property expression
766
+ return mapper.persist_selectable.c.corresponding_column(col)
767
+
768
+ # if the col is from some other mapper, just return it, assume the
769
+ # user knows what they are doing
770
+ return col
771
+
772
+
773
+ @sql.base.CompileState.plugin_for("orm", "orm_from_statement")
774
+ class _ORMFromStatementCompileState(_ORMCompileState):
775
+ _from_obj_alias = None
776
+ _has_mapper_entities = False
777
+
778
+ statement_container: FromStatement
779
+ requested_statement: Union[SelectBase, TextClause, UpdateBase]
780
+ dml_table: Optional[_DMLTableElement] = None
781
+
782
+ _has_orm_entities = False
783
+ multi_row_eager_loaders = False
784
+ eager_adding_joins = False
785
+ compound_eager_adapter = None
786
+
787
+ extra_criteria_entities = util.EMPTY_DICT
788
+ eager_joins = util.EMPTY_DICT
789
+
790
+ @classmethod
791
+ def _create_orm_context(
792
+ cls,
793
+ statement: Union[Select, FromStatement],
794
+ *,
795
+ toplevel: bool,
796
+ compiler: Optional[SQLCompiler],
797
+ **kw: Any,
798
+ ) -> _ORMFromStatementCompileState:
799
+ statement_container = statement
800
+
801
+ assert isinstance(statement_container, FromStatement)
802
+
803
+ if compiler is not None and compiler.stack:
804
+ raise sa_exc.CompileError(
805
+ "The ORM FromStatement construct only supports being "
806
+ "invoked as the topmost statement, as it is only intended to "
807
+ "define how result rows should be returned."
808
+ )
809
+
810
+ self = cls.__new__(cls)
811
+ self._primary_entity = None
812
+
813
+ self.use_legacy_query_style = (
814
+ statement_container._compile_options._use_legacy_query_style
815
+ )
816
+ self.statement_container = self.select_statement = statement_container
817
+ self.requested_statement = statement = statement_container.element
818
+
819
+ if statement.is_dml:
820
+ self.dml_table = statement.table
821
+ self.is_dml_returning = True
822
+
823
+ self._entities = []
824
+ self._polymorphic_adapters = {}
825
+
826
+ self.compile_options = statement_container._compile_options
827
+
828
+ if (
829
+ self.use_legacy_query_style
830
+ and isinstance(statement, expression.SelectBase)
831
+ and not statement._is_textual
832
+ and not statement.is_dml
833
+ and statement._label_style is LABEL_STYLE_NONE
834
+ ):
835
+ self.statement = statement.set_label_style(
836
+ LABEL_STYLE_TABLENAME_PLUS_COL
837
+ )
838
+ else:
839
+ self.statement = statement
840
+
841
+ self._label_convention = self._column_naming_convention(
842
+ (
843
+ statement._label_style
844
+ if not statement._is_textual and not statement.is_dml
845
+ else LABEL_STYLE_NONE
846
+ ),
847
+ self.use_legacy_query_style,
848
+ )
849
+
850
+ _QueryEntity.to_compile_state(
851
+ self,
852
+ statement_container._raw_columns,
853
+ self._entities,
854
+ is_current_entities=True,
855
+ )
856
+
857
+ self.current_path = statement_container._compile_options._current_path
858
+
859
+ self._init_global_attributes(
860
+ statement_container,
861
+ compiler,
862
+ process_criteria_for_toplevel=False,
863
+ toplevel=True,
864
+ )
865
+
866
+ if statement_container._with_options:
867
+ for opt in statement_container._with_options:
868
+ if opt._is_compile_state:
869
+ opt.process_compile_state(self)
870
+
871
+ if statement_container._compile_state_funcs:
872
+ for fn, key in statement_container._compile_state_funcs:
873
+ fn(self)
874
+
875
+ self.primary_columns = []
876
+ self.secondary_columns = []
877
+ self.dedupe_columns = set()
878
+ self.create_eager_joins = []
879
+ self._fallback_from_clauses = []
880
+
881
+ self.order_by = None
882
+
883
+ if self.statement._is_text_clause:
884
+ # AbstractTextClause (TextClause, TString) has no "column"
885
+ # objects at all. for this case, we generate columns from our
886
+ # _QueryEntity objects, then flip on all the
887
+ # "please match no matter what" parameters.
888
+ self.extra_criteria_entities = {}
889
+
890
+ for entity in self._entities:
891
+ entity.setup_compile_state(self)
892
+
893
+ compiler._ordered_columns = compiler._textual_ordered_columns = (
894
+ False
895
+ )
896
+
897
+ # enable looser result column matching. this is shown to be
898
+ # needed by test_query.py::TextTest
899
+ compiler._loose_column_name_matching = True
900
+
901
+ for c in self.primary_columns:
902
+ compiler.process(
903
+ c,
904
+ within_columns_clause=True,
905
+ add_to_result_map=compiler._add_to_result_map,
906
+ )
907
+ else:
908
+ # for everyone else, Select, Insert, Update, TextualSelect, they
909
+ # have column objects already. After much
910
+ # experimentation here, the best approach seems to be, use
911
+ # those columns completely, don't interfere with the compiler
912
+ # at all; just in ORM land, use an adapter to convert from
913
+ # our ORM columns to whatever columns are in the statement,
914
+ # before we look in the result row. Adapt on names
915
+ # to accept cases such as issue #9217, however also allow
916
+ # this to be overridden for cases such as #9273.
917
+ self._from_obj_alias = ORMStatementAdapter(
918
+ _TraceAdaptRole.ADAPT_FROM_STATEMENT,
919
+ self.statement,
920
+ adapt_on_names=statement_container._adapt_on_names,
921
+ )
922
+
923
+ return self
924
+
925
+ def _adapt_col_list(self, cols, current_adapter):
926
+ return cols
927
+
928
+ def _get_current_adapter(self):
929
+ return None
930
+
931
+ def setup_dml_returning_compile_state(self, dml_mapper):
932
+ """used by BulkORMInsert, Update, Delete to set up a handler
933
+ for RETURNING to return ORM objects and expressions
934
+
935
+ """
936
+ target_mapper = self.statement._propagate_attrs.get(
937
+ "plugin_subject", None
938
+ )
939
+
940
+ if self.statement.is_insert:
941
+ adapter = _DMLBulkInsertReturningColFilter(
942
+ target_mapper, dml_mapper
943
+ )
944
+ elif self.statement.is_update or self.statement.is_delete:
945
+ adapter = _DMLUpdateDeleteReturningColFilter(
946
+ target_mapper, dml_mapper
947
+ )
948
+ else:
949
+ adapter = None
950
+
951
+ if self.compile_options._is_star and (len(self._entities) != 1):
952
+ raise sa_exc.CompileError(
953
+ "Can't generate ORM query that includes multiple expressions "
954
+ "at the same time as '*'; query for '*' alone if present"
955
+ )
956
+
957
+ for entity in self._entities:
958
+ entity.setup_dml_returning_compile_state(self, adapter)
959
+
960
+
961
+ class FromStatement(GroupedElement, Generative, TypedReturnsRows[Unpack[_Ts]]):
962
+ """Core construct that represents a load of ORM objects from various
963
+ :class:`.ReturnsRows` and other classes including:
964
+
965
+ :class:`.Select`, :class:`.TextClause`, :class:`.TextualSelect`,
966
+ :class:`.CompoundSelect`, :class`.Insert`, :class:`.Update`,
967
+ and in theory, :class:`.Delete`.
968
+
969
+ """
970
+
971
+ __visit_name__ = "orm_from_statement"
972
+
973
+ _compile_options = _ORMFromStatementCompileState.default_compile_options
974
+
975
+ _compile_state_factory = _ORMFromStatementCompileState.create_for_statement
976
+
977
+ _for_update_arg = None
978
+
979
+ element: Union[ExecutableReturnsRows, TextClause]
980
+
981
+ _adapt_on_names: bool
982
+
983
+ _traverse_internals = [
984
+ ("_raw_columns", InternalTraversal.dp_clauseelement_list),
985
+ ("element", InternalTraversal.dp_clauseelement),
986
+ ] + ExecutableStatement._executable_traverse_internals
987
+
988
+ _cache_key_traversal = _traverse_internals + [
989
+ ("_compile_options", InternalTraversal.dp_has_cache_key)
990
+ ]
991
+
992
+ is_from_statement = True
993
+
994
+ def __init__(
995
+ self,
996
+ entities: Iterable[_ColumnsClauseArgument[Any]],
997
+ element: Union[ExecutableReturnsRows, TextClause],
998
+ _adapt_on_names: bool = True,
999
+ ):
1000
+ self._raw_columns = [
1001
+ coercions.expect(
1002
+ roles.ColumnsClauseRole,
1003
+ ent,
1004
+ apply_propagate_attrs=self,
1005
+ post_inspect=True,
1006
+ )
1007
+ for ent in util.to_list(entities)
1008
+ ]
1009
+ self.element = element
1010
+ self.is_dml = element.is_dml
1011
+ self.is_select = element.is_select
1012
+ self.is_delete = element.is_delete
1013
+ self.is_insert = element.is_insert
1014
+ self.is_update = element.is_update
1015
+ self._label_style = (
1016
+ element._label_style if is_select_base(element) else None
1017
+ )
1018
+ self._adapt_on_names = _adapt_on_names
1019
+
1020
+ def _compiler_dispatch(self, compiler, **kw):
1021
+ """provide a fixed _compiler_dispatch method.
1022
+
1023
+ This is roughly similar to using the sqlalchemy.ext.compiler
1024
+ ``@compiles`` extension.
1025
+
1026
+ """
1027
+
1028
+ compile_state = self._compile_state_factory(self, compiler, **kw)
1029
+
1030
+ toplevel = not compiler.stack
1031
+
1032
+ if toplevel:
1033
+ compiler.compile_state = compile_state
1034
+
1035
+ return compiler.process(compile_state.statement, **kw)
1036
+
1037
+ @property
1038
+ def column_descriptions(self):
1039
+ """Return a :term:`plugin-enabled` 'column descriptions' structure
1040
+ referring to the columns which are SELECTed by this statement.
1041
+
1042
+ See the section :ref:`queryguide_inspection` for an overview
1043
+ of this feature.
1044
+
1045
+ .. seealso::
1046
+
1047
+ :ref:`queryguide_inspection` - ORM background
1048
+
1049
+ """
1050
+ meth = cast(
1051
+ _ORMSelectCompileState, SelectState.get_plugin_class(self)
1052
+ ).get_column_descriptions
1053
+ return meth(self)
1054
+
1055
+ def _ensure_disambiguated_names(self):
1056
+ return self
1057
+
1058
+ def get_children(self, **kw):
1059
+ yield from itertools.chain.from_iterable(
1060
+ element._from_objects for element in self._raw_columns
1061
+ )
1062
+ yield from super().get_children(**kw)
1063
+
1064
+ @property
1065
+ def _all_selected_columns(self):
1066
+ return self.element._all_selected_columns
1067
+
1068
+ @property
1069
+ def _return_defaults(self):
1070
+ return self.element._return_defaults if is_dml(self.element) else None
1071
+
1072
+ @property
1073
+ def _returning(self):
1074
+ return self.element._returning if is_dml(self.element) else None
1075
+
1076
+ @property
1077
+ def _inline(self):
1078
+ return self.element._inline if is_insert_update(self.element) else None
1079
+
1080
+
1081
+ @sql.base.CompileState.plugin_for("orm", "compound_select")
1082
+ class _CompoundSelectCompileState(
1083
+ _AutoflushOnlyORMCompileState, CompoundSelectState
1084
+ ):
1085
+ pass
1086
+
1087
+
1088
+ @sql.base.CompileState.plugin_for("orm", "select")
1089
+ class _ORMSelectCompileState(_ORMCompileState, SelectState):
1090
+ _already_joined_edges = ()
1091
+
1092
+ _memoized_entities = util.EMPTY_DICT
1093
+
1094
+ _from_obj_alias = None
1095
+ _has_mapper_entities = False
1096
+
1097
+ _has_orm_entities = False
1098
+ multi_row_eager_loaders = False
1099
+ eager_adding_joins = False
1100
+ compound_eager_adapter = None
1101
+
1102
+ correlate = None
1103
+ correlate_except = None
1104
+ _where_criteria = ()
1105
+ _having_criteria = ()
1106
+
1107
+ @classmethod
1108
+ def _create_orm_context(
1109
+ cls,
1110
+ statement: Union[Select, FromStatement],
1111
+ *,
1112
+ toplevel: bool,
1113
+ compiler: Optional[SQLCompiler],
1114
+ **kw: Any,
1115
+ ) -> _ORMSelectCompileState:
1116
+
1117
+ self = cls.__new__(cls)
1118
+
1119
+ select_statement = statement
1120
+
1121
+ # if we are a select() that was never a legacy Query, we won't
1122
+ # have ORM level compile options.
1123
+ statement._compile_options = cls.default_compile_options.safe_merge(
1124
+ statement._compile_options
1125
+ )
1126
+
1127
+ if select_statement._execution_options:
1128
+ # execution options should not impact the compilation of a
1129
+ # query, and at the moment subqueryloader is putting some things
1130
+ # in here that we explicitly don't want stuck in a cache.
1131
+ self.select_statement = select_statement._clone()
1132
+ self.select_statement._execution_options = util.EMPTY_DICT
1133
+ else:
1134
+ self.select_statement = select_statement
1135
+
1136
+ # indicates this select() came from Query.statement
1137
+ self.for_statement = select_statement._compile_options._for_statement
1138
+
1139
+ # generally if we are from Query or directly from a select()
1140
+ self.use_legacy_query_style = (
1141
+ select_statement._compile_options._use_legacy_query_style
1142
+ )
1143
+
1144
+ self._entities = []
1145
+ self._primary_entity = None
1146
+ self._polymorphic_adapters = {}
1147
+
1148
+ self.compile_options = select_statement._compile_options
1149
+
1150
+ if not toplevel:
1151
+ # for subqueries, turn off eagerloads and set
1152
+ # "render_for_subquery".
1153
+ self.compile_options += {
1154
+ "_enable_eagerloads": False,
1155
+ "_render_for_subquery": True,
1156
+ }
1157
+
1158
+ # determine label style. we can make different decisions here.
1159
+ # at the moment, trying to see if we can always use DISAMBIGUATE_ONLY
1160
+ # rather than LABEL_STYLE_NONE, and if we can use disambiguate style
1161
+ # for new style ORM selects too.
1162
+ if (
1163
+ self.use_legacy_query_style
1164
+ and self.select_statement._label_style is LABEL_STYLE_LEGACY_ORM
1165
+ ):
1166
+ if not self.for_statement:
1167
+ self.label_style = LABEL_STYLE_TABLENAME_PLUS_COL
1168
+ else:
1169
+ self.label_style = LABEL_STYLE_DISAMBIGUATE_ONLY
1170
+ else:
1171
+ self.label_style = self.select_statement._label_style
1172
+
1173
+ if select_statement._memoized_select_entities:
1174
+ self._memoized_entities = {
1175
+ memoized_entities: _QueryEntity.to_compile_state(
1176
+ self,
1177
+ memoized_entities._raw_columns,
1178
+ [],
1179
+ is_current_entities=False,
1180
+ )
1181
+ for memoized_entities in (
1182
+ select_statement._memoized_select_entities
1183
+ )
1184
+ }
1185
+
1186
+ # label_convention is stateful and will yield deduping keys if it
1187
+ # sees the same key twice. therefore it's important that it is not
1188
+ # invoked for the above "memoized" entities that aren't actually
1189
+ # in the columns clause
1190
+ self._label_convention = self._column_naming_convention(
1191
+ statement._label_style, self.use_legacy_query_style
1192
+ )
1193
+
1194
+ _QueryEntity.to_compile_state(
1195
+ self,
1196
+ select_statement._raw_columns,
1197
+ self._entities,
1198
+ is_current_entities=True,
1199
+ )
1200
+
1201
+ self.current_path = select_statement._compile_options._current_path
1202
+
1203
+ self.eager_order_by = ()
1204
+
1205
+ self._init_global_attributes(
1206
+ select_statement,
1207
+ compiler,
1208
+ toplevel=toplevel,
1209
+ process_criteria_for_toplevel=False,
1210
+ )
1211
+
1212
+ if toplevel and (
1213
+ select_statement._with_options
1214
+ or select_statement._memoized_select_entities
1215
+ ):
1216
+ for (
1217
+ memoized_entities
1218
+ ) in select_statement._memoized_select_entities:
1219
+ for opt in memoized_entities._with_options:
1220
+ if opt._is_compile_state:
1221
+ opt.process_compile_state_replaced_entities(
1222
+ self,
1223
+ [
1224
+ ent
1225
+ for ent in self._memoized_entities[
1226
+ memoized_entities
1227
+ ]
1228
+ if isinstance(ent, _MapperEntity)
1229
+ ],
1230
+ )
1231
+
1232
+ for opt in self.select_statement._with_options:
1233
+ if opt._is_compile_state:
1234
+ opt.process_compile_state(self)
1235
+
1236
+ # uncomment to print out the context.attributes structure
1237
+ # after it's been set up above
1238
+ # self._dump_option_struct()
1239
+
1240
+ if select_statement._compile_state_funcs:
1241
+ for fn, key in select_statement._compile_state_funcs:
1242
+ fn(self)
1243
+
1244
+ self.primary_columns = []
1245
+ self.secondary_columns = []
1246
+ self.dedupe_columns = set()
1247
+ self.eager_joins = {}
1248
+ self.extra_criteria_entities = {}
1249
+ self.create_eager_joins = []
1250
+ self._fallback_from_clauses = []
1251
+
1252
+ # normalize the FROM clauses early by themselves, as this makes
1253
+ # it an easier job when we need to assemble a JOIN onto these,
1254
+ # for select.join() as well as joinedload(). As of 1.4 there are now
1255
+ # potentially more complex sets of FROM objects here as the use
1256
+ # of lambda statements for lazyload, load_on_pk etc. uses more
1257
+ # cloning of the select() construct. See #6495
1258
+ self.from_clauses = self._normalize_froms(
1259
+ info.selectable for info in select_statement._from_obj
1260
+ )
1261
+
1262
+ # this is a fairly arbitrary break into a second method,
1263
+ # so it might be nicer to break up create_for_statement()
1264
+ # and _setup_for_generate into three or four logical sections
1265
+ self._setup_for_generate()
1266
+
1267
+ SelectState.__init__(self, self.statement, compiler, **kw)
1268
+ return self
1269
+
1270
+ def _dump_option_struct(self):
1271
+ print("\n---------------------------------------------------\n")
1272
+ print(f"current path: {self.current_path}")
1273
+ for key in self.attributes:
1274
+ if isinstance(key, tuple) and key[0] == "loader":
1275
+ print(f"\nLoader: {PathRegistry.coerce(key[1])}")
1276
+ print(f" {self.attributes[key]}")
1277
+ print(f" {self.attributes[key].__dict__}")
1278
+ elif isinstance(key, tuple) and key[0] == "path_with_polymorphic":
1279
+ print(f"\nWith Polymorphic: {PathRegistry.coerce(key[1])}")
1280
+ print(f" {self.attributes[key]}")
1281
+
1282
+ def _setup_for_generate(self):
1283
+ query = self.select_statement
1284
+
1285
+ self.statement = None
1286
+ self._join_entities = ()
1287
+
1288
+ if self.compile_options._set_base_alias:
1289
+ # legacy Query only
1290
+ self._set_select_from_alias()
1291
+
1292
+ for memoized_entities in query._memoized_select_entities:
1293
+ if memoized_entities._setup_joins:
1294
+ self._join(
1295
+ memoized_entities._setup_joins,
1296
+ self._memoized_entities[memoized_entities],
1297
+ )
1298
+
1299
+ if query._setup_joins:
1300
+ self._join(query._setup_joins, self._entities)
1301
+
1302
+ current_adapter = self._get_current_adapter()
1303
+
1304
+ if query._where_criteria:
1305
+ self._where_criteria = query._where_criteria
1306
+
1307
+ if current_adapter:
1308
+ self._where_criteria = tuple(
1309
+ current_adapter(crit, True)
1310
+ for crit in self._where_criteria
1311
+ )
1312
+
1313
+ # TODO: some complexity with order_by here was due to mapper.order_by.
1314
+ # now that this is removed we can hopefully make order_by /
1315
+ # group_by act identically to how they are in Core select.
1316
+ self.order_by = (
1317
+ self._adapt_col_list(query._order_by_clauses, current_adapter)
1318
+ if current_adapter and query._order_by_clauses not in (None, False)
1319
+ else query._order_by_clauses
1320
+ )
1321
+
1322
+ if query._having_criteria:
1323
+ self._having_criteria = tuple(
1324
+ current_adapter(crit, True) if current_adapter else crit
1325
+ for crit in query._having_criteria
1326
+ )
1327
+
1328
+ self.group_by = (
1329
+ self._adapt_col_list(
1330
+ util.flatten_iterator(query._group_by_clauses), current_adapter
1331
+ )
1332
+ if current_adapter and query._group_by_clauses not in (None, False)
1333
+ else query._group_by_clauses or None
1334
+ )
1335
+
1336
+ if self.eager_order_by:
1337
+ adapter = self.from_clauses[0]._target_adapter
1338
+ self.eager_order_by = adapter.copy_and_process(self.eager_order_by)
1339
+
1340
+ if query._distinct_on:
1341
+ self.distinct_on = self._adapt_col_list(
1342
+ query._distinct_on, current_adapter
1343
+ )
1344
+ else:
1345
+ self.distinct_on = ()
1346
+
1347
+ self.distinct = query._distinct
1348
+
1349
+ self.syntax_extensions = {
1350
+ key: current_adapter(value, True) if current_adapter else value
1351
+ for key, value in query._get_syntax_extensions_as_dict().items()
1352
+ }
1353
+
1354
+ if query._correlate:
1355
+ # ORM mapped entities that are mapped to joins can be passed
1356
+ # to .correlate, so here they are broken into their component
1357
+ # tables.
1358
+ self.correlate = tuple(
1359
+ util.flatten_iterator(
1360
+ sql_util.surface_selectables(s) if s is not None else None
1361
+ for s in query._correlate
1362
+ )
1363
+ )
1364
+ elif query._correlate_except is not None:
1365
+ self.correlate_except = tuple(
1366
+ util.flatten_iterator(
1367
+ sql_util.surface_selectables(s) if s is not None else None
1368
+ for s in query._correlate_except
1369
+ )
1370
+ )
1371
+ elif not query._auto_correlate:
1372
+ self.correlate = (None,)
1373
+
1374
+ # PART II
1375
+
1376
+ self._for_update_arg = query._for_update_arg
1377
+
1378
+ if self.compile_options._is_star and (len(self._entities) != 1):
1379
+ raise sa_exc.CompileError(
1380
+ "Can't generate ORM query that includes multiple expressions "
1381
+ "at the same time as '*'; query for '*' alone if present"
1382
+ )
1383
+ for entity in self._entities:
1384
+ entity.setup_compile_state(self)
1385
+
1386
+ for rec in self.create_eager_joins:
1387
+ strategy = rec[0]
1388
+ strategy(self, *rec[1:])
1389
+
1390
+ # else "load from discrete FROMs" mode,
1391
+ # i.e. when each _MappedEntity has its own FROM
1392
+
1393
+ if self.compile_options._enable_single_crit:
1394
+ self._adjust_for_extra_criteria()
1395
+
1396
+ if not self.primary_columns:
1397
+ if self.compile_options._only_load_props:
1398
+ assert False, "no columns were included in _only_load_props"
1399
+
1400
+ raise sa_exc.InvalidRequestError(
1401
+ "Query contains no columns with which to SELECT from."
1402
+ )
1403
+
1404
+ if not self.from_clauses:
1405
+ self.from_clauses = list(self._fallback_from_clauses)
1406
+
1407
+ if self.order_by is False:
1408
+ self.order_by = None
1409
+
1410
+ if self._should_nest_selectable:
1411
+ self.statement = self._compound_eager_statement()
1412
+ else:
1413
+ self.statement = self._simple_statement()
1414
+
1415
+ if self.for_statement:
1416
+ ezero = self._mapper_zero()
1417
+ if ezero is not None:
1418
+ # TODO: this goes away once we get rid of the deep entity
1419
+ # thing
1420
+ self.statement = self.statement._annotate(
1421
+ {"deepentity": ezero}
1422
+ )
1423
+
1424
+ @classmethod
1425
+ def _create_entities_collection(cls, query, legacy):
1426
+ """Creates a partial ORMSelectCompileState that includes
1427
+ the full collection of _MapperEntity and other _QueryEntity objects.
1428
+
1429
+ Supports a few remaining use cases that are pre-compilation
1430
+ but still need to gather some of the column / adaption information.
1431
+
1432
+ """
1433
+ self = cls.__new__(cls)
1434
+
1435
+ self._entities = []
1436
+ self._primary_entity = None
1437
+ self._polymorphic_adapters = {}
1438
+
1439
+ self._label_convention = self._column_naming_convention(
1440
+ query._label_style, legacy
1441
+ )
1442
+
1443
+ # entities will also set up polymorphic adapters for mappers
1444
+ # that have with_polymorphic configured
1445
+ _QueryEntity.to_compile_state(
1446
+ self, query._raw_columns, self._entities, is_current_entities=True
1447
+ )
1448
+ return self
1449
+
1450
+ @classmethod
1451
+ def _get_filter_by_entities(cls, statement):
1452
+ """Return all ORM entities for filter_by() searches.
1453
+
1454
+ the ORM version for Select is special vs. update/delete since it needs
1455
+ to navigate along select.join() paths which have ORM specific
1456
+ directives.
1457
+
1458
+ beyond that, it delivers other entities as the Mapper or Aliased
1459
+ object rather than the Table or Alias, which mostly affects
1460
+ how error messages regarding ambiguous entities or entity not
1461
+ found are rendered; class-specific attributes like hybrid,
1462
+ column_property() etc. work either way since
1463
+ _entity_namespace_key_search_all() uses _entity_namespace().
1464
+
1465
+ DML Update and Delete objects, even though they also have filter_by()
1466
+ and also accept ORM objects, don't use this routine since they
1467
+ typically just have a single table, and if they have multiple tables
1468
+ it's only via WHERE clause, which interestingly do not maintain ORM
1469
+ annotations when used (that is, (User.name ==
1470
+ 'foo').left.table._annotations is empty; the ORMness of User.name is
1471
+ lost in the expression construction process, since we don't annotate
1472
+ (copy) Column objects with ORM entities the way we do for Table.
1473
+
1474
+ .. versionadded:: 2.1
1475
+ """
1476
+
1477
+ def _setup_join_targets(collection):
1478
+ for (target, *_) in collection:
1479
+ if isinstance(target, attributes.QueryableAttribute):
1480
+ yield target.entity
1481
+ elif "_no_filter_by" not in target._annotations:
1482
+ yield target
1483
+
1484
+ entities = set(_setup_join_targets(statement._setup_joins))
1485
+
1486
+ for memoized in statement._memoized_select_entities:
1487
+ entities.update(_setup_join_targets(memoized._setup_joins))
1488
+
1489
+ entities.update(
1490
+ (
1491
+ from_obj._annotations["parententity"]
1492
+ if "parententity" in from_obj._annotations
1493
+ else from_obj
1494
+ )
1495
+ for from_obj in statement._from_obj
1496
+ if "_no_filter_by" not in from_obj._annotations
1497
+ )
1498
+
1499
+ for element in statement._raw_columns:
1500
+ if "entity_namespace" in element._annotations:
1501
+ ens = element._annotations["entity_namespace"]
1502
+ entities.add(ens)
1503
+ elif "_no_filter_by" not in element._annotations:
1504
+ entities.update(element._from_objects)
1505
+
1506
+ return entities
1507
+
1508
+ @classmethod
1509
+ def all_selected_columns(cls, statement):
1510
+ for element in statement._raw_columns:
1511
+ if (
1512
+ element.is_selectable
1513
+ and "entity_namespace" in element._annotations
1514
+ ):
1515
+ ens = element._annotations["entity_namespace"]
1516
+ if not ens.is_mapper and not ens.is_aliased_class:
1517
+ yield from _select_iterables([element])
1518
+ else:
1519
+ yield from _select_iterables(ens._all_column_expressions)
1520
+ else:
1521
+ yield from _select_iterables([element])
1522
+
1523
+ @classmethod
1524
+ def get_columns_clause_froms(cls, statement):
1525
+ return cls._normalize_froms(
1526
+ itertools.chain.from_iterable(
1527
+ (
1528
+ element._from_objects
1529
+ if "parententity" not in element._annotations
1530
+ else [
1531
+ element._annotations[
1532
+ "parententity"
1533
+ ].__clause_element__()
1534
+ ]
1535
+ )
1536
+ for element in statement._raw_columns
1537
+ )
1538
+ )
1539
+
1540
+ @classmethod
1541
+ def from_statement(cls, statement, from_statement):
1542
+ from_statement = coercions.expect(
1543
+ roles.ReturnsRowsRole,
1544
+ from_statement,
1545
+ apply_propagate_attrs=statement,
1546
+ )
1547
+
1548
+ stmt = FromStatement(statement._raw_columns, from_statement)
1549
+
1550
+ stmt.__dict__.update(
1551
+ _with_options=statement._with_options,
1552
+ _compile_state_funcs=statement._compile_state_funcs,
1553
+ _execution_options=statement._execution_options,
1554
+ _propagate_attrs=statement._propagate_attrs,
1555
+ )
1556
+ return stmt
1557
+
1558
+ def _set_select_from_alias(self):
1559
+ """used only for legacy Query cases"""
1560
+
1561
+ query = self.select_statement # query
1562
+
1563
+ assert self.compile_options._set_base_alias
1564
+ assert len(query._from_obj) == 1
1565
+
1566
+ adapter = self._get_select_from_alias_from_obj(query._from_obj[0])
1567
+ if adapter:
1568
+ self.compile_options += {"_enable_single_crit": False}
1569
+ self._from_obj_alias = adapter
1570
+
1571
+ def _get_select_from_alias_from_obj(self, from_obj):
1572
+ """used only for legacy Query cases"""
1573
+
1574
+ info = from_obj
1575
+
1576
+ if "parententity" in info._annotations:
1577
+ info = info._annotations["parententity"]
1578
+
1579
+ if hasattr(info, "mapper"):
1580
+ if not info.is_aliased_class:
1581
+ raise sa_exc.ArgumentError(
1582
+ "A selectable (FromClause) instance is "
1583
+ "expected when the base alias is being set."
1584
+ )
1585
+ else:
1586
+ return info._adapter
1587
+
1588
+ elif isinstance(info.selectable, sql.selectable.AliasedReturnsRows):
1589
+ equivs = self._all_equivs()
1590
+ assert info is info.selectable
1591
+ return ORMStatementAdapter(
1592
+ _TraceAdaptRole.LEGACY_SELECT_FROM_ALIAS,
1593
+ info.selectable,
1594
+ equivalents=equivs,
1595
+ )
1596
+ else:
1597
+ return None
1598
+
1599
+ def _mapper_zero(self):
1600
+ """return the Mapper associated with the first QueryEntity."""
1601
+ return self._entities[0].mapper
1602
+
1603
+ def _entity_zero(self):
1604
+ """Return the 'entity' (mapper or AliasedClass) associated
1605
+ with the first QueryEntity, or alternatively the 'select from'
1606
+ entity if specified."""
1607
+
1608
+ for ent in self.from_clauses:
1609
+ if "parententity" in ent._annotations:
1610
+ return ent._annotations["parententity"]
1611
+ for qent in self._entities:
1612
+ if qent.entity_zero:
1613
+ return qent.entity_zero
1614
+
1615
+ return None
1616
+
1617
+ def _only_full_mapper_zero(self, methname):
1618
+ if self._entities != [self._primary_entity]:
1619
+ raise sa_exc.InvalidRequestError(
1620
+ "%s() can only be used against "
1621
+ "a single mapped class." % methname
1622
+ )
1623
+ return self._primary_entity.entity_zero
1624
+
1625
+ def _only_entity_zero(self, rationale=None):
1626
+ if len(self._entities) > 1:
1627
+ raise sa_exc.InvalidRequestError(
1628
+ rationale
1629
+ or "This operation requires a Query "
1630
+ "against a single mapper."
1631
+ )
1632
+ return self._entity_zero()
1633
+
1634
+ def _all_equivs(self):
1635
+ equivs = {}
1636
+
1637
+ for memoized_entities in self._memoized_entities.values():
1638
+ for ent in [
1639
+ ent
1640
+ for ent in memoized_entities
1641
+ if isinstance(ent, _MapperEntity)
1642
+ ]:
1643
+ equivs.update(ent.mapper._equivalent_columns)
1644
+
1645
+ for ent in [
1646
+ ent for ent in self._entities if isinstance(ent, _MapperEntity)
1647
+ ]:
1648
+ equivs.update(ent.mapper._equivalent_columns)
1649
+ return equivs
1650
+
1651
+ def _compound_eager_statement(self):
1652
+ # for eager joins present and LIMIT/OFFSET/DISTINCT,
1653
+ # wrap the query inside a select,
1654
+ # then append eager joins onto that
1655
+
1656
+ if self.order_by:
1657
+ # the default coercion for ORDER BY is now the OrderByRole,
1658
+ # which adds an additional post coercion to ByOfRole in that
1659
+ # elements are converted into label references. For the
1660
+ # eager load / subquery wrapping case, we need to un-coerce
1661
+ # the original expressions outside of the label references
1662
+ # in order to have them render.
1663
+ unwrapped_order_by = [
1664
+ (
1665
+ elem.element
1666
+ if isinstance(elem, sql.elements._label_reference)
1667
+ else elem
1668
+ )
1669
+ for elem in self.order_by
1670
+ ]
1671
+
1672
+ order_by_col_expr = sql_util.expand_column_list_from_order_by(
1673
+ self.primary_columns, unwrapped_order_by
1674
+ )
1675
+ else:
1676
+ order_by_col_expr = []
1677
+ unwrapped_order_by = None
1678
+
1679
+ # put FOR UPDATE on the inner query, where MySQL will honor it,
1680
+ # as well as if it has an OF so PostgreSQL can use it.
1681
+ inner = self._select_statement(
1682
+ self.primary_columns
1683
+ + [c for c in order_by_col_expr if c not in self.dedupe_columns],
1684
+ self.from_clauses,
1685
+ self._where_criteria,
1686
+ self._having_criteria,
1687
+ self.label_style,
1688
+ self.order_by,
1689
+ for_update=self._for_update_arg,
1690
+ hints=self.select_statement._hints,
1691
+ statement_hints=self.select_statement._statement_hints,
1692
+ correlate=self.correlate,
1693
+ correlate_except=self.correlate_except,
1694
+ **self._select_args,
1695
+ )
1696
+
1697
+ inner = inner.alias()
1698
+
1699
+ equivs = self._all_equivs()
1700
+
1701
+ self.compound_eager_adapter = ORMStatementAdapter(
1702
+ _TraceAdaptRole.COMPOUND_EAGER_STATEMENT, inner, equivalents=equivs
1703
+ )
1704
+
1705
+ statement = future.select(
1706
+ *([inner] + self.secondary_columns) # use_labels=self.labels
1707
+ )
1708
+ statement._label_style = self.label_style
1709
+
1710
+ # Oracle Database however does not allow FOR UPDATE on the subquery,
1711
+ # and the Oracle Database dialects ignore it, plus for PostgreSQL,
1712
+ # MySQL we expect that all elements of the row are locked, so also put
1713
+ # it on the outside (except in the case of PG when OF is used)
1714
+ if (
1715
+ self._for_update_arg is not None
1716
+ and self._for_update_arg.of is None
1717
+ ):
1718
+ statement._for_update_arg = self._for_update_arg
1719
+
1720
+ from_clause = inner
1721
+ for eager_join in self.eager_joins.values():
1722
+ # EagerLoader places a 'stop_on' attribute on the join,
1723
+ # giving us a marker as to where the "splice point" of
1724
+ # the join should be
1725
+ from_clause = sql_util.splice_joins(
1726
+ from_clause, eager_join, eager_join.stop_on
1727
+ )
1728
+
1729
+ statement.select_from.non_generative(statement, from_clause)
1730
+
1731
+ if unwrapped_order_by:
1732
+ statement.order_by.non_generative(
1733
+ statement,
1734
+ *self.compound_eager_adapter.copy_and_process(
1735
+ unwrapped_order_by
1736
+ ),
1737
+ )
1738
+
1739
+ statement.order_by.non_generative(statement, *self.eager_order_by)
1740
+ return statement
1741
+
1742
+ def _simple_statement(self):
1743
+ statement = self._select_statement(
1744
+ self.primary_columns + self.secondary_columns,
1745
+ tuple(self.from_clauses) + tuple(self.eager_joins.values()),
1746
+ self._where_criteria,
1747
+ self._having_criteria,
1748
+ self.label_style,
1749
+ self.order_by,
1750
+ for_update=self._for_update_arg,
1751
+ hints=self.select_statement._hints,
1752
+ statement_hints=self.select_statement._statement_hints,
1753
+ correlate=self.correlate,
1754
+ correlate_except=self.correlate_except,
1755
+ **self._select_args,
1756
+ )
1757
+
1758
+ if self.eager_order_by:
1759
+ statement.order_by.non_generative(statement, *self.eager_order_by)
1760
+ return statement
1761
+
1762
+ def _select_statement(
1763
+ self,
1764
+ raw_columns,
1765
+ from_obj,
1766
+ where_criteria,
1767
+ having_criteria,
1768
+ label_style,
1769
+ order_by,
1770
+ for_update,
1771
+ hints,
1772
+ statement_hints,
1773
+ correlate,
1774
+ correlate_except,
1775
+ limit_clause,
1776
+ offset_clause,
1777
+ fetch_clause,
1778
+ fetch_clause_options,
1779
+ distinct,
1780
+ distinct_on,
1781
+ prefixes,
1782
+ suffixes,
1783
+ group_by,
1784
+ independent_ctes,
1785
+ independent_ctes_opts,
1786
+ syntax_extensions,
1787
+ ):
1788
+ statement = Select._create_raw_select(
1789
+ _raw_columns=raw_columns,
1790
+ _from_obj=from_obj,
1791
+ _label_style=label_style,
1792
+ )
1793
+
1794
+ if where_criteria:
1795
+ statement._where_criteria = where_criteria
1796
+ if having_criteria:
1797
+ statement._having_criteria = having_criteria
1798
+
1799
+ if order_by:
1800
+ statement._order_by_clauses += tuple(order_by)
1801
+
1802
+ if distinct_on:
1803
+ statement._distinct = True
1804
+ statement._distinct_on = distinct_on
1805
+ elif distinct:
1806
+ statement._distinct = True
1807
+
1808
+ if group_by:
1809
+ statement._group_by_clauses += tuple(group_by)
1810
+
1811
+ statement._limit_clause = limit_clause
1812
+ statement._offset_clause = offset_clause
1813
+ statement._fetch_clause = fetch_clause
1814
+ statement._fetch_clause_options = fetch_clause_options
1815
+ statement._independent_ctes = independent_ctes
1816
+ statement._independent_ctes_opts = independent_ctes_opts
1817
+ if syntax_extensions:
1818
+ statement._set_syntax_extensions(**syntax_extensions)
1819
+
1820
+ if prefixes:
1821
+ statement._prefixes = prefixes
1822
+
1823
+ if suffixes:
1824
+ statement._suffixes = suffixes
1825
+
1826
+ statement._for_update_arg = for_update
1827
+
1828
+ if hints:
1829
+ statement._hints = hints
1830
+ if statement_hints:
1831
+ statement._statement_hints = statement_hints
1832
+
1833
+ if correlate:
1834
+ statement.correlate.non_generative(statement, *correlate)
1835
+
1836
+ if correlate_except is not None:
1837
+ statement.correlate_except.non_generative(
1838
+ statement, *correlate_except
1839
+ )
1840
+
1841
+ return statement
1842
+
1843
+ def _adapt_polymorphic_element(self, element):
1844
+ if "parententity" in element._annotations:
1845
+ search = element._annotations["parententity"]
1846
+ alias = self._polymorphic_adapters.get(search, None)
1847
+ if alias:
1848
+ return alias.adapt_clause(element)
1849
+
1850
+ if isinstance(element, expression.FromClause):
1851
+ search = element
1852
+ elif hasattr(element, "table"):
1853
+ search = element.table
1854
+ else:
1855
+ return None
1856
+
1857
+ alias = self._polymorphic_adapters.get(search, None)
1858
+ if alias:
1859
+ return alias.adapt_clause(element)
1860
+
1861
+ def _adapt_col_list(self, cols, current_adapter):
1862
+ if current_adapter:
1863
+ return [current_adapter(o, True) for o in cols]
1864
+ else:
1865
+ return cols
1866
+
1867
+ def _get_current_adapter(self):
1868
+ adapters = []
1869
+
1870
+ if self._from_obj_alias:
1871
+ # used for legacy going forward for query set_ops, e.g.
1872
+ # union(), union_all(), etc.
1873
+ # 1.4 and previously, also used for from_self(),
1874
+ # select_entity_from()
1875
+ #
1876
+ # for the "from obj" alias, apply extra rule to the
1877
+ # 'ORM only' check, if this query were generated from a
1878
+ # subquery of itself, i.e. _from_selectable(), apply adaption
1879
+ # to all SQL constructs.
1880
+ adapters.append(
1881
+ self._from_obj_alias.replace,
1882
+ )
1883
+
1884
+ # this was *hopefully* the only adapter we were going to need
1885
+ # going forward...however, we unfortunately need _from_obj_alias
1886
+ # for query.union(), which we can't drop
1887
+ if self._polymorphic_adapters:
1888
+ adapters.append(self._adapt_polymorphic_element)
1889
+
1890
+ if not adapters:
1891
+ return None
1892
+
1893
+ def _adapt_clause(clause, as_filter):
1894
+ # do we adapt all expression elements or only those
1895
+ # tagged as 'ORM' constructs ?
1896
+
1897
+ def replace(elem):
1898
+ for adapter in adapters:
1899
+ e = adapter(elem)
1900
+ if e is not None:
1901
+ return e
1902
+
1903
+ return visitors.replacement_traverse(clause, {}, replace)
1904
+
1905
+ return _adapt_clause
1906
+
1907
+ def _join(self, args, entities_collection):
1908
+ for right, onclause, from_, flags in args:
1909
+ isouter = flags["isouter"]
1910
+ full = flags["full"]
1911
+
1912
+ right = inspect(right)
1913
+ if onclause is not None:
1914
+ onclause = inspect(onclause)
1915
+
1916
+ if isinstance(right, interfaces.PropComparator):
1917
+ if onclause is not None:
1918
+ raise sa_exc.InvalidRequestError(
1919
+ "No 'on clause' argument may be passed when joining "
1920
+ "to a relationship path as a target"
1921
+ )
1922
+
1923
+ onclause = right
1924
+ right = None
1925
+ elif "parententity" in right._annotations:
1926
+ right = right._annotations["parententity"]
1927
+
1928
+ if onclause is None:
1929
+ if not right.is_selectable and not hasattr(right, "mapper"):
1930
+ raise sa_exc.ArgumentError(
1931
+ "Expected mapped entity or "
1932
+ "selectable/table as join target"
1933
+ )
1934
+
1935
+ if isinstance(onclause, interfaces.PropComparator):
1936
+ # descriptor/property given (or determined); this tells us
1937
+ # explicitly what the expected "left" side of the join is.
1938
+
1939
+ of_type = getattr(onclause, "_of_type", None)
1940
+
1941
+ if right is None:
1942
+ if of_type:
1943
+ right = of_type
1944
+ else:
1945
+ right = onclause.property
1946
+
1947
+ try:
1948
+ right = right.entity
1949
+ except AttributeError as err:
1950
+ raise sa_exc.ArgumentError(
1951
+ "Join target %s does not refer to a "
1952
+ "mapped entity" % right
1953
+ ) from err
1954
+
1955
+ left = onclause._parententity
1956
+
1957
+ prop = onclause.property
1958
+ if not isinstance(onclause, attributes.QueryableAttribute):
1959
+ onclause = prop
1960
+
1961
+ # check for this path already present. don't render in that
1962
+ # case.
1963
+ if (left, right, prop.key) in self._already_joined_edges:
1964
+ continue
1965
+
1966
+ if from_ is not None:
1967
+ if (
1968
+ from_ is not left
1969
+ and from_._annotations.get("parententity", None)
1970
+ is not left
1971
+ ):
1972
+ raise sa_exc.InvalidRequestError(
1973
+ "explicit from clause %s does not match left side "
1974
+ "of relationship attribute %s"
1975
+ % (
1976
+ from_._annotations.get("parententity", from_),
1977
+ onclause,
1978
+ )
1979
+ )
1980
+ elif from_ is not None:
1981
+ prop = None
1982
+ left = from_
1983
+ else:
1984
+ # no descriptor/property given; we will need to figure out
1985
+ # what the effective "left" side is
1986
+ prop = left = None
1987
+
1988
+ # figure out the final "left" and "right" sides and create an
1989
+ # ORMJoin to add to our _from_obj tuple
1990
+ self._join_left_to_right(
1991
+ entities_collection,
1992
+ left,
1993
+ right,
1994
+ onclause,
1995
+ prop,
1996
+ isouter,
1997
+ full,
1998
+ )
1999
+
2000
+ def _join_left_to_right(
2001
+ self,
2002
+ entities_collection,
2003
+ left,
2004
+ right,
2005
+ onclause,
2006
+ prop,
2007
+ outerjoin,
2008
+ full,
2009
+ ):
2010
+ """given raw "left", "right", "onclause" parameters consumed from
2011
+ a particular key within _join(), add a real ORMJoin object to
2012
+ our _from_obj list (or augment an existing one)
2013
+
2014
+ """
2015
+
2016
+ explicit_left = left
2017
+ if left is None:
2018
+ # left not given (e.g. no relationship object/name specified)
2019
+ # figure out the best "left" side based on our existing froms /
2020
+ # entities
2021
+ assert prop is None
2022
+ (
2023
+ left,
2024
+ replace_from_obj_index,
2025
+ use_entity_index,
2026
+ ) = self._join_determine_implicit_left_side(
2027
+ entities_collection, left, right, onclause
2028
+ )
2029
+ else:
2030
+ # left is given via a relationship/name, or as explicit left side.
2031
+ # Determine where in our
2032
+ # "froms" list it should be spliced/appended as well as what
2033
+ # existing entity it corresponds to.
2034
+ (
2035
+ replace_from_obj_index,
2036
+ use_entity_index,
2037
+ ) = self._join_place_explicit_left_side(entities_collection, left)
2038
+
2039
+ if left is right:
2040
+ raise sa_exc.InvalidRequestError(
2041
+ "Can't construct a join from %s to %s, they "
2042
+ "are the same entity" % (left, right)
2043
+ )
2044
+
2045
+ # the right side as given often needs to be adapted. additionally
2046
+ # a lot of things can be wrong with it. handle all that and
2047
+ # get back the new effective "right" side
2048
+ r_info, right, onclause = self._join_check_and_adapt_right_side(
2049
+ left, right, onclause, prop
2050
+ )
2051
+
2052
+ if not r_info.is_selectable:
2053
+ extra_criteria = self._get_extra_criteria(r_info)
2054
+ else:
2055
+ extra_criteria = ()
2056
+
2057
+ if replace_from_obj_index is not None:
2058
+ # splice into an existing element in the
2059
+ # self._from_obj list
2060
+ left_clause = self.from_clauses[replace_from_obj_index]
2061
+
2062
+ if explicit_left is not None and onclause is None:
2063
+ onclause = _ORMJoin._join_condition(explicit_left, right)
2064
+
2065
+ self.from_clauses = (
2066
+ self.from_clauses[:replace_from_obj_index]
2067
+ + [
2068
+ _ORMJoin(
2069
+ left_clause,
2070
+ right,
2071
+ onclause,
2072
+ isouter=outerjoin,
2073
+ full=full,
2074
+ _extra_criteria=extra_criteria,
2075
+ )
2076
+ ]
2077
+ + self.from_clauses[replace_from_obj_index + 1 :]
2078
+ )
2079
+ else:
2080
+ # add a new element to the self._from_obj list
2081
+ if use_entity_index is not None:
2082
+ # make use of _MapperEntity selectable, which is usually
2083
+ # entity_zero.selectable, but if with_polymorphic() were used
2084
+ # might be distinct
2085
+ assert isinstance(
2086
+ entities_collection[use_entity_index], _MapperEntity
2087
+ )
2088
+ left_clause = entities_collection[use_entity_index].selectable
2089
+ else:
2090
+ left_clause = left
2091
+
2092
+ self.from_clauses = self.from_clauses + [
2093
+ _ORMJoin(
2094
+ left_clause,
2095
+ r_info,
2096
+ onclause,
2097
+ isouter=outerjoin,
2098
+ full=full,
2099
+ _extra_criteria=extra_criteria,
2100
+ )
2101
+ ]
2102
+
2103
+ def _join_determine_implicit_left_side(
2104
+ self, entities_collection, left, right, onclause
2105
+ ):
2106
+ """When join conditions don't express the left side explicitly,
2107
+ determine if an existing FROM or entity in this query
2108
+ can serve as the left hand side.
2109
+
2110
+ """
2111
+
2112
+ # when we are here, it means join() was called without an ORM-
2113
+ # specific way of telling us what the "left" side is, e.g.:
2114
+ #
2115
+ # join(RightEntity)
2116
+ #
2117
+ # or
2118
+ #
2119
+ # join(RightEntity, RightEntity.foo == LeftEntity.bar)
2120
+ #
2121
+
2122
+ r_info = inspect(right)
2123
+
2124
+ replace_from_obj_index = use_entity_index = None
2125
+
2126
+ if self.from_clauses:
2127
+ # we have a list of FROMs already. So by definition this
2128
+ # join has to connect to one of those FROMs.
2129
+
2130
+ indexes = sql_util.find_left_clause_to_join_from(
2131
+ self.from_clauses, r_info.selectable, onclause
2132
+ )
2133
+
2134
+ if len(indexes) == 1:
2135
+ replace_from_obj_index = indexes[0]
2136
+ left = self.from_clauses[replace_from_obj_index]
2137
+ elif len(indexes) > 1:
2138
+ raise sa_exc.InvalidRequestError(
2139
+ "Can't determine which FROM clause to join "
2140
+ "from, there are multiple FROMS which can "
2141
+ "join to this entity. Please use the .select_from() "
2142
+ "method to establish an explicit left side, as well as "
2143
+ "providing an explicit ON clause if not present already "
2144
+ "to help resolve the ambiguity."
2145
+ )
2146
+ else:
2147
+ raise sa_exc.InvalidRequestError(
2148
+ "Don't know how to join to %r. "
2149
+ "Please use the .select_from() "
2150
+ "method to establish an explicit left side, as well as "
2151
+ "providing an explicit ON clause if not present already "
2152
+ "to help resolve the ambiguity." % (right,)
2153
+ )
2154
+
2155
+ elif entities_collection:
2156
+ # we have no explicit FROMs, so the implicit left has to
2157
+ # come from our list of entities.
2158
+
2159
+ potential = {}
2160
+ for entity_index, ent in enumerate(entities_collection):
2161
+ entity = ent.entity_zero_or_selectable
2162
+ if entity is None:
2163
+ continue
2164
+ ent_info = inspect(entity)
2165
+ if ent_info is r_info: # left and right are the same, skip
2166
+ continue
2167
+
2168
+ # by using a dictionary with the selectables as keys this
2169
+ # de-duplicates those selectables as occurs when the query is
2170
+ # against a series of columns from the same selectable
2171
+ if isinstance(ent, _MapperEntity):
2172
+ potential[ent.selectable] = (entity_index, entity)
2173
+ else:
2174
+ potential[ent_info.selectable] = (None, entity)
2175
+
2176
+ all_clauses = list(potential.keys())
2177
+ indexes = sql_util.find_left_clause_to_join_from(
2178
+ all_clauses, r_info.selectable, onclause
2179
+ )
2180
+
2181
+ if len(indexes) == 1:
2182
+ use_entity_index, left = potential[all_clauses[indexes[0]]]
2183
+ elif len(indexes) > 1:
2184
+ raise sa_exc.InvalidRequestError(
2185
+ "Can't determine which FROM clause to join "
2186
+ "from, there are multiple FROMS which can "
2187
+ "join to this entity. Please use the .select_from() "
2188
+ "method to establish an explicit left side, as well as "
2189
+ "providing an explicit ON clause if not present already "
2190
+ "to help resolve the ambiguity."
2191
+ )
2192
+ else:
2193
+ raise sa_exc.InvalidRequestError(
2194
+ "Don't know how to join to %r. "
2195
+ "Please use the .select_from() "
2196
+ "method to establish an explicit left side, as well as "
2197
+ "providing an explicit ON clause if not present already "
2198
+ "to help resolve the ambiguity." % (right,)
2199
+ )
2200
+ else:
2201
+ raise sa_exc.InvalidRequestError(
2202
+ "No entities to join from; please use "
2203
+ "select_from() to establish the left "
2204
+ "entity/selectable of this join"
2205
+ )
2206
+
2207
+ return left, replace_from_obj_index, use_entity_index
2208
+
2209
+ def _join_place_explicit_left_side(self, entities_collection, left):
2210
+ """When join conditions express a left side explicitly, determine
2211
+ where in our existing list of FROM clauses we should join towards,
2212
+ or if we need to make a new join, and if so is it from one of our
2213
+ existing entities.
2214
+
2215
+ """
2216
+
2217
+ # when we are here, it means join() was called with an indicator
2218
+ # as to an exact left side, which means a path to a
2219
+ # Relationship was given, e.g.:
2220
+ #
2221
+ # join(RightEntity, LeftEntity.right)
2222
+ #
2223
+ # or
2224
+ #
2225
+ # join(LeftEntity.right)
2226
+ #
2227
+ # as well as string forms:
2228
+ #
2229
+ # join(RightEntity, "right")
2230
+ #
2231
+ # etc.
2232
+ #
2233
+
2234
+ replace_from_obj_index = use_entity_index = None
2235
+
2236
+ l_info = inspect(left)
2237
+ if self.from_clauses:
2238
+ indexes = sql_util.find_left_clause_that_matches_given(
2239
+ self.from_clauses, l_info.selectable
2240
+ )
2241
+
2242
+ if len(indexes) > 1:
2243
+ raise sa_exc.InvalidRequestError(
2244
+ "Can't identify which entity in which to assign the "
2245
+ "left side of this join. Please use a more specific "
2246
+ "ON clause."
2247
+ )
2248
+
2249
+ # have an index, means the left side is already present in
2250
+ # an existing FROM in the self._from_obj tuple
2251
+ if indexes:
2252
+ replace_from_obj_index = indexes[0]
2253
+
2254
+ # no index, means we need to add a new element to the
2255
+ # self._from_obj tuple
2256
+
2257
+ # no from element present, so we will have to add to the
2258
+ # self._from_obj tuple. Determine if this left side matches up
2259
+ # with existing mapper entities, in which case we want to apply the
2260
+ # aliasing / adaptation rules present on that entity if any
2261
+ if (
2262
+ replace_from_obj_index is None
2263
+ and entities_collection
2264
+ and hasattr(l_info, "mapper")
2265
+ ):
2266
+ for idx, ent in enumerate(entities_collection):
2267
+ # TODO: should we be checking for multiple mapper entities
2268
+ # matching?
2269
+ if isinstance(ent, _MapperEntity) and ent.corresponds_to(left):
2270
+ use_entity_index = idx
2271
+ break
2272
+
2273
+ return replace_from_obj_index, use_entity_index
2274
+
2275
+ def _join_check_and_adapt_right_side(self, left, right, onclause, prop):
2276
+ """transform the "right" side of the join as well as the onclause
2277
+ according to polymorphic mapping translations, aliasing on the query
2278
+ or on the join, special cases where the right and left side have
2279
+ overlapping tables.
2280
+
2281
+ """
2282
+
2283
+ l_info = inspect(left)
2284
+ r_info = inspect(right)
2285
+
2286
+ overlap = False
2287
+
2288
+ right_mapper = getattr(r_info, "mapper", None)
2289
+ # if the target is a joined inheritance mapping,
2290
+ # be more liberal about auto-aliasing.
2291
+ if right_mapper and (
2292
+ right_mapper.with_polymorphic
2293
+ or isinstance(right_mapper.persist_selectable, expression.Join)
2294
+ ):
2295
+ for from_obj in self.from_clauses or [l_info.selectable]:
2296
+ if sql_util.selectables_overlap(
2297
+ l_info.selectable, from_obj
2298
+ ) and sql_util.selectables_overlap(
2299
+ from_obj, r_info.selectable
2300
+ ):
2301
+ overlap = True
2302
+ break
2303
+
2304
+ if overlap and l_info.selectable is r_info.selectable:
2305
+ raise sa_exc.InvalidRequestError(
2306
+ "Can't join table/selectable '%s' to itself"
2307
+ % l_info.selectable
2308
+ )
2309
+
2310
+ right_mapper, right_selectable, right_is_aliased = (
2311
+ getattr(r_info, "mapper", None),
2312
+ r_info.selectable,
2313
+ getattr(r_info, "is_aliased_class", False),
2314
+ )
2315
+
2316
+ if (
2317
+ right_mapper
2318
+ and prop
2319
+ and not right_mapper.common_parent(prop.mapper)
2320
+ ):
2321
+ raise sa_exc.InvalidRequestError(
2322
+ "Join target %s does not correspond to "
2323
+ "the right side of join condition %s" % (right, onclause)
2324
+ )
2325
+
2326
+ # _join_entities is used as a hint for single-table inheritance
2327
+ # purposes at the moment
2328
+ if hasattr(r_info, "mapper"):
2329
+ self._join_entities += (r_info,)
2330
+
2331
+ need_adapter = False
2332
+
2333
+ # test for joining to an unmapped selectable as the target
2334
+ if r_info.is_clause_element:
2335
+ if prop:
2336
+ right_mapper = prop.mapper
2337
+
2338
+ if right_selectable._is_lateral:
2339
+ # orm_only is disabled to suit the case where we have to
2340
+ # adapt an explicit correlate(Entity) - the select() loses
2341
+ # the ORM-ness in this case right now, ideally it would not
2342
+ current_adapter = self._get_current_adapter()
2343
+ if current_adapter is not None:
2344
+ # TODO: we had orm_only=False here before, removing
2345
+ # it didn't break things. if we identify the rationale,
2346
+ # may need to apply "_orm_only" annotation here.
2347
+ right = current_adapter(right, True)
2348
+
2349
+ elif prop:
2350
+ # joining to selectable with a mapper property given
2351
+ # as the ON clause
2352
+
2353
+ if not right_selectable.is_derived_from(
2354
+ right_mapper.persist_selectable
2355
+ ):
2356
+ raise sa_exc.InvalidRequestError(
2357
+ "Selectable '%s' is not derived from '%s'"
2358
+ % (
2359
+ right_selectable.description,
2360
+ right_mapper.persist_selectable.description,
2361
+ )
2362
+ )
2363
+
2364
+ # if the destination selectable is a plain select(),
2365
+ # turn it into an alias().
2366
+ if isinstance(right_selectable, expression.SelectBase):
2367
+ right_selectable = coercions.expect(
2368
+ roles.FromClauseRole, right_selectable
2369
+ )
2370
+ need_adapter = True
2371
+
2372
+ # make the right hand side target into an ORM entity
2373
+ right = AliasedClass(right_mapper, right_selectable)
2374
+
2375
+ util.warn_deprecated(
2376
+ "An alias is being generated automatically against "
2377
+ "joined entity %s for raw clauseelement, which is "
2378
+ "deprecated and will be removed in a later release. "
2379
+ "Use the aliased() "
2380
+ "construct explicitly, see the linked example."
2381
+ % right_mapper,
2382
+ "1.4",
2383
+ code="xaj1",
2384
+ )
2385
+
2386
+ # test for overlap:
2387
+ # orm/inheritance/relationships.py
2388
+ # SelfReferentialM2MTest
2389
+ aliased_entity = right_mapper and not right_is_aliased and overlap
2390
+
2391
+ if not need_adapter and aliased_entity:
2392
+ # there are a few places in the ORM that automatic aliasing
2393
+ # is still desirable, and can't be automatic with a Core
2394
+ # only approach. For illustrations of "overlaps" see
2395
+ # test/orm/inheritance/test_relationships.py. There are also
2396
+ # general overlap cases with many-to-many tables where automatic
2397
+ # aliasing is desirable.
2398
+ right = AliasedClass(right, flat=True)
2399
+ need_adapter = True
2400
+
2401
+ util.warn(
2402
+ "An alias is being generated automatically against "
2403
+ "joined entity %s due to overlapping tables. This is a "
2404
+ "legacy pattern which may be "
2405
+ "deprecated in a later release. Use the "
2406
+ "aliased(<entity>, flat=True) "
2407
+ "construct explicitly, see the linked example." % right_mapper,
2408
+ code="xaj2",
2409
+ )
2410
+
2411
+ if need_adapter:
2412
+ # if need_adapter is True, we are in a deprecated case and
2413
+ # a warning has been emitted.
2414
+ assert right_mapper
2415
+
2416
+ adapter = ORMAdapter(
2417
+ _TraceAdaptRole.DEPRECATED_JOIN_ADAPT_RIGHT_SIDE,
2418
+ inspect(right),
2419
+ equivalents=right_mapper._equivalent_columns,
2420
+ )
2421
+
2422
+ # if an alias() on the right side was generated,
2423
+ # which is intended to wrap a the right side in a subquery,
2424
+ # ensure that columns retrieved from this target in the result
2425
+ # set are also adapted.
2426
+ self._mapper_loads_polymorphically_with(right_mapper, adapter)
2427
+ elif (
2428
+ not r_info.is_clause_element
2429
+ and not right_is_aliased
2430
+ and right_mapper._has_aliased_polymorphic_fromclause
2431
+ ):
2432
+ # for the case where the target mapper has a with_polymorphic
2433
+ # set up, ensure an adapter is set up for criteria that works
2434
+ # against this mapper. Previously, this logic used to
2435
+ # use the "create_aliases or aliased_entity" case to generate
2436
+ # an aliased() object, but this creates an alias that isn't
2437
+ # strictly necessary.
2438
+ # see test/orm/test_core_compilation.py
2439
+ # ::RelNaturalAliasedJoinsTest::test_straight
2440
+ # and similar
2441
+ self._mapper_loads_polymorphically_with(
2442
+ right_mapper,
2443
+ ORMAdapter(
2444
+ _TraceAdaptRole.WITH_POLYMORPHIC_ADAPTER_RIGHT_JOIN,
2445
+ right_mapper,
2446
+ selectable=right_mapper.selectable,
2447
+ equivalents=right_mapper._equivalent_columns,
2448
+ ),
2449
+ )
2450
+ # if the onclause is a ClauseElement, adapt it with any
2451
+ # adapters that are in place right now
2452
+ if isinstance(onclause, expression.ClauseElement):
2453
+ current_adapter = self._get_current_adapter()
2454
+ if current_adapter:
2455
+ onclause = current_adapter(onclause, True)
2456
+
2457
+ # if joining on a MapperProperty path,
2458
+ # track the path to prevent redundant joins
2459
+ if prop:
2460
+ self._already_joined_edges += ((left, right, prop.key),)
2461
+
2462
+ return inspect(right), right, onclause
2463
+
2464
+ @property
2465
+ def _select_args(self):
2466
+ return {
2467
+ "limit_clause": self.select_statement._limit_clause,
2468
+ "offset_clause": self.select_statement._offset_clause,
2469
+ "distinct": self.distinct,
2470
+ "distinct_on": self.distinct_on,
2471
+ "prefixes": self.select_statement._prefixes,
2472
+ "suffixes": self.select_statement._suffixes,
2473
+ "group_by": self.group_by or None,
2474
+ "fetch_clause": self.select_statement._fetch_clause,
2475
+ "fetch_clause_options": (
2476
+ self.select_statement._fetch_clause_options
2477
+ ),
2478
+ "independent_ctes": self.select_statement._independent_ctes,
2479
+ "independent_ctes_opts": (
2480
+ self.select_statement._independent_ctes_opts
2481
+ ),
2482
+ "syntax_extensions": self.syntax_extensions,
2483
+ }
2484
+
2485
+ @property
2486
+ def _should_nest_selectable(self):
2487
+ kwargs = self._select_args
2488
+
2489
+ if not self.eager_adding_joins:
2490
+ return False
2491
+
2492
+ return (
2493
+ (
2494
+ kwargs.get("limit_clause") is not None
2495
+ and self.multi_row_eager_loaders
2496
+ )
2497
+ or (
2498
+ kwargs.get("offset_clause") is not None
2499
+ and self.multi_row_eager_loaders
2500
+ )
2501
+ or kwargs.get("distinct", False)
2502
+ or kwargs.get("distinct_on", ())
2503
+ or kwargs.get("group_by", False)
2504
+ )
2505
+
2506
+ def _get_extra_criteria(self, ext_info):
2507
+ if (
2508
+ "additional_entity_criteria",
2509
+ ext_info.mapper,
2510
+ ) in self.global_attributes:
2511
+ return tuple(
2512
+ ae._resolve_where_criteria(ext_info)
2513
+ for ae in self.global_attributes[
2514
+ ("additional_entity_criteria", ext_info.mapper)
2515
+ ]
2516
+ if (ae.include_aliases or ae.entity is ext_info)
2517
+ and ae._should_include(self)
2518
+ )
2519
+ else:
2520
+ return ()
2521
+
2522
+ def _adjust_for_extra_criteria(self):
2523
+ """Apply extra criteria filtering.
2524
+
2525
+ For all distinct single-table-inheritance mappers represented in
2526
+ the columns clause of this query, as well as the "select from entity",
2527
+ add criterion to the WHERE
2528
+ clause of the given QueryContext such that only the appropriate
2529
+ subtypes are selected from the total results.
2530
+
2531
+ Additionally, add WHERE criteria originating from LoaderCriteriaOptions
2532
+ associated with the global context.
2533
+
2534
+ """
2535
+ ext_infos = [
2536
+ fromclause._annotations.get("parententity", None)
2537
+ for fromclause in self.from_clauses
2538
+ ] + [
2539
+ elem._annotations.get("parententity", None)
2540
+ for where_crit in self.select_statement._where_criteria
2541
+ for elem in sql_util.surface_expressions(where_crit)
2542
+ ]
2543
+
2544
+ for ext_info in ext_infos:
2545
+
2546
+ if (
2547
+ ext_info
2548
+ and (
2549
+ ext_info.mapper._single_table_criterion is not None
2550
+ or ("additional_entity_criteria", ext_info.mapper)
2551
+ in self.global_attributes
2552
+ )
2553
+ and ext_info not in self.extra_criteria_entities
2554
+ ):
2555
+ self.extra_criteria_entities[ext_info] = (
2556
+ ext_info,
2557
+ ext_info._adapter if ext_info.is_aliased_class else None,
2558
+ )
2559
+
2560
+ _where_criteria_to_add = ()
2561
+
2562
+ merged_single_crit = collections.defaultdict(
2563
+ lambda: (util.OrderedSet(), set())
2564
+ )
2565
+
2566
+ for ext_info, adapter in util.OrderedSet(
2567
+ self.extra_criteria_entities.values()
2568
+ ):
2569
+ if ext_info in self._join_entities:
2570
+ continue
2571
+
2572
+ # assemble single table inheritance criteria.
2573
+ if (
2574
+ ext_info.is_aliased_class
2575
+ and ext_info._base_alias()._is_with_polymorphic
2576
+ ):
2577
+ # for a with_polymorphic(), we always include the full
2578
+ # hierarchy from what's given as the base class for the wpoly.
2579
+ # this is new in 2.1 for #12395 so that it matches the behavior
2580
+ # of joined inheritance.
2581
+ hierarchy_root = ext_info._base_alias()
2582
+ else:
2583
+ hierarchy_root = ext_info
2584
+
2585
+ single_crit_component = (
2586
+ hierarchy_root.mapper._single_table_criteria_component
2587
+ )
2588
+
2589
+ if single_crit_component is not None:
2590
+ polymorphic_on, criteria = single_crit_component
2591
+
2592
+ polymorphic_on = polymorphic_on._annotate(
2593
+ {
2594
+ "parententity": hierarchy_root,
2595
+ "parentmapper": hierarchy_root.mapper,
2596
+ }
2597
+ )
2598
+
2599
+ list_of_single_crits, adapters = merged_single_crit[
2600
+ (hierarchy_root, polymorphic_on)
2601
+ ]
2602
+ list_of_single_crits.update(criteria)
2603
+ if adapter:
2604
+ adapters.add(adapter)
2605
+
2606
+ # assemble "additional entity criteria", which come from
2607
+ # with_loader_criteria() options
2608
+ if not self.compile_options._for_refresh_state:
2609
+ additional_entity_criteria = self._get_extra_criteria(ext_info)
2610
+ _where_criteria_to_add += tuple(
2611
+ adapter.traverse(crit) if adapter else crit
2612
+ for crit in additional_entity_criteria
2613
+ )
2614
+
2615
+ # merge together single table inheritance criteria keyed to
2616
+ # top-level mapper / aliasedinsp (which may be a with_polymorphic())
2617
+ for (ext_info, polymorphic_on), (
2618
+ merged_crit,
2619
+ adapters,
2620
+ ) in merged_single_crit.items():
2621
+ new_crit = polymorphic_on.in_(merged_crit)
2622
+ for adapter in adapters:
2623
+ new_crit = adapter.traverse(new_crit)
2624
+ _where_criteria_to_add += (new_crit,)
2625
+
2626
+ current_adapter = self._get_current_adapter()
2627
+ if current_adapter:
2628
+ # finally run all the criteria through the "main" adapter, if we
2629
+ # have one, and concatenate to final WHERE criteria
2630
+ for crit in _where_criteria_to_add:
2631
+ crit = current_adapter(crit, False)
2632
+ self._where_criteria += (crit,)
2633
+ else:
2634
+ # else just concatenate our criteria to the final WHERE criteria
2635
+ self._where_criteria += _where_criteria_to_add
2636
+
2637
+
2638
+ def _column_descriptions(
2639
+ query_or_select_stmt: Union[Query, Select, FromStatement],
2640
+ compile_state: Optional[_ORMSelectCompileState] = None,
2641
+ legacy: bool = False,
2642
+ ) -> List[ORMColumnDescription]:
2643
+ if compile_state is None:
2644
+ compile_state = _ORMSelectCompileState._create_entities_collection(
2645
+ query_or_select_stmt, legacy=legacy
2646
+ )
2647
+ ctx = compile_state
2648
+ d = [
2649
+ {
2650
+ "name": ent._label_name,
2651
+ "type": ent.type,
2652
+ "aliased": getattr(insp_ent, "is_aliased_class", False),
2653
+ "expr": ent.expr,
2654
+ "entity": (
2655
+ getattr(insp_ent, "entity", None)
2656
+ if ent.entity_zero is not None
2657
+ and not insp_ent.is_clause_element
2658
+ else None
2659
+ ),
2660
+ }
2661
+ for ent, insp_ent in [
2662
+ (_ent, _ent.entity_zero) for _ent in ctx._entities
2663
+ ]
2664
+ ]
2665
+ return d
2666
+
2667
+
2668
+ def _legacy_filter_by_entity_zero(
2669
+ query_or_augmented_select: Union[Query[Any], Select[Unpack[TupleAny]]],
2670
+ ) -> Optional[_InternalEntityType[Any]]:
2671
+ self = query_or_augmented_select
2672
+ if self._setup_joins:
2673
+ _last_joined_entity = self._last_joined_entity
2674
+ if _last_joined_entity is not None:
2675
+ return _last_joined_entity
2676
+
2677
+ if self._from_obj and "parententity" in self._from_obj[0]._annotations:
2678
+ return self._from_obj[0]._annotations["parententity"]
2679
+
2680
+ return _entity_from_pre_ent_zero(self)
2681
+
2682
+
2683
+ def _entity_from_pre_ent_zero(
2684
+ query_or_augmented_select: Union[Query[Any], Select[Unpack[TupleAny]]],
2685
+ ) -> Optional[_InternalEntityType[Any]]:
2686
+ self = query_or_augmented_select
2687
+ if not self._raw_columns:
2688
+ return None
2689
+
2690
+ ent = self._raw_columns[0]
2691
+
2692
+ if "parententity" in ent._annotations:
2693
+ return ent._annotations["parententity"]
2694
+ elif isinstance(ent, ORMColumnsClauseRole):
2695
+ return ent.entity
2696
+ elif "bundle" in ent._annotations:
2697
+ return ent._annotations["bundle"]
2698
+ else:
2699
+ return ent
2700
+
2701
+
2702
+ def _determine_last_joined_entity(
2703
+ setup_joins: Tuple[_SetupJoinsElement, ...],
2704
+ entity_zero: Optional[_InternalEntityType[Any]] = None,
2705
+ ) -> Optional[Union[_InternalEntityType[Any], _JoinTargetElement]]:
2706
+ if not setup_joins:
2707
+ return None
2708
+
2709
+ target, onclause, from_, flags = setup_joins[-1]
2710
+
2711
+ if isinstance(
2712
+ target,
2713
+ attributes.QueryableAttribute,
2714
+ ):
2715
+ return target.entity
2716
+ else:
2717
+ return target
2718
+
2719
+
2720
+ class _QueryEntity:
2721
+ """represent an entity column returned within a Query result."""
2722
+
2723
+ __slots__ = ()
2724
+
2725
+ supports_single_entity: bool
2726
+
2727
+ _non_hashable_value = False
2728
+ _null_column_type = False
2729
+ use_id_for_hash = False
2730
+
2731
+ _label_name: Optional[str]
2732
+ type: Union[Type[Any], TypeEngine[Any]]
2733
+ expr: Union[_InternalEntityType, ColumnElement[Any]]
2734
+ entity_zero: Optional[_InternalEntityType]
2735
+
2736
+ def setup_compile_state(self, compile_state: _ORMCompileState) -> None:
2737
+ raise NotImplementedError()
2738
+
2739
+ def setup_dml_returning_compile_state(
2740
+ self,
2741
+ compile_state: _ORMCompileState,
2742
+ adapter: Optional[_DMLReturningColFilter],
2743
+ ) -> None:
2744
+ raise NotImplementedError()
2745
+
2746
+ def row_processor(self, context, result):
2747
+ raise NotImplementedError()
2748
+
2749
+ @classmethod
2750
+ def to_compile_state(
2751
+ cls, compile_state, entities, entities_collection, is_current_entities
2752
+ ):
2753
+ for idx, entity in enumerate(entities):
2754
+ if entity._is_lambda_element:
2755
+ if entity._is_sequence:
2756
+ cls.to_compile_state(
2757
+ compile_state,
2758
+ entity._resolved,
2759
+ entities_collection,
2760
+ is_current_entities,
2761
+ )
2762
+ continue
2763
+ else:
2764
+ entity = entity._resolved
2765
+
2766
+ if entity.is_clause_element:
2767
+ if entity.is_selectable:
2768
+ if "parententity" in entity._annotations:
2769
+ _MapperEntity(
2770
+ compile_state,
2771
+ entity,
2772
+ entities_collection,
2773
+ is_current_entities,
2774
+ )
2775
+ else:
2776
+ _ColumnEntity._for_columns(
2777
+ compile_state,
2778
+ entity._select_iterable,
2779
+ entities_collection,
2780
+ idx,
2781
+ is_current_entities,
2782
+ )
2783
+ else:
2784
+ if entity._annotations.get("bundle", False):
2785
+ _BundleEntity(
2786
+ compile_state,
2787
+ entity,
2788
+ entities_collection,
2789
+ is_current_entities,
2790
+ )
2791
+ elif entity._is_clause_list:
2792
+ # this is legacy only - test_composites.py
2793
+ # test_query_cols_legacy
2794
+ _ColumnEntity._for_columns(
2795
+ compile_state,
2796
+ entity._select_iterable,
2797
+ entities_collection,
2798
+ idx,
2799
+ is_current_entities,
2800
+ )
2801
+ else:
2802
+ _ColumnEntity._for_columns(
2803
+ compile_state,
2804
+ [entity],
2805
+ entities_collection,
2806
+ idx,
2807
+ is_current_entities,
2808
+ )
2809
+ elif entity.is_bundle:
2810
+ _BundleEntity(compile_state, entity, entities_collection)
2811
+
2812
+ return entities_collection
2813
+
2814
+
2815
+ class _MapperEntity(_QueryEntity):
2816
+ """mapper/class/AliasedClass entity"""
2817
+
2818
+ __slots__ = (
2819
+ "expr",
2820
+ "mapper",
2821
+ "entity_zero",
2822
+ "is_aliased_class",
2823
+ "path",
2824
+ "_extra_entities",
2825
+ "_label_name",
2826
+ "_with_polymorphic_mappers",
2827
+ "selectable",
2828
+ "_polymorphic_discriminator",
2829
+ )
2830
+
2831
+ expr: _InternalEntityType
2832
+ mapper: Mapper[Any]
2833
+ entity_zero: _InternalEntityType
2834
+ is_aliased_class: bool
2835
+ path: PathRegistry
2836
+ _label_name: str
2837
+
2838
+ def __init__(
2839
+ self, compile_state, entity, entities_collection, is_current_entities
2840
+ ):
2841
+ entities_collection.append(self)
2842
+ if is_current_entities:
2843
+ if compile_state._primary_entity is None:
2844
+ compile_state._primary_entity = self
2845
+ compile_state._has_mapper_entities = True
2846
+ compile_state._has_orm_entities = True
2847
+
2848
+ entity = entity._annotations["parententity"]
2849
+ entity._post_inspect
2850
+ ext_info = self.entity_zero = entity
2851
+ entity = ext_info.entity
2852
+
2853
+ self.expr = entity
2854
+ self.mapper = mapper = ext_info.mapper
2855
+
2856
+ self._extra_entities = (self.expr,)
2857
+
2858
+ if ext_info.is_aliased_class:
2859
+ self._label_name = ext_info.name
2860
+ else:
2861
+ self._label_name = mapper.class_.__name__
2862
+
2863
+ self.is_aliased_class = ext_info.is_aliased_class
2864
+ self.path = ext_info._path_registry
2865
+
2866
+ self.selectable = ext_info.selectable
2867
+ self._with_polymorphic_mappers = ext_info.with_polymorphic_mappers
2868
+ self._polymorphic_discriminator = ext_info.polymorphic_on
2869
+
2870
+ if mapper._should_select_with_poly_adapter:
2871
+ compile_state._create_with_polymorphic_adapter(
2872
+ ext_info, self.selectable
2873
+ )
2874
+
2875
+ supports_single_entity = True
2876
+
2877
+ _non_hashable_value = True
2878
+ use_id_for_hash = True
2879
+
2880
+ @property
2881
+ def type(self):
2882
+ return self.mapper.class_
2883
+
2884
+ @property
2885
+ def entity_zero_or_selectable(self):
2886
+ return self.entity_zero
2887
+
2888
+ def corresponds_to(self, entity):
2889
+ return _entity_corresponds_to(self.entity_zero, entity)
2890
+
2891
+ def _get_entity_clauses(self, compile_state):
2892
+ adapter = None
2893
+
2894
+ if not self.is_aliased_class:
2895
+ if compile_state._polymorphic_adapters:
2896
+ adapter = compile_state._polymorphic_adapters.get(
2897
+ self.mapper, None
2898
+ )
2899
+ else:
2900
+ adapter = self.entity_zero._adapter
2901
+
2902
+ if adapter:
2903
+ if compile_state._from_obj_alias:
2904
+ ret = adapter.wrap(compile_state._from_obj_alias)
2905
+ else:
2906
+ ret = adapter
2907
+ else:
2908
+ ret = compile_state._from_obj_alias
2909
+
2910
+ return ret
2911
+
2912
+ def row_processor(self, context, result):
2913
+ compile_state = context.compile_state
2914
+ adapter = self._get_entity_clauses(compile_state)
2915
+
2916
+ if compile_state.compound_eager_adapter and adapter:
2917
+ adapter = adapter.wrap(compile_state.compound_eager_adapter)
2918
+ elif not adapter:
2919
+ adapter = compile_state.compound_eager_adapter
2920
+
2921
+ if compile_state._primary_entity is self:
2922
+ only_load_props = compile_state.compile_options._only_load_props
2923
+ refresh_state = context.refresh_state
2924
+ else:
2925
+ only_load_props = refresh_state = None
2926
+
2927
+ _instance = loading._instance_processor(
2928
+ self,
2929
+ self.mapper,
2930
+ context,
2931
+ result,
2932
+ self.path,
2933
+ adapter,
2934
+ only_load_props=only_load_props,
2935
+ refresh_state=refresh_state,
2936
+ polymorphic_discriminator=self._polymorphic_discriminator,
2937
+ )
2938
+
2939
+ return _instance, self._label_name, self._extra_entities
2940
+
2941
+ def setup_dml_returning_compile_state(
2942
+ self,
2943
+ compile_state: _ORMCompileState,
2944
+ adapter: Optional[_DMLReturningColFilter],
2945
+ ) -> None:
2946
+ loading._setup_entity_query(
2947
+ compile_state,
2948
+ self.mapper,
2949
+ self,
2950
+ self.path,
2951
+ adapter,
2952
+ compile_state.primary_columns,
2953
+ with_polymorphic=self._with_polymorphic_mappers,
2954
+ only_load_props=compile_state.compile_options._only_load_props,
2955
+ polymorphic_discriminator=self._polymorphic_discriminator,
2956
+ )
2957
+
2958
+ def setup_compile_state(self, compile_state):
2959
+ adapter = self._get_entity_clauses(compile_state)
2960
+
2961
+ single_table_crit = self.mapper._single_table_criterion
2962
+ if (
2963
+ single_table_crit is not None
2964
+ or ("additional_entity_criteria", self.mapper)
2965
+ in compile_state.global_attributes
2966
+ ):
2967
+ ext_info = self.entity_zero
2968
+ compile_state.extra_criteria_entities[ext_info] = (
2969
+ ext_info,
2970
+ ext_info._adapter if ext_info.is_aliased_class else None,
2971
+ )
2972
+
2973
+ loading._setup_entity_query(
2974
+ compile_state,
2975
+ self.mapper,
2976
+ self,
2977
+ self.path,
2978
+ adapter,
2979
+ compile_state.primary_columns,
2980
+ with_polymorphic=self._with_polymorphic_mappers,
2981
+ only_load_props=compile_state.compile_options._only_load_props,
2982
+ polymorphic_discriminator=self._polymorphic_discriminator,
2983
+ )
2984
+ compile_state._fallback_from_clauses.append(self.selectable)
2985
+
2986
+
2987
+ class _BundleEntity(_QueryEntity):
2988
+ _extra_entities = ()
2989
+
2990
+ __slots__ = (
2991
+ "bundle",
2992
+ "expr",
2993
+ "type",
2994
+ "_label_name",
2995
+ "_entities",
2996
+ "supports_single_entity",
2997
+ )
2998
+
2999
+ _entities: List[_QueryEntity]
3000
+ bundle: Bundle
3001
+ type: Type[Any]
3002
+ _label_name: str
3003
+ supports_single_entity: bool
3004
+ expr: Bundle
3005
+
3006
+ def __init__(
3007
+ self,
3008
+ compile_state,
3009
+ expr,
3010
+ entities_collection,
3011
+ is_current_entities,
3012
+ setup_entities=True,
3013
+ parent_bundle=None,
3014
+ ):
3015
+ compile_state._has_orm_entities = True
3016
+
3017
+ expr = expr._annotations["bundle"]
3018
+ if parent_bundle:
3019
+ parent_bundle._entities.append(self)
3020
+ else:
3021
+ entities_collection.append(self)
3022
+
3023
+ if isinstance(
3024
+ expr, (attributes.QueryableAttribute, interfaces.PropComparator)
3025
+ ):
3026
+ bundle = expr.__clause_element__()
3027
+ else:
3028
+ bundle = expr
3029
+
3030
+ self.bundle = self.expr = bundle
3031
+ self.type = type(bundle)
3032
+ self._label_name = bundle.name
3033
+ self._entities = []
3034
+
3035
+ if setup_entities:
3036
+ for expr in bundle.exprs:
3037
+ if "bundle" in expr._annotations:
3038
+ _BundleEntity(
3039
+ compile_state,
3040
+ expr,
3041
+ entities_collection,
3042
+ is_current_entities,
3043
+ parent_bundle=self,
3044
+ )
3045
+ elif isinstance(expr, Bundle):
3046
+ _BundleEntity(
3047
+ compile_state,
3048
+ expr,
3049
+ entities_collection,
3050
+ is_current_entities,
3051
+ parent_bundle=self,
3052
+ )
3053
+ else:
3054
+ _ORMColumnEntity._for_columns(
3055
+ compile_state,
3056
+ [expr],
3057
+ entities_collection,
3058
+ None,
3059
+ is_current_entities,
3060
+ parent_bundle=self,
3061
+ )
3062
+
3063
+ self.supports_single_entity = self.bundle.single_entity
3064
+
3065
+ @property
3066
+ def mapper(self):
3067
+ ezero = self.entity_zero
3068
+ if ezero is not None:
3069
+ return ezero.mapper
3070
+ else:
3071
+ return None
3072
+
3073
+ @property
3074
+ def entity_zero(self):
3075
+ for ent in self._entities:
3076
+ ezero = ent.entity_zero
3077
+ if ezero is not None:
3078
+ return ezero
3079
+ else:
3080
+ return None
3081
+
3082
+ def corresponds_to(self, entity):
3083
+ # TODO: we might be able to implement this but for now
3084
+ # we are working around it
3085
+ return False
3086
+
3087
+ @property
3088
+ def entity_zero_or_selectable(self):
3089
+ for ent in self._entities:
3090
+ ezero = ent.entity_zero_or_selectable
3091
+ if ezero is not None:
3092
+ return ezero
3093
+ else:
3094
+ return None
3095
+
3096
+ def setup_compile_state(self, compile_state):
3097
+ for ent in self._entities:
3098
+ ent.setup_compile_state(compile_state)
3099
+
3100
+ def setup_dml_returning_compile_state(
3101
+ self,
3102
+ compile_state: _ORMCompileState,
3103
+ adapter: Optional[_DMLReturningColFilter],
3104
+ ) -> None:
3105
+ return self.setup_compile_state(compile_state)
3106
+
3107
+ def row_processor(self, context, result):
3108
+ procs, labels, extra = zip(
3109
+ *[ent.row_processor(context, result) for ent in self._entities]
3110
+ )
3111
+
3112
+ proc = self.bundle.create_row_processor(context.query, procs, labels)
3113
+
3114
+ return proc, self._label_name, self._extra_entities
3115
+
3116
+
3117
+ class _ColumnEntity(_QueryEntity):
3118
+ __slots__ = (
3119
+ "_fetch_column",
3120
+ "_row_processor",
3121
+ "raw_column_index",
3122
+ "translate_raw_column",
3123
+ )
3124
+
3125
+ @classmethod
3126
+ def _for_columns(
3127
+ cls,
3128
+ compile_state,
3129
+ columns,
3130
+ entities_collection,
3131
+ raw_column_index,
3132
+ is_current_entities,
3133
+ parent_bundle=None,
3134
+ ):
3135
+ for column in columns:
3136
+ annotations = column._annotations
3137
+ if "parententity" in annotations:
3138
+ _entity = annotations["parententity"]
3139
+ else:
3140
+ _entity = sql_util.extract_first_column_annotation(
3141
+ column, "parententity"
3142
+ )
3143
+
3144
+ if _entity:
3145
+ if "identity_token" in column._annotations:
3146
+ _IdentityTokenEntity(
3147
+ compile_state,
3148
+ column,
3149
+ entities_collection,
3150
+ _entity,
3151
+ raw_column_index,
3152
+ is_current_entities,
3153
+ parent_bundle=parent_bundle,
3154
+ )
3155
+ else:
3156
+ _ORMColumnEntity(
3157
+ compile_state,
3158
+ column,
3159
+ entities_collection,
3160
+ _entity,
3161
+ raw_column_index,
3162
+ is_current_entities,
3163
+ parent_bundle=parent_bundle,
3164
+ )
3165
+ else:
3166
+ _RawColumnEntity(
3167
+ compile_state,
3168
+ column,
3169
+ entities_collection,
3170
+ raw_column_index,
3171
+ is_current_entities,
3172
+ parent_bundle=parent_bundle,
3173
+ )
3174
+
3175
+ @property
3176
+ def type(self):
3177
+ return self.column.type
3178
+
3179
+ @property
3180
+ def _non_hashable_value(self):
3181
+ return not self.column.type.hashable
3182
+
3183
+ @property
3184
+ def _null_column_type(self):
3185
+ return self.column.type._isnull
3186
+
3187
+ def row_processor(self, context, result):
3188
+ compile_state = context.compile_state
3189
+
3190
+ # the resulting callable is entirely cacheable so just return
3191
+ # it if we already made one
3192
+ if self._row_processor is not None:
3193
+ getter, label_name, extra_entities = self._row_processor
3194
+ if self.translate_raw_column:
3195
+ extra_entities += (
3196
+ context.query._raw_columns[self.raw_column_index],
3197
+ )
3198
+
3199
+ return getter, label_name, extra_entities
3200
+
3201
+ # retrieve the column that would have been set up in
3202
+ # setup_compile_state, to avoid doing redundant work
3203
+ if self._fetch_column is not None:
3204
+ column = self._fetch_column
3205
+ else:
3206
+ # fetch_column will be None when we are doing a from_statement
3207
+ # and setup_compile_state may not have been called.
3208
+ column = self.column
3209
+
3210
+ # previously, the RawColumnEntity didn't look for from_obj_alias
3211
+ # however I can't think of a case where we would be here and
3212
+ # we'd want to ignore it if this is the from_statement use case.
3213
+ # it's not really a use case to have raw columns + from_statement
3214
+ if compile_state._from_obj_alias:
3215
+ column = compile_state._from_obj_alias.columns[column]
3216
+
3217
+ if column._annotations:
3218
+ # annotated columns perform more slowly in compiler and
3219
+ # result due to the __eq__() method, so use deannotated
3220
+ column = column._deannotate()
3221
+
3222
+ if compile_state.compound_eager_adapter:
3223
+ column = compile_state.compound_eager_adapter.columns[column]
3224
+
3225
+ getter = result._getter(column)
3226
+ ret = getter, self._label_name, self._extra_entities
3227
+ self._row_processor = ret
3228
+
3229
+ if self.translate_raw_column:
3230
+ extra_entities = self._extra_entities + (
3231
+ context.query._raw_columns[self.raw_column_index],
3232
+ )
3233
+ return getter, self._label_name, extra_entities
3234
+ else:
3235
+ return ret
3236
+
3237
+
3238
+ class _RawColumnEntity(_ColumnEntity):
3239
+ entity_zero = None
3240
+ mapper = None
3241
+ supports_single_entity = False
3242
+
3243
+ __slots__ = (
3244
+ "expr",
3245
+ "column",
3246
+ "_label_name",
3247
+ "entity_zero_or_selectable",
3248
+ "_extra_entities",
3249
+ )
3250
+
3251
+ def __init__(
3252
+ self,
3253
+ compile_state,
3254
+ column,
3255
+ entities_collection,
3256
+ raw_column_index,
3257
+ is_current_entities,
3258
+ parent_bundle=None,
3259
+ ):
3260
+ self.expr = column
3261
+ self.raw_column_index = raw_column_index
3262
+ self.translate_raw_column = raw_column_index is not None
3263
+
3264
+ if column._is_star:
3265
+ compile_state.compile_options += {"_is_star": True}
3266
+
3267
+ if not is_current_entities or column._is_text_clause:
3268
+ self._label_name = None
3269
+ else:
3270
+ if parent_bundle:
3271
+ self._label_name = column._proxy_key
3272
+ else:
3273
+ self._label_name = compile_state._label_convention(column)
3274
+
3275
+ if parent_bundle:
3276
+ parent_bundle._entities.append(self)
3277
+ else:
3278
+ entities_collection.append(self)
3279
+
3280
+ self.column = column
3281
+ self.entity_zero_or_selectable = (
3282
+ self.column._from_objects[0] if self.column._from_objects else None
3283
+ )
3284
+ self._extra_entities = (self.expr, self.column)
3285
+ self._fetch_column = self._row_processor = None
3286
+
3287
+ def corresponds_to(self, entity):
3288
+ return False
3289
+
3290
+ def setup_dml_returning_compile_state(
3291
+ self,
3292
+ compile_state: _ORMCompileState,
3293
+ adapter: Optional[_DMLReturningColFilter],
3294
+ ) -> None:
3295
+ return self.setup_compile_state(compile_state)
3296
+
3297
+ def setup_compile_state(self, compile_state):
3298
+ current_adapter = compile_state._get_current_adapter()
3299
+ if current_adapter:
3300
+ column = current_adapter(self.column, False)
3301
+ if column is None:
3302
+ return
3303
+ else:
3304
+ column = self.column
3305
+
3306
+ if column._annotations:
3307
+ # annotated columns perform more slowly in compiler and
3308
+ # result due to the __eq__() method, so use deannotated
3309
+ column = column._deannotate()
3310
+
3311
+ compile_state.dedupe_columns.add(column)
3312
+ compile_state.primary_columns.append(column)
3313
+ self._fetch_column = column
3314
+
3315
+
3316
+ class _ORMColumnEntity(_ColumnEntity):
3317
+ """Column/expression based entity."""
3318
+
3319
+ supports_single_entity = False
3320
+
3321
+ __slots__ = (
3322
+ "expr",
3323
+ "mapper",
3324
+ "column",
3325
+ "_label_name",
3326
+ "entity_zero_or_selectable",
3327
+ "entity_zero",
3328
+ "_extra_entities",
3329
+ )
3330
+
3331
+ def __init__(
3332
+ self,
3333
+ compile_state,
3334
+ column,
3335
+ entities_collection,
3336
+ parententity,
3337
+ raw_column_index,
3338
+ is_current_entities,
3339
+ parent_bundle=None,
3340
+ ):
3341
+ annotations = column._annotations
3342
+
3343
+ _entity = parententity
3344
+
3345
+ # an AliasedClass won't have proxy_key in the annotations for
3346
+ # a column if it was acquired using the class' adapter directly,
3347
+ # such as using AliasedInsp._adapt_element(). this occurs
3348
+ # within internal loaders.
3349
+
3350
+ orm_key = annotations.get("proxy_key", None)
3351
+ proxy_owner = annotations.get("proxy_owner", _entity)
3352
+ if orm_key:
3353
+ self.expr = getattr(proxy_owner.entity, orm_key)
3354
+ self.translate_raw_column = False
3355
+ else:
3356
+ # if orm_key is not present, that means this is an ad-hoc
3357
+ # SQL ColumnElement, like a CASE() or other expression.
3358
+ # include this column position from the invoked statement
3359
+ # in the ORM-level ResultSetMetaData on each execute, so that
3360
+ # it can be targeted by identity after caching
3361
+ self.expr = column
3362
+ self.translate_raw_column = raw_column_index is not None
3363
+
3364
+ self.raw_column_index = raw_column_index
3365
+
3366
+ if is_current_entities:
3367
+ if parent_bundle:
3368
+ self._label_name = orm_key if orm_key else column._proxy_key
3369
+ else:
3370
+ self._label_name = compile_state._label_convention(
3371
+ column, col_name=orm_key
3372
+ )
3373
+ else:
3374
+ self._label_name = None
3375
+
3376
+ _entity._post_inspect
3377
+ self.entity_zero = self.entity_zero_or_selectable = ezero = _entity
3378
+ self.mapper = mapper = _entity.mapper
3379
+
3380
+ if parent_bundle:
3381
+ parent_bundle._entities.append(self)
3382
+ else:
3383
+ entities_collection.append(self)
3384
+
3385
+ compile_state._has_orm_entities = True
3386
+
3387
+ self.column = column
3388
+
3389
+ self._fetch_column = self._row_processor = None
3390
+
3391
+ self._extra_entities = (self.expr, self.column)
3392
+
3393
+ if mapper._should_select_with_poly_adapter:
3394
+ compile_state._create_with_polymorphic_adapter(
3395
+ ezero, ezero.selectable
3396
+ )
3397
+
3398
+ def corresponds_to(self, entity):
3399
+ if _is_aliased_class(entity):
3400
+ # TODO: polymorphic subclasses ?
3401
+ return entity is self.entity_zero
3402
+ else:
3403
+ return not _is_aliased_class(
3404
+ self.entity_zero
3405
+ ) and entity.common_parent(self.entity_zero)
3406
+
3407
+ def setup_dml_returning_compile_state(
3408
+ self,
3409
+ compile_state: _ORMCompileState,
3410
+ adapter: Optional[_DMLReturningColFilter],
3411
+ ) -> None:
3412
+
3413
+ self._fetch_column = column = self.column
3414
+ if adapter:
3415
+ column = adapter(column, False)
3416
+
3417
+ if column is not None:
3418
+ compile_state.dedupe_columns.add(column)
3419
+ compile_state.primary_columns.append(column)
3420
+
3421
+ def setup_compile_state(self, compile_state):
3422
+ current_adapter = compile_state._get_current_adapter()
3423
+ if current_adapter:
3424
+ column = current_adapter(self.column, False)
3425
+ if column is None:
3426
+ assert compile_state.is_dml_returning
3427
+ self._fetch_column = self.column
3428
+ return
3429
+ else:
3430
+ column = self.column
3431
+
3432
+ ezero = self.entity_zero
3433
+
3434
+ single_table_crit = self.mapper._single_table_criterion
3435
+ if (
3436
+ single_table_crit is not None
3437
+ or ("additional_entity_criteria", self.mapper)
3438
+ in compile_state.global_attributes
3439
+ ):
3440
+ compile_state.extra_criteria_entities[ezero] = (
3441
+ ezero,
3442
+ ezero._adapter if ezero.is_aliased_class else None,
3443
+ )
3444
+
3445
+ if column._annotations and not column._expression_label:
3446
+ # annotated columns perform more slowly in compiler and
3447
+ # result due to the __eq__() method, so use deannotated
3448
+ column = column._deannotate()
3449
+
3450
+ # use entity_zero as the from if we have it. this is necessary
3451
+ # for polymorphic scenarios where our FROM is based on ORM entity,
3452
+ # not the FROM of the column. but also, don't use it if our column
3453
+ # doesn't actually have any FROMs that line up, such as when its
3454
+ # a scalar subquery.
3455
+ if set(self.column._from_objects).intersection(
3456
+ ezero.selectable._from_objects
3457
+ ):
3458
+ compile_state._fallback_from_clauses.append(ezero.selectable)
3459
+
3460
+ compile_state.dedupe_columns.add(column)
3461
+ compile_state.primary_columns.append(column)
3462
+ self._fetch_column = column
3463
+
3464
+
3465
+ class _IdentityTokenEntity(_ORMColumnEntity):
3466
+ translate_raw_column = False
3467
+
3468
+ def setup_compile_state(self, compile_state):
3469
+ pass
3470
+
3471
+ def row_processor(self, context, result):
3472
+ def getter(row):
3473
+ return context.load_options._identity_token
3474
+
3475
+ return getter, self._label_name, self._extra_entities