context.py 112 KB

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