SQLAlchemy 2.0.47__cp313-cp313t-win32.whl

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