Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/context.py: 26%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1373 statements  

1# orm/context.py 

2# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors 

3# <see AUTHORS file> 

4# 

5# This module is part of SQLAlchemy and is released under 

6# the MIT License: https://www.opensource.org/licenses/mit-license.php 

7# mypy: ignore-errors 

8 

9from __future__ import annotations 

10 

11import collections 

12import itertools 

13from typing import AbstractSet 

14from typing import Any 

15from typing import cast 

16from typing import Dict 

17from typing import FrozenSet 

18from typing import Iterable 

19from typing import List 

20from typing import Optional 

21from typing import Set 

22from typing import Tuple 

23from typing import Type 

24from typing import TYPE_CHECKING 

25from typing import TypeVar 

26from typing import Union 

27 

28from . import attributes 

29from . import interfaces 

30from . import loading 

31from .base import _is_aliased_class 

32from .interfaces import ORMColumnDescription 

33from .interfaces import ORMColumnsClauseRole 

34from .path_registry import PathRegistry 

35from .util import _entity_corresponds_to 

36from .util import _ORMJoin 

37from .util import _TraceAdaptRole 

38from .util import AliasedClass 

39from .util import Bundle 

40from .util import ORMAdapter 

41from .util import ORMStatementAdapter 

42from .. import exc as sa_exc 

43from .. import future 

44from .. import inspect 

45from .. import sql 

46from .. import util 

47from ..sql import coercions 

48from ..sql import expression 

49from ..sql import roles 

50from ..sql import util as sql_util 

51from ..sql import visitors 

52from ..sql._typing import is_dml 

53from ..sql._typing import is_insert_update 

54from ..sql._typing import is_select_base 

55from ..sql.base import _expand_cloned 

56from ..sql.base import _from_objects 

57from ..sql.base import _select_iterables 

58from ..sql.base import CacheableOptions 

59from ..sql.base import CompileState 

60from ..sql.base import Executable 

61from ..sql.base import ExecutableStatement 

62from ..sql.base import Generative 

63from ..sql.base import Options 

64from ..sql.dml import UpdateBase 

65from ..sql.elements import GroupedElement 

66from ..sql.elements import TextClause 

67from ..sql.selectable import CompoundSelectState 

68from ..sql.selectable import LABEL_STYLE_DISAMBIGUATE_ONLY 

69from ..sql.selectable import LABEL_STYLE_NONE 

70from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL 

71from ..sql.selectable import Select 

72from ..sql.selectable import SelectLabelStyle 

73from ..sql.selectable import SelectState 

74from ..sql.selectable import TypedReturnsRows 

75from ..sql.visitors import InternalTraversal 

76from ..util.typing import TupleAny 

77from ..util.typing import TypeVarTuple 

78from ..util.typing import Unpack 

79 

80if TYPE_CHECKING: 

81 from ._typing import _InternalEntityType 

82 from ._typing import OrmExecuteOptionsParameter 

83 from .loading import _PostLoad 

84 from .mapper import Mapper 

85 from .query import Query 

86 from .session import _BindArguments 

87 from .session import Session 

88 from .util import LoaderCriteriaOption 

89 from ..engine import Result 

90 from ..engine.interfaces import _CoreSingleExecuteParams 

91 from ..sql._typing import _ColumnsClauseArgument 

92 from ..sql.compiler import SQLCompiler 

93 from ..sql.dml import _DMLTableElement 

94 from ..sql.elements import ColumnElement 

95 from ..sql.selectable import _JoinTargetElement 

96 from ..sql.selectable import _LabelConventionCallable 

97 from ..sql.selectable import _SetupJoinsElement 

98 from ..sql.selectable import ExecutableReturnsRows 

99 from ..sql.selectable import FromClause 

100 from ..sql.selectable import SelectBase 

101 from ..sql.type_api import TypeEngine 

102 

103_T = TypeVar("_T", bound=Any) 

104_Ts = TypeVarTuple("_Ts") 

105_path_registry = PathRegistry.root 

106 

107LABEL_STYLE_LEGACY_ORM = SelectLabelStyle.LABEL_STYLE_LEGACY_ORM 

108 

109 

110class QueryContext: 

111 __slots__ = ( 

112 "top_level_context", 

113 "compile_state", 

114 "query", 

115 "user_passed_query", 

116 "params", 

117 "load_options", 

118 "bind_arguments", 

119 "execution_options", 

120 "session", 

121 "autoflush", 

122 "populate_existing", 

123 "invoke_all_eagers", 

124 "version_check", 

125 "refresh_state", 

126 "create_eager_joins", 

127 "propagated_loader_options", 

128 "attributes", 

129 "runid", 

130 "partials", 

131 "post_load_paths", 

132 "identity_token", 

133 "yield_per", 

134 "loaders_require_buffering", 

135 "loaders_require_uniquing", 

136 ) 

137 

138 runid: int 

139 post_load_paths: Dict[PathRegistry, _PostLoad] 

140 compile_state: _ORMCompileState 

141 

142 @property 

143 def requires_uniquing(self) -> bool: 

144 return bool(self.compile_state.multi_row_eager_loaders) 

145 

146 class default_load_options(Options): 

147 _only_return_tuples = False 

148 _populate_existing = False 

149 _version_check = False 

150 _invoke_all_eagers = True 

151 _autoflush = True 

152 _identity_token = None 

153 _yield_per = None 

154 _refresh_state = None 

155 _lazy_loaded_from = None 

156 _legacy_uniquing = False 

157 _sa_top_level_orm_context = None 

158 _is_user_refresh = False 

159 

160 def __init__( 

161 self, 

162 compile_state: CompileState, 

163 statement: Union[ 

164 Select[Unpack[TupleAny]], 

165 FromStatement[Unpack[TupleAny]], 

166 UpdateBase, 

167 ], 

168 user_passed_query: Union[ 

169 Select[Unpack[TupleAny]], 

170 FromStatement[Unpack[TupleAny]], 

171 UpdateBase, 

172 ], 

173 params: _CoreSingleExecuteParams, 

174 session: Session, 

175 load_options: Union[ 

176 Type[QueryContext.default_load_options], 

177 QueryContext.default_load_options, 

178 ], 

179 execution_options: Optional[OrmExecuteOptionsParameter] = None, 

180 bind_arguments: Optional[_BindArguments] = None, 

181 ): 

182 self.load_options = load_options 

183 self.execution_options = execution_options or util.EMPTY_DICT 

184 self.bind_arguments = bind_arguments or util.EMPTY_DICT 

185 self.compile_state = compile_state 

186 self.query = statement 

187 

188 # the query that the end user passed to Session.execute() or similar. 

189 # this is usually the same as .query, except in the bulk_persistence 

190 # routines where a separate FromStatement is manufactured in the 

191 # compile stage; this allows differentiation in that case. 

192 self.user_passed_query = user_passed_query 

193 

194 self.session = session 

195 self.loaders_require_buffering = False 

196 self.loaders_require_uniquing = False 

197 self.params = params 

198 self.top_level_context = load_options._sa_top_level_orm_context 

199 

200 cached_options = compile_state.select_statement._with_options 

201 uncached_options = user_passed_query._with_options 

202 

203 # see issue #7447 , #8399 for some background 

204 # propagated loader options will be present on loaded InstanceState 

205 # objects under state.load_options and are typically used by 

206 # LazyLoader to apply options to the SELECT statement it emits. 

207 # For compile state options (i.e. loader strategy options), these 

208 # need to line up with the ".load_path" attribute which in 

209 # loader.py is pulled from context.compile_state.current_path. 

210 # so, this means these options have to be the ones from the 

211 # *cached* statement that's travelling with compile_state, not the 

212 # *current* statement which won't match up for an ad-hoc 

213 # AliasedClass 

214 self.propagated_loader_options = tuple( 

215 opt._adapt_cached_option_to_uncached_option(self, uncached_opt) 

216 for opt, uncached_opt in zip(cached_options, uncached_options) 

217 if opt.propagate_to_loaders 

218 ) 

219 

220 self.attributes = dict(compile_state.attributes) 

221 

222 self.autoflush = load_options._autoflush 

223 self.populate_existing = load_options._populate_existing 

224 self.invoke_all_eagers = load_options._invoke_all_eagers 

225 self.version_check = load_options._version_check 

226 self.refresh_state = load_options._refresh_state 

227 self.yield_per = load_options._yield_per 

228 self.identity_token = load_options._identity_token 

229 

230 def _get_top_level_context(self) -> QueryContext: 

231 return self.top_level_context or self 

232 

233 

234_orm_load_exec_options = util.immutabledict( 

235 {"_result_disable_adapt_to_context": True} 

236) 

237 

238 

239class _AbstractORMCompileState(CompileState): 

240 is_dml_returning = False 

241 

242 def _init_global_attributes( 

243 self, statement, compiler, *, toplevel, process_criteria_for_toplevel 

244 ): 

245 self.attributes = {} 

246 

247 if compiler is None: 

248 # this is the legacy / testing only ORM _compile_state() use case. 

249 # there is no need to apply criteria options for this. 

250 self.global_attributes = {} 

251 assert toplevel 

252 return 

253 else: 

254 self.global_attributes = ga = compiler._global_attributes 

255 

256 if toplevel: 

257 ga["toplevel_orm"] = True 

258 

259 if process_criteria_for_toplevel: 

260 for opt in statement._with_options: 

261 if opt._is_criteria_option: 

262 opt.process_compile_state(self) 

263 

264 return 

265 elif ga.get("toplevel_orm", False): 

266 return 

267 

268 stack_0 = compiler.stack[0] 

269 

270 try: 

271 toplevel_stmt = stack_0["selectable"] 

272 except KeyError: 

273 pass 

274 else: 

275 for opt in toplevel_stmt._with_options: 

276 if opt._is_compile_state and opt._is_criteria_option: 

277 opt.process_compile_state(self) 

278 

279 ga["toplevel_orm"] = True 

280 

281 @classmethod 

282 def create_for_statement( 

283 cls, 

284 statement: Executable, 

285 compiler: SQLCompiler, 

286 **kw: Any, 

287 ) -> CompileState: 

288 """Create a context for a statement given a :class:`.Compiler`. 

289 

290 This method is always invoked in the context of SQLCompiler.process(). 

291 

292 For a Select object, this would be invoked from 

293 SQLCompiler.visit_select(). For the special FromStatement object used 

294 by Query to indicate "Query.from_statement()", this is called by 

295 FromStatement._compiler_dispatch() that would be called by 

296 SQLCompiler.process(). 

297 """ 

298 return super().create_for_statement(statement, compiler, **kw) 

299 

300 @classmethod 

301 def orm_pre_session_exec( 

302 cls, 

303 session, 

304 statement, 

305 params, 

306 execution_options, 

307 bind_arguments, 

308 is_pre_event, 

309 ): 

310 raise NotImplementedError() 

311 

312 @classmethod 

313 def orm_execute_statement( 

314 cls, 

315 session, 

316 statement, 

317 params, 

318 execution_options, 

319 bind_arguments, 

320 conn, 

321 ) -> Result: 

322 result = conn.execute( 

323 statement, params or {}, execution_options=execution_options 

324 ) 

325 return cls.orm_setup_cursor_result( 

326 session, 

327 statement, 

328 params, 

329 execution_options, 

330 bind_arguments, 

331 result, 

332 ) 

333 

334 @classmethod 

335 def orm_setup_cursor_result( 

336 cls, 

337 session, 

338 statement, 

339 params, 

340 execution_options, 

341 bind_arguments, 

342 result, 

343 ): 

344 raise NotImplementedError() 

345 

346 

347class _AutoflushOnlyORMCompileState(_AbstractORMCompileState): 

348 """ORM compile state that is a passthrough, except for autoflush.""" 

349 

350 @classmethod 

351 def orm_pre_session_exec( 

352 cls, 

353 session, 

354 statement, 

355 params, 

356 execution_options, 

357 bind_arguments, 

358 is_pre_event, 

359 ): 

360 # consume result-level load_options. These may have been set up 

361 # in an ORMExecuteState hook 

362 ( 

363 load_options, 

364 execution_options, 

365 ) = QueryContext.default_load_options.from_execution_options( 

366 "_sa_orm_load_options", 

367 { 

368 "autoflush", 

369 }, 

370 execution_options, 

371 statement._execution_options, 

372 ) 

373 

374 if not is_pre_event and load_options._autoflush: 

375 session._autoflush() 

376 

377 return statement, execution_options, params 

378 

379 @classmethod 

380 def orm_setup_cursor_result( 

381 cls, 

382 session, 

383 statement, 

384 params, 

385 execution_options, 

386 bind_arguments, 

387 result, 

388 ): 

389 return result 

390 

391 

392class _ORMCompileState(_AbstractORMCompileState): 

393 class default_compile_options(CacheableOptions): 

394 _cache_key_traversal = [ 

395 ("_use_legacy_query_style", InternalTraversal.dp_boolean), 

396 ("_for_statement", InternalTraversal.dp_boolean), 

397 ("_bake_ok", InternalTraversal.dp_boolean), 

398 ("_current_path", InternalTraversal.dp_has_cache_key), 

399 ("_enable_single_crit", InternalTraversal.dp_boolean), 

400 ("_enable_eagerloads", InternalTraversal.dp_boolean), 

401 ("_only_load_props", InternalTraversal.dp_plain_obj), 

402 ("_set_base_alias", InternalTraversal.dp_boolean), 

403 ("_for_refresh_state", InternalTraversal.dp_boolean), 

404 ("_render_for_subquery", InternalTraversal.dp_boolean), 

405 ("_is_star", InternalTraversal.dp_boolean), 

406 ] 

407 

408 # set to True by default from Query._statement_20(), to indicate 

409 # the rendered query should look like a legacy ORM query. right 

410 # now this basically indicates we should use tablename_columnname 

411 # style labels. Generally indicates the statement originated 

412 # from a Query object. 

413 _use_legacy_query_style = False 

414 

415 # set *only* when we are coming from the Query.statement 

416 # accessor, or a Query-level equivalent such as 

417 # query.subquery(). this supersedes "toplevel". 

418 _for_statement = False 

419 

420 _bake_ok = True 

421 _current_path = _path_registry 

422 _enable_single_crit = True 

423 _enable_eagerloads = True 

424 _only_load_props = None 

425 _set_base_alias = False 

426 _for_refresh_state = False 

427 _render_for_subquery = False 

428 _is_star = False 

429 

430 attributes: Dict[Any, Any] 

431 global_attributes: Dict[Any, Any] 

432 

433 statement: Union[ 

434 Select[Unpack[TupleAny]], FromStatement[Unpack[TupleAny]], UpdateBase 

435 ] 

436 select_statement: Union[ 

437 Select[Unpack[TupleAny]], FromStatement[Unpack[TupleAny]] 

438 ] 

439 _entities: List[_QueryEntity] 

440 _polymorphic_adapters: Dict[_InternalEntityType, ORMAdapter] 

441 compile_options: Union[ 

442 Type[default_compile_options], default_compile_options 

443 ] 

444 _primary_entity: Optional[_QueryEntity] 

445 use_legacy_query_style: bool 

446 _label_convention: _LabelConventionCallable 

447 primary_columns: List[ColumnElement[Any]] 

448 secondary_columns: List[ColumnElement[Any]] 

449 dedupe_columns: Set[ColumnElement[Any]] 

450 create_eager_joins: List[ 

451 # TODO: this structure is set up by JoinedLoader 

452 TupleAny 

453 ] 

454 current_path: PathRegistry = _path_registry 

455 _has_mapper_entities = False 

456 

457 def __init__(self, *arg, **kw): 

458 raise NotImplementedError() 

459 

460 @classmethod 

461 def create_for_statement( 

462 cls, 

463 statement: Executable, 

464 compiler: SQLCompiler, 

465 **kw: Any, 

466 ) -> _ORMCompileState: 

467 return cls._create_orm_context( 

468 cast("Union[Select, FromStatement]", statement), 

469 toplevel=not compiler.stack, 

470 compiler=compiler, 

471 **kw, 

472 ) 

473 

474 @classmethod 

475 def _create_orm_context( 

476 cls, 

477 statement: Union[Select, FromStatement], 

478 *, 

479 toplevel: bool, 

480 compiler: Optional[SQLCompiler], 

481 **kw: Any, 

482 ) -> _ORMCompileState: 

483 raise NotImplementedError() 

484 

485 def _append_dedupe_col_collection(self, obj, col_collection): 

486 dedupe = self.dedupe_columns 

487 if obj not in dedupe: 

488 dedupe.add(obj) 

489 col_collection.append(obj) 

490 

491 @classmethod 

492 def _column_naming_convention( 

493 cls, label_style: SelectLabelStyle, legacy: bool 

494 ) -> _LabelConventionCallable: 

495 if legacy: 

496 

497 def name(col, col_name=None): 

498 if col_name: 

499 return col_name 

500 else: 

501 return getattr(col, "key") 

502 

503 return name 

504 else: 

505 return SelectState._column_naming_convention(label_style) 

506 

507 @classmethod 

508 def get_column_descriptions(cls, statement): 

509 return _column_descriptions(statement) 

510 

511 @classmethod 

512 def orm_pre_session_exec( 

513 cls, 

514 session, 

515 statement, 

516 params, 

517 execution_options, 

518 bind_arguments, 

519 is_pre_event, 

520 ): 

521 # consume result-level load_options. These may have been set up 

522 # in an ORMExecuteState hook 

523 ( 

524 load_options, 

525 execution_options, 

526 ) = QueryContext.default_load_options.from_execution_options( 

527 "_sa_orm_load_options", 

528 { 

529 "populate_existing", 

530 "autoflush", 

531 "yield_per", 

532 "identity_token", 

533 "sa_top_level_orm_context", 

534 }, 

535 execution_options, 

536 statement._execution_options, 

537 ) 

538 

539 # default execution options for ORM results: 

540 # 1. _result_disable_adapt_to_context=True 

541 # this will disable the ResultSetMetadata._adapt_to_context() 

542 # step which we don't need, as we have result processors cached 

543 # against the original SELECT statement before caching. 

544 

545 if "sa_top_level_orm_context" in execution_options: 

546 ctx = execution_options["sa_top_level_orm_context"] 

547 execution_options = ctx.query._execution_options.merge_with( 

548 ctx.execution_options, execution_options 

549 ) 

550 

551 if not execution_options: 

552 execution_options = _orm_load_exec_options 

553 else: 

554 execution_options = execution_options.union(_orm_load_exec_options) 

555 

556 # would have been placed here by legacy Query only 

557 if load_options._yield_per: 

558 execution_options = execution_options.union( 

559 {"yield_per": load_options._yield_per} 

560 ) 

561 

562 if ( 

563 getattr(statement._compile_options, "_current_path", None) 

564 and len(statement._compile_options._current_path) > 10 

565 and execution_options.get("compiled_cache", True) is not None 

566 ): 

567 execution_options: util.immutabledict[str, Any] = ( 

568 execution_options.union( 

569 { 

570 "compiled_cache": None, 

571 "_cache_disable_reason": "excess depth for " 

572 "ORM loader options", 

573 } 

574 ) 

575 ) 

576 

577 bind_arguments["clause"] = statement 

578 

579 # new in 1.4 - the coercions system is leveraged to allow the 

580 # "subject" mapper of a statement be propagated to the top 

581 # as the statement is built. "subject" mapper is the generally 

582 # standard object used as an identifier for multi-database schemes. 

583 

584 # we are here based on the fact that _propagate_attrs contains 

585 # "compile_state_plugin": "orm". The "plugin_subject" 

586 # needs to be present as well. 

587 

588 try: 

589 plugin_subject = statement._propagate_attrs["plugin_subject"] 

590 except KeyError: 

591 assert False, "statement had 'orm' plugin but no plugin_subject" 

592 else: 

593 if plugin_subject: 

594 bind_arguments["mapper"] = plugin_subject.mapper 

595 

596 if not is_pre_event and load_options._autoflush: 

597 session._autoflush() 

598 

599 return statement, execution_options, params 

600 

601 @classmethod 

602 def orm_setup_cursor_result( 

603 cls, 

604 session, 

605 statement, 

606 params, 

607 execution_options, 

608 bind_arguments, 

609 result, 

610 ): 

611 execution_context = result.context 

612 compile_state = execution_context.compiled.compile_state 

613 

614 # cover edge case where ORM entities used in legacy select 

615 # were passed to session.execute: 

616 # session.execute(legacy_select([User.id, User.name])) 

617 # see test_query->test_legacy_tuple_old_select 

618 

619 load_options = execution_options.get( 

620 "_sa_orm_load_options", QueryContext.default_load_options 

621 ) 

622 

623 if compile_state.compile_options._is_star: 

624 return result 

625 

626 querycontext = QueryContext( 

627 compile_state, 

628 statement, 

629 statement, 

630 params, 

631 session, 

632 load_options, 

633 execution_options, 

634 bind_arguments, 

635 ) 

636 return loading.instances(result, querycontext) 

637 

638 @property 

639 def _lead_mapper_entities(self): 

640 """return all _MapperEntity objects in the lead entities collection. 

641 

642 Does **not** include entities that have been replaced by 

643 with_entities(), with_only_columns() 

644 

645 """ 

646 return [ 

647 ent for ent in self._entities if isinstance(ent, _MapperEntity) 

648 ] 

649 

650 def _create_with_polymorphic_adapter(self, ext_info, selectable): 

651 """given MapperEntity or ORMColumnEntity, setup polymorphic loading 

652 if called for by the Mapper. 

653 

654 As of #8168 in 2.0.0rc1, polymorphic adapters, which greatly increase 

655 the complexity of the query creation process, are not used at all 

656 except in the quasi-legacy cases of with_polymorphic referring to an 

657 alias and/or subquery. This would apply to concrete polymorphic 

658 loading, and joined inheritance where a subquery is 

659 passed to with_polymorphic (which is completely unnecessary in modern 

660 use). 

661 

662 TODO: What is a "quasi-legacy" case? Do we need this method with 

663 2.0 style select() queries or not? Why is with_polymorphic referring 

664 to an alias or subquery "legacy" ? 

665 

666 """ 

667 if ( 

668 not ext_info.is_aliased_class 

669 and ext_info.mapper.persist_selectable 

670 not in self._polymorphic_adapters 

671 ): 

672 for mp in ext_info.mapper.iterate_to_root(): 

673 self._mapper_loads_polymorphically_with( 

674 mp, 

675 ORMAdapter( 

676 _TraceAdaptRole.WITH_POLYMORPHIC_ADAPTER, 

677 mp, 

678 equivalents=mp._equivalent_columns, 

679 selectable=selectable, 

680 ), 

681 ) 

682 

683 def _mapper_loads_polymorphically_with(self, mapper, adapter): 

684 for m2 in mapper._with_polymorphic_mappers or [mapper]: 

685 self._polymorphic_adapters[m2] = adapter 

686 

687 for m in m2.iterate_to_root(): 

688 self._polymorphic_adapters[m.local_table] = adapter 

689 

690 @classmethod 

691 def _create_entities_collection(cls, query, legacy): 

692 raise NotImplementedError( 

693 "this method only works for ORMSelectCompileState" 

694 ) 

695 

696 

697class _DMLReturningColFilter: 

698 """a base for an adapter used for the DML RETURNING cases 

699 

700 Has a subset of the interface used by 

701 :class:`.ORMAdapter` and is used for :class:`._QueryEntity` 

702 instances to set up their columns as used in RETURNING for a 

703 DML statement. 

704 

705 """ 

706 

707 __slots__ = ("mapper", "columns", "__weakref__") 

708 

709 def __init__(self, target_mapper, immediate_dml_mapper): 

710 if ( 

711 immediate_dml_mapper is not None 

712 and target_mapper.local_table 

713 is not immediate_dml_mapper.local_table 

714 ): 

715 # joined inh, or in theory other kinds of multi-table mappings 

716 self.mapper = immediate_dml_mapper 

717 else: 

718 # single inh, normal mappings, etc. 

719 self.mapper = target_mapper 

720 self.columns = self.columns = util.WeakPopulateDict( 

721 self.adapt_check_present # type: ignore 

722 ) 

723 

724 def __call__(self, col, as_filter): 

725 for cc in sql_util._find_columns(col): 

726 c2 = self.adapt_check_present(cc) 

727 if c2 is not None: 

728 return col 

729 else: 

730 return None 

731 

732 def adapt_check_present(self, col): 

733 raise NotImplementedError() 

734 

735 

736class _DMLBulkInsertReturningColFilter(_DMLReturningColFilter): 

737 """an adapter used for the DML RETURNING case specifically 

738 for ORM bulk insert (or any hypothetical DML that is splitting out a class 

739 hierarchy among multiple DML statements....ORM bulk insert is the only 

740 example right now) 

741 

742 its main job is to limit the columns in a RETURNING to only a specific 

743 mapped table in a hierarchy. 

744 

745 """ 

746 

747 def adapt_check_present(self, col): 

748 mapper = self.mapper 

749 prop = mapper._columntoproperty.get(col, None) 

750 if prop is None: 

751 return None 

752 return mapper.local_table.c.corresponding_column(col) 

753 

754 

755class _DMLUpdateDeleteReturningColFilter(_DMLReturningColFilter): 

756 """an adapter used for the DML RETURNING case specifically 

757 for ORM enabled UPDATE/DELETE 

758 

759 its main job is to limit the columns in a RETURNING to include 

760 only direct persisted columns from the immediate selectable, not 

761 expressions like column_property(), or to also allow columns from other 

762 mappers for the UPDATE..FROM use case. 

763 

764 """ 

765 

766 def adapt_check_present(self, col): 

767 mapper = self.mapper 

768 prop = mapper._columntoproperty.get(col, None) 

769 if prop is not None: 

770 # if the col is from the immediate mapper, only return a persisted 

771 # column, not any kind of column_property expression 

772 return mapper.persist_selectable.c.corresponding_column(col) 

773 

774 # if the col is from some other mapper, just return it, assume the 

775 # user knows what they are doing 

776 return col 

777 

778 

779@sql.base.CompileState.plugin_for("orm", "orm_from_statement") 

780class _ORMFromStatementCompileState(_ORMCompileState): 

781 _from_obj_alias = None 

782 _has_mapper_entities = False 

783 

784 statement_container: FromStatement 

785 requested_statement: Union[SelectBase, TextClause, UpdateBase] 

786 dml_table: Optional[_DMLTableElement] = None 

787 

788 _has_orm_entities = False 

789 multi_row_eager_loaders = False 

790 eager_adding_joins = False 

791 compound_eager_adapter = None 

792 

793 extra_criteria_entities = util.EMPTY_DICT 

794 eager_joins = util.EMPTY_DICT 

795 

796 @classmethod 

797 def _create_orm_context( 

798 cls, 

799 statement: Union[Select, FromStatement], 

800 *, 

801 toplevel: bool, 

802 compiler: Optional[SQLCompiler], 

803 **kw: Any, 

804 ) -> _ORMFromStatementCompileState: 

805 statement_container = statement 

806 

807 assert isinstance(statement_container, FromStatement) 

808 

809 if compiler is not None and compiler.stack: 

810 raise sa_exc.CompileError( 

811 "The ORM FromStatement construct only supports being " 

812 "invoked as the topmost statement, as it is only intended to " 

813 "define how result rows should be returned." 

814 ) 

815 

816 self = cls.__new__(cls) 

817 self._primary_entity = None 

818 

819 self.use_legacy_query_style = ( 

820 statement_container._compile_options._use_legacy_query_style 

821 ) 

822 self.statement_container = self.select_statement = statement_container 

823 self.requested_statement = statement = statement_container.element 

824 

825 if statement.is_dml: 

826 self.dml_table = statement.table 

827 self.is_dml_returning = True 

828 

829 self._entities = [] 

830 self._polymorphic_adapters = {} 

831 

832 self.compile_options = statement_container._compile_options 

833 

834 if ( 

835 self.use_legacy_query_style 

836 and isinstance(statement, expression.SelectBase) 

837 and not statement._is_textual 

838 and not statement.is_dml 

839 and statement._label_style is LABEL_STYLE_NONE 

840 ): 

841 self.statement = statement.set_label_style( 

842 LABEL_STYLE_TABLENAME_PLUS_COL 

843 ) 

844 else: 

845 self.statement = statement 

846 

847 self._label_convention = self._column_naming_convention( 

848 ( 

849 statement._label_style 

850 if not statement._is_textual and not statement.is_dml 

851 else LABEL_STYLE_NONE 

852 ), 

853 self.use_legacy_query_style, 

854 ) 

855 

856 _QueryEntity.to_compile_state( 

857 self, 

858 statement_container._raw_columns, 

859 self._entities, 

860 is_current_entities=True, 

861 ) 

862 

863 self.current_path = statement_container._compile_options._current_path 

864 

865 self._init_global_attributes( 

866 statement_container, 

867 compiler, 

868 process_criteria_for_toplevel=False, 

869 toplevel=True, 

870 ) 

871 

872 if statement_container._with_options: 

873 for opt in statement_container._with_options: 

874 if opt._is_compile_state: 

875 opt.process_compile_state(self) 

876 

877 if statement_container._compile_state_funcs: 

878 for fn, key in statement_container._compile_state_funcs: 

879 fn(self) 

880 

881 self.primary_columns = [] 

882 self.secondary_columns = [] 

883 self.dedupe_columns = set() 

884 self.create_eager_joins = [] 

885 self._fallback_from_clauses = [] 

886 

887 self.order_by = None 

888 

889 if self.statement._is_text_clause: 

890 # AbstractTextClause (TextClause, TString) has no "column" 

891 # objects at all. for this case, we generate columns from our 

892 # _QueryEntity objects, then flip on all the 

893 # "please match no matter what" parameters. 

894 self.extra_criteria_entities = {} 

895 

896 for entity in self._entities: 

897 entity.setup_compile_state(self) 

898 

899 compiler._ordered_columns = compiler._textual_ordered_columns = ( 

900 False 

901 ) 

902 

903 # enable looser result column matching. this is shown to be 

904 # needed by test_query.py::TextTest 

905 compiler._loose_column_name_matching = True 

906 

907 for c in self.primary_columns: 

908 compiler.process( 

909 c, 

910 within_columns_clause=True, 

911 add_to_result_map=compiler._add_to_result_map, 

912 ) 

913 else: 

914 # for everyone else, Select, Insert, Update, TextualSelect, they 

915 # have column objects already. After much 

916 # experimentation here, the best approach seems to be, use 

917 # those columns completely, don't interfere with the compiler 

918 # at all; just in ORM land, use an adapter to convert from 

919 # our ORM columns to whatever columns are in the statement, 

920 # before we look in the result row. Adapt on names 

921 # to accept cases such as issue #9217, however also allow 

922 # this to be overridden for cases such as #9273. 

923 self._from_obj_alias = ORMStatementAdapter( 

924 _TraceAdaptRole.ADAPT_FROM_STATEMENT, 

925 self.statement, 

926 adapt_on_names=statement_container._adapt_on_names, 

927 ) 

928 

929 return self 

930 

931 def _adapt_col_list(self, cols, current_adapter): 

932 return cols 

933 

934 def _get_current_adapter(self): 

935 return None 

936 

937 def setup_dml_returning_compile_state(self, dml_mapper): 

938 """used by BulkORMInsert, Update, Delete to set up a handler 

939 for RETURNING to return ORM objects and expressions 

940 

941 """ 

942 target_mapper = self.statement._propagate_attrs.get( 

943 "plugin_subject", None 

944 ) 

945 

946 if self.statement.is_insert: 

947 adapter = _DMLBulkInsertReturningColFilter( 

948 target_mapper, dml_mapper 

949 ) 

950 elif self.statement.is_update or self.statement.is_delete: 

951 adapter = _DMLUpdateDeleteReturningColFilter( 

952 target_mapper, dml_mapper 

953 ) 

954 else: 

955 adapter = None 

956 

957 if self.compile_options._is_star and (len(self._entities) != 1): 

958 raise sa_exc.CompileError( 

959 "Can't generate ORM query that includes multiple expressions " 

960 "at the same time as '*'; query for '*' alone if present" 

961 ) 

962 

963 for entity in self._entities: 

964 entity.setup_dml_returning_compile_state(self, adapter) 

965 

966 

967class FromStatement(GroupedElement, Generative, TypedReturnsRows[Unpack[_Ts]]): 

968 """Core construct that represents a load of ORM objects from various 

969 :class:`.ReturnsRows` and other classes including: 

970 

971 :class:`.Select`, :class:`.TextClause`, :class:`.TextualSelect`, 

972 :class:`.CompoundSelect`, :class`.Insert`, :class:`.Update`, 

973 and in theory, :class:`.Delete`. 

974 

975 """ 

976 

977 __visit_name__ = "orm_from_statement" 

978 

979 _compile_options = _ORMFromStatementCompileState.default_compile_options 

980 

981 _compile_state_factory = _ORMFromStatementCompileState.create_for_statement 

982 

983 _for_update_arg = None 

984 

985 element: Union[ExecutableReturnsRows, TextClause] 

986 

987 _adapt_on_names: bool 

988 

989 _traverse_internals = [ 

990 ("_raw_columns", InternalTraversal.dp_clauseelement_list), 

991 ("element", InternalTraversal.dp_clauseelement), 

992 ] + ExecutableStatement._executable_traverse_internals 

993 

994 _cache_key_traversal = _traverse_internals + [ 

995 ("_compile_options", InternalTraversal.dp_has_cache_key) 

996 ] 

997 

998 is_from_statement = True 

999 

1000 def __init__( 

1001 self, 

1002 entities: Iterable[_ColumnsClauseArgument[Any]], 

1003 element: Union[ExecutableReturnsRows, TextClause], 

1004 _adapt_on_names: bool = True, 

1005 ): 

1006 self._raw_columns = [ 

1007 coercions.expect( 

1008 roles.ColumnsClauseRole, 

1009 ent, 

1010 apply_propagate_attrs=self, 

1011 post_inspect=True, 

1012 ) 

1013 for ent in util.to_list(entities) 

1014 ] 

1015 self.element = element 

1016 self.is_dml = element.is_dml 

1017 self.is_select = element.is_select 

1018 self.is_delete = element.is_delete 

1019 self.is_insert = element.is_insert 

1020 self.is_update = element.is_update 

1021 self._label_style = ( 

1022 element._label_style if is_select_base(element) else None 

1023 ) 

1024 self._adapt_on_names = _adapt_on_names 

1025 

1026 def _compiler_dispatch(self, compiler, **kw): 

1027 """provide a fixed _compiler_dispatch method. 

1028 

1029 This is roughly similar to using the sqlalchemy.ext.compiler 

1030 ``@compiles`` extension. 

1031 

1032 """ 

1033 

1034 if compiler._collect_params: 

1035 compiler._add_to_params(self) 

1036 

1037 compile_state = self._compile_state_factory(self, compiler, **kw) 

1038 

1039 toplevel = not compiler.stack 

1040 

1041 if toplevel: 

1042 compiler.compile_state = compile_state 

1043 

1044 return compiler.process(compile_state.statement, **kw) 

1045 

1046 @property 

1047 def column_descriptions(self): 

1048 """Return a :term:`plugin-enabled` 'column descriptions' structure 

1049 referring to the columns which are SELECTed by this statement. 

1050 

1051 See the section :ref:`queryguide_inspection` for an overview 

1052 of this feature. 

1053 

1054 .. seealso:: 

1055 

1056 :ref:`queryguide_inspection` - ORM background 

1057 

1058 """ 

1059 meth = cast( 

1060 _ORMSelectCompileState, SelectState.get_plugin_class(self) 

1061 ).get_column_descriptions 

1062 return meth(self) 

1063 

1064 def _ensure_disambiguated_names(self): 

1065 return self 

1066 

1067 def get_children(self, **kw): 

1068 yield from itertools.chain.from_iterable( 

1069 element._from_objects for element in self._raw_columns 

1070 ) 

1071 yield from super().get_children(**kw) 

1072 

1073 @property 

1074 def _all_selected_columns(self): 

1075 return self.element._all_selected_columns 

1076 

1077 @property 

1078 def _return_defaults(self): 

1079 return self.element._return_defaults if is_dml(self.element) else None 

1080 

1081 @property 

1082 def _returning(self): 

1083 return self.element._returning if is_dml(self.element) else None 

1084 

1085 @property 

1086 def _inline(self): 

1087 return self.element._inline if is_insert_update(self.element) else None 

1088 

1089 

1090@sql.base.CompileState.plugin_for("orm", "compound_select") 

1091class _CompoundSelectCompileState( 

1092 _AutoflushOnlyORMCompileState, CompoundSelectState 

1093): 

1094 pass 

1095 

1096 

1097@sql.base.CompileState.plugin_for("orm", "select") 

1098class _ORMSelectCompileState(_ORMCompileState, SelectState): 

1099 _already_joined_edges = () 

1100 

1101 _memoized_entities = util.EMPTY_DICT 

1102 

1103 _from_obj_alias = None 

1104 _has_mapper_entities = False 

1105 

1106 _has_orm_entities = False 

1107 multi_row_eager_loaders = False 

1108 eager_adding_joins = False 

1109 compound_eager_adapter = None 

1110 

1111 correlate = None 

1112 correlate_except = None 

1113 _where_criteria = () 

1114 _having_criteria = () 

1115 

1116 # see SQLCompiler._enclosing_correlate_froms() 

1117 _explicit_correlate_froms: AbstractSet[FromClause] = util.EMPTY_SET 

1118 _implicit_correlate_froms: AbstractSet[FromClause] = util.EMPTY_SET 

1119 

1120 # with_loader_criteria() options whose criteria contain this SELECT, 

1121 # either directly or within an enclosing SELECT; see 

1122 # LoaderCriteriaOption._should_include() 

1123 _containing_loader_criteria: FrozenSet[LoaderCriteriaOption] = ( 

1124 util.EMPTY_SET 

1125 ) 

1126 

1127 @classmethod 

1128 def _create_orm_context( 

1129 cls, 

1130 statement: Union[Select, FromStatement], 

1131 *, 

1132 toplevel: bool, 

1133 compiler: Optional[SQLCompiler], 

1134 **kw: Any, 

1135 ) -> _ORMSelectCompileState: 

1136 

1137 self = cls.__new__(cls) 

1138 

1139 if not toplevel: 

1140 assert compiler is not None 

1141 ( 

1142 self._explicit_correlate_froms, 

1143 self._implicit_correlate_froms, 

1144 ) = compiler._enclosing_correlate_froms( 

1145 compiler.stack[-1], 

1146 kw.get("asfrom", False), 

1147 kw.get("lateral", False), 

1148 ) 

1149 

1150 # the nearest enclosing ORM SELECT has already collected the 

1151 # options of all the SELECTs enclosing it 

1152 for entry in reversed(compiler.stack): 

1153 enclosing_state = entry.get("compile_state") 

1154 if isinstance(enclosing_state, _ORMSelectCompileState): 

1155 self._containing_loader_criteria = ( 

1156 enclosing_state._containing_loader_criteria 

1157 ) 

1158 break 

1159 

1160 if "for_loader_criteria" in statement._annotations: 

1161 self._containing_loader_criteria |= { 

1162 statement._annotations["for_loader_criteria"] 

1163 } 

1164 

1165 select_statement = statement 

1166 

1167 # if we are a select() that was never a legacy Query, we won't 

1168 # have ORM level compile options. 

1169 statement._compile_options = cls.default_compile_options.safe_merge( 

1170 statement._compile_options 

1171 ) 

1172 

1173 if select_statement._execution_options: 

1174 # execution options should not impact the compilation of a 

1175 # query, and at the moment subqueryloader is putting some things 

1176 # in here that we explicitly don't want stuck in a cache. 

1177 self.select_statement = select_statement._clone() 

1178 self.select_statement._execution_options = util.EMPTY_DICT 

1179 else: 

1180 self.select_statement = select_statement 

1181 

1182 # indicates this select() came from Query.statement 

1183 self.for_statement = select_statement._compile_options._for_statement 

1184 

1185 # generally if we are from Query or directly from a select() 

1186 self.use_legacy_query_style = ( 

1187 select_statement._compile_options._use_legacy_query_style 

1188 ) 

1189 

1190 self._entities = [] 

1191 self._primary_entity = None 

1192 self._polymorphic_adapters = {} 

1193 

1194 self.compile_options = select_statement._compile_options 

1195 

1196 if not toplevel: 

1197 # for subqueries, turn off eagerloads and set 

1198 # "render_for_subquery". 

1199 self.compile_options += { 

1200 "_enable_eagerloads": False, 

1201 "_render_for_subquery": True, 

1202 } 

1203 

1204 # determine label style. we can make different decisions here. 

1205 # at the moment, trying to see if we can always use DISAMBIGUATE_ONLY 

1206 # rather than LABEL_STYLE_NONE, and if we can use disambiguate style 

1207 # for new style ORM selects too. 

1208 if ( 

1209 self.use_legacy_query_style 

1210 and self.select_statement._label_style is LABEL_STYLE_LEGACY_ORM 

1211 ): 

1212 if not self.for_statement: 

1213 self.label_style = LABEL_STYLE_TABLENAME_PLUS_COL 

1214 else: 

1215 self.label_style = LABEL_STYLE_DISAMBIGUATE_ONLY 

1216 else: 

1217 self.label_style = self.select_statement._label_style 

1218 

1219 if select_statement._memoized_select_entities: 

1220 self._memoized_entities = { 

1221 memoized_entities: _QueryEntity.to_compile_state( 

1222 self, 

1223 memoized_entities._raw_columns, 

1224 [], 

1225 is_current_entities=False, 

1226 ) 

1227 for memoized_entities in ( 

1228 select_statement._memoized_select_entities 

1229 ) 

1230 } 

1231 

1232 # label_convention is stateful and will yield deduping keys if it 

1233 # sees the same key twice. therefore it's important that it is not 

1234 # invoked for the above "memoized" entities that aren't actually 

1235 # in the columns clause 

1236 self._label_convention = self._column_naming_convention( 

1237 statement._label_style, self.use_legacy_query_style 

1238 ) 

1239 

1240 _QueryEntity.to_compile_state( 

1241 self, 

1242 select_statement._raw_columns, 

1243 self._entities, 

1244 is_current_entities=True, 

1245 ) 

1246 

1247 self.current_path = select_statement._compile_options._current_path 

1248 

1249 self.eager_order_by = () 

1250 

1251 self._init_global_attributes( 

1252 select_statement, 

1253 compiler, 

1254 toplevel=toplevel, 

1255 process_criteria_for_toplevel=False, 

1256 ) 

1257 

1258 if toplevel and ( 

1259 select_statement._with_options 

1260 or select_statement._memoized_select_entities 

1261 ): 

1262 for ( 

1263 memoized_entities 

1264 ) in select_statement._memoized_select_entities: 

1265 for opt in memoized_entities._with_options: 

1266 if opt._is_compile_state: 

1267 opt.process_compile_state_replaced_entities( 

1268 self, 

1269 [ 

1270 ent 

1271 for ent in self._memoized_entities[ 

1272 memoized_entities 

1273 ] 

1274 if isinstance(ent, _MapperEntity) 

1275 ], 

1276 ) 

1277 

1278 for opt in self.select_statement._with_options: 

1279 if opt._is_compile_state: 

1280 opt.process_compile_state(self) 

1281 

1282 # uncomment to print out the context.attributes structure 

1283 # after it's been set up above 

1284 # self._dump_option_struct() 

1285 

1286 if select_statement._compile_state_funcs: 

1287 for fn, key in select_statement._compile_state_funcs: 

1288 fn(self) 

1289 

1290 self.primary_columns = [] 

1291 self.secondary_columns = [] 

1292 self.dedupe_columns = set() 

1293 self.eager_joins = {} 

1294 self.extra_criteria_entities = {} 

1295 self.create_eager_joins = [] 

1296 self._fallback_from_clauses = [] 

1297 

1298 # normalize the FROM clauses early by themselves, as this makes 

1299 # it an easier job when we need to assemble a JOIN onto these, 

1300 # for select.join() as well as joinedload(). As of 1.4 there are now 

1301 # potentially more complex sets of FROM objects here as the use 

1302 # of lambda statements for lazyload, load_on_pk etc. uses more 

1303 # cloning of the select() construct. See #6495 

1304 self.from_clauses = self._normalize_froms( 

1305 info.selectable for info in select_statement._from_obj 

1306 ) 

1307 

1308 # this is a fairly arbitrary break into a second method, 

1309 # so it might be nicer to break up create_for_statement() 

1310 # and _setup_for_generate into three or four logical sections 

1311 self._setup_for_generate() 

1312 

1313 SelectState.__init__(self, self.statement, compiler, **kw) 

1314 return self 

1315 

1316 def _dump_option_struct(self): 

1317 print("\n---------------------------------------------------\n") 

1318 print(f"current path: {self.current_path}") 

1319 for key in self.attributes: 

1320 if isinstance(key, tuple) and key[0] == "loader": 

1321 print(f"\nLoader: {PathRegistry.coerce(key[1])}") 

1322 print(f" {self.attributes[key]}") 

1323 print(f" {self.attributes[key].__dict__}") 

1324 elif isinstance(key, tuple) and key[0] == "path_with_polymorphic": 

1325 print(f"\nWith Polymorphic: {PathRegistry.coerce(key[1])}") 

1326 print(f" {self.attributes[key]}") 

1327 

1328 def _setup_for_generate(self): 

1329 query = self.select_statement 

1330 

1331 self.statement = None 

1332 self._join_entities = () 

1333 

1334 if self.compile_options._set_base_alias: 

1335 # legacy Query only 

1336 self._set_select_from_alias() 

1337 

1338 for memoized_entities in query._memoized_select_entities: 

1339 if memoized_entities._setup_joins: 

1340 self._join( 

1341 memoized_entities._setup_joins, 

1342 self._memoized_entities[memoized_entities], 

1343 ) 

1344 

1345 if query._setup_joins: 

1346 self._join(query._setup_joins, self._entities) 

1347 

1348 current_adapter = self._get_current_adapter() 

1349 

1350 if query._where_criteria: 

1351 self._where_criteria = query._where_criteria 

1352 

1353 if current_adapter: 

1354 self._where_criteria = tuple( 

1355 current_adapter(crit, True) 

1356 for crit in self._where_criteria 

1357 ) 

1358 

1359 # TODO: some complexity with order_by here was due to mapper.order_by. 

1360 # now that this is removed we can hopefully make order_by / 

1361 # group_by act identically to how they are in Core select. 

1362 self.order_by = ( 

1363 self._adapt_col_list(query._order_by_clauses, current_adapter) 

1364 if current_adapter and query._order_by_clauses not in (None, False) 

1365 else query._order_by_clauses 

1366 ) 

1367 

1368 if query._having_criteria: 

1369 self._having_criteria = tuple( 

1370 current_adapter(crit, True) if current_adapter else crit 

1371 for crit in query._having_criteria 

1372 ) 

1373 

1374 self.group_by = ( 

1375 self._adapt_col_list( 

1376 util.flatten_iterator(query._group_by_clauses), current_adapter 

1377 ) 

1378 if current_adapter and query._group_by_clauses not in (None, False) 

1379 else query._group_by_clauses or None 

1380 ) 

1381 

1382 if self.eager_order_by: 

1383 adapter = self.from_clauses[0]._target_adapter 

1384 self.eager_order_by = adapter.copy_and_process(self.eager_order_by) 

1385 

1386 if query._distinct_on: 

1387 self.distinct_on = self._adapt_col_list( 

1388 query._distinct_on, current_adapter 

1389 ) 

1390 else: 

1391 self.distinct_on = () 

1392 

1393 self.distinct = query._distinct 

1394 

1395 self.syntax_extensions = { 

1396 key: current_adapter(value, True) if current_adapter else value 

1397 for key, value in query._get_syntax_extensions_as_dict().items() 

1398 } 

1399 

1400 if query._correlate: 

1401 # ORM mapped entities that are mapped to joins can be passed 

1402 # to .correlate, so here they are broken into their component 

1403 # tables. 

1404 self.correlate = tuple( 

1405 util.flatten_iterator( 

1406 sql_util.surface_selectables(s) if s is not None else None 

1407 for s in query._correlate 

1408 ) 

1409 ) 

1410 elif query._correlate_except is not None: 

1411 self.correlate_except = tuple( 

1412 util.flatten_iterator( 

1413 sql_util.surface_selectables(s) if s is not None else None 

1414 for s in query._correlate_except 

1415 ) 

1416 ) 

1417 elif not query._auto_correlate: 

1418 self.correlate = (None,) 

1419 

1420 # PART II 

1421 

1422 self._for_update_arg = query._for_update_arg 

1423 

1424 if self.compile_options._is_star and (len(self._entities) != 1): 

1425 raise sa_exc.CompileError( 

1426 "Can't generate ORM query that includes multiple expressions " 

1427 "at the same time as '*'; query for '*' alone if present" 

1428 ) 

1429 for entity in self._entities: 

1430 entity.setup_compile_state(self) 

1431 

1432 for rec in self.create_eager_joins: 

1433 strategy = rec[0] 

1434 strategy(self, *rec[1:]) 

1435 

1436 # else "load from discrete FROMs" mode, 

1437 # i.e. when each _MappedEntity has its own FROM 

1438 

1439 if self.compile_options._enable_single_crit: 

1440 self._adjust_for_extra_criteria() 

1441 

1442 if not self.primary_columns: 

1443 if self.compile_options._only_load_props: 

1444 assert False, "no columns were included in _only_load_props" 

1445 

1446 raise sa_exc.InvalidRequestError( 

1447 "Query contains no columns with which to SELECT from." 

1448 ) 

1449 

1450 if not self.from_clauses: 

1451 self.from_clauses = list(self._fallback_from_clauses) 

1452 

1453 if self.order_by is False: 

1454 self.order_by = None 

1455 

1456 if self._should_nest_selectable: 

1457 self.statement = self._compound_eager_statement() 

1458 else: 

1459 self.statement = self._simple_statement() 

1460 

1461 if self.for_statement: 

1462 ezero = self._mapper_zero() 

1463 if ezero is not None: 

1464 # TODO: this goes away once we get rid of the deep entity 

1465 # thing 

1466 self.statement = self.statement._annotate( 

1467 {"deepentity": ezero} 

1468 ) 

1469 

1470 @classmethod 

1471 def _create_entities_collection(cls, query, legacy): 

1472 """Creates a partial ORMSelectCompileState that includes 

1473 the full collection of _MapperEntity and other _QueryEntity objects. 

1474 

1475 Supports a few remaining use cases that are pre-compilation 

1476 but still need to gather some of the column / adaption information. 

1477 

1478 """ 

1479 self = cls.__new__(cls) 

1480 

1481 self._entities = [] 

1482 self._primary_entity = None 

1483 self._polymorphic_adapters = {} 

1484 

1485 self._label_convention = self._column_naming_convention( 

1486 query._label_style, legacy 

1487 ) 

1488 

1489 # entities will also set up polymorphic adapters for mappers 

1490 # that have with_polymorphic configured 

1491 _QueryEntity.to_compile_state( 

1492 self, query._raw_columns, self._entities, is_current_entities=True 

1493 ) 

1494 return self 

1495 

1496 @classmethod 

1497 def _get_filter_by_entities(cls, statement): 

1498 """Return all ORM entities for filter_by() searches. 

1499 

1500 the ORM version for Select is special vs. update/delete since it needs 

1501 to navigate along select.join() paths which have ORM specific 

1502 directives. 

1503 

1504 beyond that, it delivers other entities as the Mapper or Aliased 

1505 object rather than the Table or Alias, which mostly affects 

1506 how error messages regarding ambiguous entities or entity not 

1507 found are rendered; class-specific attributes like hybrid, 

1508 column_property() etc. work either way since 

1509 _entity_namespace_key_search_all() uses _entity_namespace(). 

1510 

1511 DML Update and Delete objects, even though they also have filter_by() 

1512 and also accept ORM objects, don't use this routine since they 

1513 typically just have a single table, and if they have multiple tables 

1514 it's only via WHERE clause, which interestingly do not maintain ORM 

1515 annotations when used (that is, (User.name == 

1516 'foo').left.table._annotations is empty; the ORMness of User.name is 

1517 lost in the expression construction process, since we don't annotate 

1518 (copy) Column objects with ORM entities the way we do for Table. 

1519 

1520 .. versionadded:: 2.1 

1521 """ 

1522 

1523 def _setup_join_targets(collection): 

1524 for (target, *_) in collection: 

1525 if isinstance(target, attributes.QueryableAttribute): 

1526 yield target.entity 

1527 elif "_no_filter_by" not in target._annotations: 

1528 yield target 

1529 

1530 entities = set(_setup_join_targets(statement._setup_joins)) 

1531 

1532 for memoized in statement._memoized_select_entities: 

1533 entities.update(_setup_join_targets(memoized._setup_joins)) 

1534 

1535 entities.update( 

1536 ( 

1537 from_obj._annotations["parententity"] 

1538 if "parententity" in from_obj._annotations 

1539 else from_obj 

1540 ) 

1541 for from_obj in statement._from_obj 

1542 if "_no_filter_by" not in from_obj._annotations 

1543 ) 

1544 

1545 for element in statement._raw_columns: 

1546 if "entity_namespace" in element._annotations: 

1547 ens = element._annotations["entity_namespace"] 

1548 entities.add(ens) 

1549 elif "_no_filter_by" not in element._annotations: 

1550 entities.update(element._from_objects) 

1551 

1552 return entities 

1553 

1554 @classmethod 

1555 def all_selected_columns(cls, statement): 

1556 for element in statement._raw_columns: 

1557 if ( 

1558 element.is_selectable 

1559 and "entity_namespace" in element._annotations 

1560 ): 

1561 ens = element._annotations["entity_namespace"] 

1562 if not ens.is_mapper and not ens.is_aliased_class: 

1563 yield from _select_iterables([element]) 

1564 else: 

1565 yield from _select_iterables(ens._all_column_expressions) 

1566 else: 

1567 yield from _select_iterables([element]) 

1568 

1569 @classmethod 

1570 def get_columns_clause_froms(cls, statement): 

1571 return cls._normalize_froms( 

1572 itertools.chain.from_iterable( 

1573 ( 

1574 element._from_objects 

1575 if "parententity" not in element._annotations 

1576 else [ 

1577 element._annotations[ 

1578 "parententity" 

1579 ].__clause_element__() 

1580 ] 

1581 ) 

1582 for element in statement._raw_columns 

1583 ) 

1584 ) 

1585 

1586 @classmethod 

1587 def from_statement(cls, statement, from_statement): 

1588 from_statement = coercions.expect( 

1589 roles.ReturnsRowsRole, 

1590 from_statement, 

1591 apply_propagate_attrs=statement, 

1592 ) 

1593 

1594 stmt = FromStatement(statement._raw_columns, from_statement) 

1595 

1596 stmt.__dict__.update( 

1597 _with_options=statement._with_options, 

1598 _compile_state_funcs=statement._compile_state_funcs, 

1599 _execution_options=statement._execution_options, 

1600 _propagate_attrs=statement._propagate_attrs, 

1601 ) 

1602 return stmt 

1603 

1604 def _set_select_from_alias(self): 

1605 """used only for legacy Query cases""" 

1606 

1607 query = self.select_statement # query 

1608 

1609 assert self.compile_options._set_base_alias 

1610 assert len(query._from_obj) == 1 

1611 

1612 adapter = self._get_select_from_alias_from_obj(query._from_obj[0]) 

1613 if adapter: 

1614 self.compile_options += {"_enable_single_crit": False} 

1615 self._from_obj_alias = adapter 

1616 

1617 def _get_select_from_alias_from_obj(self, from_obj): 

1618 """used only for legacy Query cases""" 

1619 

1620 info = from_obj 

1621 

1622 if "parententity" in info._annotations: 

1623 info = info._annotations["parententity"] 

1624 

1625 if hasattr(info, "mapper"): 

1626 if not info.is_aliased_class: 

1627 raise sa_exc.ArgumentError( 

1628 "A selectable (FromClause) instance is " 

1629 "expected when the base alias is being set." 

1630 ) 

1631 else: 

1632 return info._adapter 

1633 

1634 elif isinstance(info.selectable, sql.selectable.AliasedReturnsRows): 

1635 equivs = self._all_equivs() 

1636 assert info is info.selectable 

1637 return ORMStatementAdapter( 

1638 _TraceAdaptRole.LEGACY_SELECT_FROM_ALIAS, 

1639 info.selectable, 

1640 equivalents=equivs, 

1641 ) 

1642 else: 

1643 return None 

1644 

1645 def _mapper_zero(self): 

1646 """return the Mapper associated with the first QueryEntity.""" 

1647 return self._entities[0].mapper 

1648 

1649 def _entity_zero(self): 

1650 """Return the 'entity' (mapper or AliasedClass) associated 

1651 with the first QueryEntity, or alternatively the 'select from' 

1652 entity if specified.""" 

1653 

1654 for ent in self.from_clauses: 

1655 if "parententity" in ent._annotations: 

1656 return ent._annotations["parententity"] 

1657 for qent in self._entities: 

1658 if qent.entity_zero: 

1659 return qent.entity_zero 

1660 

1661 return None 

1662 

1663 def _only_full_mapper_zero(self, methname): 

1664 if self._entities != [self._primary_entity]: 

1665 raise sa_exc.InvalidRequestError( 

1666 "%s() can only be used against " 

1667 "a single mapped class." % methname 

1668 ) 

1669 return self._primary_entity.entity_zero 

1670 

1671 def _only_entity_zero(self, rationale=None): 

1672 if len(self._entities) > 1: 

1673 raise sa_exc.InvalidRequestError( 

1674 rationale 

1675 or "This operation requires a Query " 

1676 "against a single mapper." 

1677 ) 

1678 return self._entity_zero() 

1679 

1680 def _all_equivs(self): 

1681 equivs = {} 

1682 

1683 for memoized_entities in self._memoized_entities.values(): 

1684 for ent in [ 

1685 ent 

1686 for ent in memoized_entities 

1687 if isinstance(ent, _MapperEntity) 

1688 ]: 

1689 equivs.update(ent.mapper._equivalent_columns) 

1690 

1691 for ent in [ 

1692 ent for ent in self._entities if isinstance(ent, _MapperEntity) 

1693 ]: 

1694 equivs.update(ent.mapper._equivalent_columns) 

1695 return equivs 

1696 

1697 def _compound_eager_statement(self): 

1698 # for eager joins present and LIMIT/OFFSET/DISTINCT, 

1699 # wrap the query inside a select, 

1700 # then append eager joins onto that 

1701 

1702 if self.order_by: 

1703 # the default coercion for ORDER BY is now the OrderByRole, 

1704 # which adds an additional post coercion to ByOfRole in that 

1705 # elements are converted into label references. For the 

1706 # eager load / subquery wrapping case, we need to un-coerce 

1707 # the original expressions outside of the label references 

1708 # in order to have them render. 

1709 unwrapped_order_by = [ 

1710 ( 

1711 elem.element 

1712 if isinstance(elem, sql.elements._label_reference) 

1713 else elem 

1714 ) 

1715 for elem in self.order_by 

1716 ] 

1717 

1718 order_by_col_expr = sql_util.expand_column_list_from_order_by( 

1719 self.primary_columns, unwrapped_order_by 

1720 ) 

1721 else: 

1722 order_by_col_expr = [] 

1723 unwrapped_order_by = None 

1724 

1725 # put FOR UPDATE on the inner query, where MySQL will honor it, 

1726 # as well as if it has an OF so PostgreSQL can use it. 

1727 inner = self._select_statement( 

1728 self.primary_columns 

1729 + [c for c in order_by_col_expr if c not in self.dedupe_columns], 

1730 self.from_clauses, 

1731 self._where_criteria, 

1732 self._having_criteria, 

1733 self.label_style, 

1734 self.order_by, 

1735 for_update=self._for_update_arg, 

1736 hints=self.select_statement._hints, 

1737 statement_hints=self.select_statement._statement_hints, 

1738 correlate=self.correlate, 

1739 correlate_except=self.correlate_except, 

1740 **self._select_args, 

1741 ) 

1742 

1743 inner = inner.alias() 

1744 

1745 equivs = self._all_equivs() 

1746 

1747 self.compound_eager_adapter = ORMStatementAdapter( 

1748 _TraceAdaptRole.COMPOUND_EAGER_STATEMENT, inner, equivalents=equivs 

1749 ) 

1750 

1751 statement = future.select( 

1752 *([inner] + self.secondary_columns) # use_labels=self.labels 

1753 ) 

1754 statement._label_style = self.label_style 

1755 

1756 # Oracle Database however does not allow FOR UPDATE on the subquery, 

1757 # and the Oracle Database dialects ignore it, plus for PostgreSQL, 

1758 # MySQL we expect that all elements of the row are locked, so also put 

1759 # it on the outside (except in the case of PG when OF is used) 

1760 if ( 

1761 self._for_update_arg is not None 

1762 and self._for_update_arg.of is None 

1763 ): 

1764 statement._for_update_arg = self._for_update_arg 

1765 

1766 from_clause = inner 

1767 for eager_join in self.eager_joins.values(): 

1768 # EagerLoader places a 'stop_on' attribute on the join, 

1769 # giving us a marker as to where the "splice point" of 

1770 # the join should be 

1771 from_clause = sql_util.splice_joins( 

1772 from_clause, eager_join, eager_join.stop_on 

1773 ) 

1774 

1775 statement.select_from.non_generative(statement, from_clause) 

1776 

1777 if unwrapped_order_by: 

1778 statement.order_by.non_generative( 

1779 statement, 

1780 *self.compound_eager_adapter.copy_and_process( 

1781 unwrapped_order_by 

1782 ), 

1783 ) 

1784 

1785 statement.order_by.non_generative(statement, *self.eager_order_by) 

1786 return statement 

1787 

1788 def _simple_statement(self): 

1789 statement = self._select_statement( 

1790 self.primary_columns + self.secondary_columns, 

1791 tuple(self.from_clauses) + tuple(self.eager_joins.values()), 

1792 self._where_criteria, 

1793 self._having_criteria, 

1794 self.label_style, 

1795 self.order_by, 

1796 for_update=self._for_update_arg, 

1797 hints=self.select_statement._hints, 

1798 statement_hints=self.select_statement._statement_hints, 

1799 correlate=self.correlate, 

1800 correlate_except=self.correlate_except, 

1801 **self._select_args, 

1802 ) 

1803 

1804 if self.eager_order_by: 

1805 statement.order_by.non_generative(statement, *self.eager_order_by) 

1806 return statement 

1807 

1808 def _select_statement( 

1809 self, 

1810 raw_columns, 

1811 from_obj, 

1812 where_criteria, 

1813 having_criteria, 

1814 label_style, 

1815 order_by, 

1816 for_update, 

1817 hints, 

1818 statement_hints, 

1819 correlate, 

1820 correlate_except, 

1821 limit_clause, 

1822 offset_clause, 

1823 fetch_clause, 

1824 fetch_clause_options, 

1825 distinct, 

1826 distinct_on, 

1827 prefixes, 

1828 suffixes, 

1829 group_by, 

1830 independent_ctes, 

1831 independent_ctes_opts, 

1832 syntax_extensions, 

1833 ): 

1834 statement = Select._create_raw_select( 

1835 _raw_columns=raw_columns, 

1836 _from_obj=from_obj, 

1837 _label_style=label_style, 

1838 ) 

1839 

1840 if where_criteria: 

1841 statement._where_criteria = where_criteria 

1842 if having_criteria: 

1843 statement._having_criteria = having_criteria 

1844 

1845 if order_by: 

1846 statement._order_by_clauses += tuple(order_by) 

1847 

1848 if distinct_on: 

1849 statement._distinct = True 

1850 statement._distinct_on = distinct_on 

1851 elif distinct: 

1852 statement._distinct = True 

1853 

1854 if group_by: 

1855 statement._group_by_clauses += tuple(group_by) 

1856 

1857 statement._limit_clause = limit_clause 

1858 statement._offset_clause = offset_clause 

1859 statement._fetch_clause = fetch_clause 

1860 statement._fetch_clause_options = fetch_clause_options 

1861 statement._independent_ctes = independent_ctes 

1862 statement._independent_ctes_opts = independent_ctes_opts 

1863 if syntax_extensions: 

1864 statement._set_syntax_extensions(**syntax_extensions) 

1865 

1866 if prefixes: 

1867 statement._prefixes = prefixes 

1868 

1869 if suffixes: 

1870 statement._suffixes = suffixes 

1871 

1872 statement._for_update_arg = for_update 

1873 

1874 if hints: 

1875 statement._hints = hints 

1876 if statement_hints: 

1877 statement._statement_hints = statement_hints 

1878 

1879 if correlate: 

1880 statement.correlate.non_generative(statement, *correlate) 

1881 

1882 if correlate_except is not None: 

1883 statement.correlate_except.non_generative( 

1884 statement, *correlate_except 

1885 ) 

1886 

1887 return statement 

1888 

1889 def _adapt_polymorphic_element(self, element): 

1890 if "parententity" in element._annotations: 

1891 search = element._annotations["parententity"] 

1892 alias = self._polymorphic_adapters.get(search, None) 

1893 if alias: 

1894 return alias.adapt_clause(element) 

1895 

1896 if isinstance(element, expression.FromClause): 

1897 search = element 

1898 elif hasattr(element, "table"): 

1899 search = element.table 

1900 else: 

1901 return None 

1902 

1903 alias = self._polymorphic_adapters.get(search, None) 

1904 if alias: 

1905 return alias.adapt_clause(element) 

1906 

1907 def _adapt_col_list(self, cols, current_adapter): 

1908 if current_adapter: 

1909 return [current_adapter(o, True) for o in cols] 

1910 else: 

1911 return cols 

1912 

1913 def _get_current_adapter(self): 

1914 adapters = [] 

1915 

1916 if self._from_obj_alias: 

1917 # used for legacy going forward for query set_ops, e.g. 

1918 # union(), union_all(), etc. 

1919 # 1.4 and previously, also used for from_self(), 

1920 # select_entity_from() 

1921 # 

1922 # for the "from obj" alias, apply extra rule to the 

1923 # 'ORM only' check, if this query were generated from a 

1924 # subquery of itself, i.e. _from_selectable(), apply adaption 

1925 # to all SQL constructs. 

1926 adapters.append( 

1927 self._from_obj_alias.replace, 

1928 ) 

1929 

1930 # this was *hopefully* the only adapter we were going to need 

1931 # going forward...however, we unfortunately need _from_obj_alias 

1932 # for query.union(), which we can't drop 

1933 if self._polymorphic_adapters: 

1934 adapters.append(self._adapt_polymorphic_element) 

1935 

1936 if not adapters: 

1937 return None 

1938 

1939 def _adapt_clause(clause, as_filter): 

1940 # do we adapt all expression elements or only those 

1941 # tagged as 'ORM' constructs ? 

1942 

1943 def replace(elem): 

1944 for adapter in adapters: 

1945 e = adapter(elem) 

1946 if e is not None: 

1947 return e 

1948 

1949 return visitors.replacement_traverse(clause, {}, replace) 

1950 

1951 return _adapt_clause 

1952 

1953 def _join(self, args, entities_collection): 

1954 for right, onclause, from_, flags in args: 

1955 isouter = flags["isouter"] 

1956 full = flags["full"] 

1957 

1958 right = inspect(right) 

1959 if onclause is not None: 

1960 onclause = inspect(onclause) 

1961 

1962 if isinstance(right, interfaces.PropComparator): 

1963 if onclause is not None: 

1964 raise sa_exc.InvalidRequestError( 

1965 "No 'on clause' argument may be passed when joining " 

1966 "to a relationship path as a target" 

1967 ) 

1968 

1969 onclause = right 

1970 right = None 

1971 elif "parententity" in right._annotations: 

1972 right = right._annotations["parententity"] 

1973 

1974 if onclause is None: 

1975 if not right.is_selectable and not hasattr(right, "mapper"): 

1976 raise sa_exc.ArgumentError( 

1977 "Expected mapped entity or " 

1978 "selectable/table as join target" 

1979 ) 

1980 

1981 if isinstance(onclause, interfaces.PropComparator): 

1982 # descriptor/property given (or determined); this tells us 

1983 # explicitly what the expected "left" side of the join is. 

1984 

1985 of_type = getattr(onclause, "_of_type", None) 

1986 

1987 if right is None: 

1988 if of_type: 

1989 right = of_type 

1990 else: 

1991 right = onclause.property 

1992 

1993 try: 

1994 right = right.entity 

1995 except AttributeError as err: 

1996 raise sa_exc.ArgumentError( 

1997 "Join target %s does not refer to a " 

1998 "mapped entity" % right 

1999 ) from err 

2000 

2001 left = onclause._parententity 

2002 

2003 prop = onclause.property 

2004 if not isinstance(onclause, attributes.QueryableAttribute): 

2005 onclause = prop 

2006 

2007 # check for this path already present. don't render in that 

2008 # case. 

2009 if (left, right, prop.key) in self._already_joined_edges: 

2010 continue 

2011 

2012 if from_ is not None: 

2013 if ( 

2014 from_ is not left 

2015 and from_._annotations.get("parententity", None) 

2016 is not left 

2017 ): 

2018 raise sa_exc.InvalidRequestError( 

2019 "explicit from clause %s does not match left side " 

2020 "of relationship attribute %s" 

2021 % ( 

2022 from_._annotations.get("parententity", from_), 

2023 onclause, 

2024 ) 

2025 ) 

2026 elif from_ is not None: 

2027 prop = None 

2028 left = from_ 

2029 else: 

2030 # no descriptor/property given; we will need to figure out 

2031 # what the effective "left" side is 

2032 prop = left = None 

2033 

2034 # figure out the final "left" and "right" sides and create an 

2035 # ORMJoin to add to our _from_obj tuple 

2036 self._join_left_to_right( 

2037 entities_collection, 

2038 left, 

2039 right, 

2040 onclause, 

2041 prop, 

2042 isouter, 

2043 full, 

2044 ) 

2045 

2046 def _join_left_to_right( 

2047 self, 

2048 entities_collection, 

2049 left, 

2050 right, 

2051 onclause, 

2052 prop, 

2053 outerjoin, 

2054 full, 

2055 ): 

2056 """given raw "left", "right", "onclause" parameters consumed from 

2057 a particular key within _join(), add a real ORMJoin object to 

2058 our _from_obj list (or augment an existing one) 

2059 

2060 """ 

2061 

2062 explicit_left = left 

2063 if left is None: 

2064 # left not given (e.g. no relationship object/name specified) 

2065 # figure out the best "left" side based on our existing froms / 

2066 # entities 

2067 assert prop is None 

2068 ( 

2069 left, 

2070 replace_from_obj_index, 

2071 use_entity_index, 

2072 ) = self._join_determine_implicit_left_side( 

2073 entities_collection, left, right, onclause 

2074 ) 

2075 else: 

2076 # left is given via a relationship/name, or as explicit left side. 

2077 # Determine where in our 

2078 # "froms" list it should be spliced/appended as well as what 

2079 # existing entity it corresponds to. 

2080 ( 

2081 replace_from_obj_index, 

2082 use_entity_index, 

2083 ) = self._join_place_explicit_left_side(entities_collection, left) 

2084 

2085 if left is right: 

2086 raise sa_exc.InvalidRequestError( 

2087 "Can't construct a join from %s to %s, they " 

2088 "are the same entity" % (left, right) 

2089 ) 

2090 

2091 # the right side as given often needs to be adapted. additionally 

2092 # a lot of things can be wrong with it. handle all that and 

2093 # get back the new effective "right" side 

2094 r_info, right, onclause = self._join_check_and_adapt_right_side( 

2095 left, right, onclause, prop 

2096 ) 

2097 

2098 if not r_info.is_selectable: 

2099 extra_criteria = self._get_extra_criteria(r_info) 

2100 else: 

2101 extra_criteria = () 

2102 

2103 if replace_from_obj_index is not None: 

2104 # splice into an existing element in the 

2105 # self._from_obj list 

2106 left_clause = self.from_clauses[replace_from_obj_index] 

2107 

2108 if explicit_left is not None and onclause is None: 

2109 onclause = _ORMJoin._join_condition(explicit_left, right) 

2110 

2111 self.from_clauses = ( 

2112 self.from_clauses[:replace_from_obj_index] 

2113 + [ 

2114 _ORMJoin( 

2115 left_clause, 

2116 right, 

2117 onclause, 

2118 isouter=outerjoin, 

2119 full=full, 

2120 _extra_criteria=extra_criteria, 

2121 ) 

2122 ] 

2123 + self.from_clauses[replace_from_obj_index + 1 :] 

2124 ) 

2125 else: 

2126 # add a new element to the self._from_obj list 

2127 if use_entity_index is not None: 

2128 # make use of _MapperEntity selectable, which is usually 

2129 # entity_zero.selectable, but if with_polymorphic() were used 

2130 # might be distinct 

2131 assert isinstance( 

2132 entities_collection[use_entity_index], _MapperEntity 

2133 ) 

2134 left_clause = entities_collection[use_entity_index].selectable 

2135 else: 

2136 left_clause = left 

2137 

2138 self.from_clauses = self.from_clauses + [ 

2139 _ORMJoin( 

2140 left_clause, 

2141 r_info, 

2142 onclause, 

2143 isouter=outerjoin, 

2144 full=full, 

2145 _extra_criteria=extra_criteria, 

2146 ) 

2147 ] 

2148 

2149 def _join_determine_implicit_left_side( 

2150 self, entities_collection, left, right, onclause 

2151 ): 

2152 """When join conditions don't express the left side explicitly, 

2153 determine if an existing FROM or entity in this query 

2154 can serve as the left hand side. 

2155 

2156 """ 

2157 

2158 # when we are here, it means join() was called without an ORM- 

2159 # specific way of telling us what the "left" side is, e.g.: 

2160 # 

2161 # join(RightEntity) 

2162 # 

2163 # or 

2164 # 

2165 # join(RightEntity, RightEntity.foo == LeftEntity.bar) 

2166 # 

2167 

2168 r_info = inspect(right) 

2169 

2170 replace_from_obj_index = use_entity_index = None 

2171 

2172 if self.from_clauses: 

2173 # we have a list of FROMs already. So by definition this 

2174 # join has to connect to one of those FROMs. 

2175 

2176 indexes = sql_util.find_left_clause_to_join_from( 

2177 self.from_clauses, r_info.selectable, onclause 

2178 ) 

2179 

2180 if len(indexes) == 1: 

2181 replace_from_obj_index = indexes[0] 

2182 left = self.from_clauses[replace_from_obj_index] 

2183 elif len(indexes) > 1: 

2184 raise sa_exc.InvalidRequestError( 

2185 "Can't determine which FROM clause to join " 

2186 "from, there are multiple FROMS which can " 

2187 "join to this entity. Please use the .select_from() " 

2188 "method to establish an explicit left side, as well as " 

2189 "providing an explicit ON clause if not present already " 

2190 "to help resolve the ambiguity." 

2191 ) 

2192 else: 

2193 raise sa_exc.InvalidRequestError( 

2194 "Don't know how to join to %r. " 

2195 "Please use the .select_from() " 

2196 "method to establish an explicit left side, as well as " 

2197 "providing an explicit ON clause if not present already " 

2198 "to help resolve the ambiguity." % (right,) 

2199 ) 

2200 

2201 elif entities_collection: 

2202 # we have no explicit FROMs, so the implicit left has to 

2203 # come from our list of entities. 

2204 

2205 potential = {} 

2206 for entity_index, ent in enumerate(entities_collection): 

2207 entity = ent.entity_zero_or_selectable 

2208 if entity is None: 

2209 continue 

2210 ent_info = inspect(entity) 

2211 if ent_info is r_info: # left and right are the same, skip 

2212 continue 

2213 

2214 # by using a dictionary with the selectables as keys this 

2215 # de-duplicates those selectables as occurs when the query is 

2216 # against a series of columns from the same selectable 

2217 if isinstance(ent, _MapperEntity): 

2218 potential[ent.selectable] = (entity_index, entity) 

2219 else: 

2220 potential[ent_info.selectable] = (None, entity) 

2221 

2222 all_clauses = list(potential.keys()) 

2223 indexes = sql_util.find_left_clause_to_join_from( 

2224 all_clauses, r_info.selectable, onclause 

2225 ) 

2226 

2227 if len(indexes) == 1: 

2228 use_entity_index, left = potential[all_clauses[indexes[0]]] 

2229 elif len(indexes) > 1: 

2230 raise sa_exc.InvalidRequestError( 

2231 "Can't determine which FROM clause to join " 

2232 "from, there are multiple FROMS which can " 

2233 "join to this entity. Please use the .select_from() " 

2234 "method to establish an explicit left side, as well as " 

2235 "providing an explicit ON clause if not present already " 

2236 "to help resolve the ambiguity." 

2237 ) 

2238 else: 

2239 raise sa_exc.InvalidRequestError( 

2240 "Don't know how to join to %r. " 

2241 "Please use the .select_from() " 

2242 "method to establish an explicit left side, as well as " 

2243 "providing an explicit ON clause if not present already " 

2244 "to help resolve the ambiguity." % (right,) 

2245 ) 

2246 else: 

2247 raise sa_exc.InvalidRequestError( 

2248 "No entities to join from; please use " 

2249 "select_from() to establish the left " 

2250 "entity/selectable of this join" 

2251 ) 

2252 

2253 return left, replace_from_obj_index, use_entity_index 

2254 

2255 def _join_place_explicit_left_side(self, entities_collection, left): 

2256 """When join conditions express a left side explicitly, determine 

2257 where in our existing list of FROM clauses we should join towards, 

2258 or if we need to make a new join, and if so is it from one of our 

2259 existing entities. 

2260 

2261 """ 

2262 

2263 # when we are here, it means join() was called with an indicator 

2264 # as to an exact left side, which means a path to a 

2265 # Relationship was given, e.g.: 

2266 # 

2267 # join(RightEntity, LeftEntity.right) 

2268 # 

2269 # or 

2270 # 

2271 # join(LeftEntity.right) 

2272 # 

2273 # as well as string forms: 

2274 # 

2275 # join(RightEntity, "right") 

2276 # 

2277 # etc. 

2278 # 

2279 

2280 replace_from_obj_index = use_entity_index = None 

2281 

2282 l_info = inspect(left) 

2283 if self.from_clauses: 

2284 indexes = sql_util.find_left_clause_that_matches_given( 

2285 self.from_clauses, l_info.selectable 

2286 ) 

2287 

2288 if len(indexes) > 1: 

2289 raise sa_exc.InvalidRequestError( 

2290 "Can't identify which entity in which to assign the " 

2291 "left side of this join. Please use a more specific " 

2292 "ON clause." 

2293 ) 

2294 

2295 # have an index, means the left side is already present in 

2296 # an existing FROM in the self._from_obj tuple 

2297 if indexes: 

2298 replace_from_obj_index = indexes[0] 

2299 

2300 # no index, means we need to add a new element to the 

2301 # self._from_obj tuple 

2302 

2303 # no from element present, so we will have to add to the 

2304 # self._from_obj tuple. Determine if this left side matches up 

2305 # with existing mapper entities, in which case we want to apply the 

2306 # aliasing / adaptation rules present on that entity if any 

2307 if ( 

2308 replace_from_obj_index is None 

2309 and entities_collection 

2310 and hasattr(l_info, "mapper") 

2311 ): 

2312 for idx, ent in enumerate(entities_collection): 

2313 # TODO: should we be checking for multiple mapper entities 

2314 # matching? 

2315 if isinstance(ent, _MapperEntity) and ent.corresponds_to(left): 

2316 use_entity_index = idx 

2317 break 

2318 

2319 return replace_from_obj_index, use_entity_index 

2320 

2321 def _join_check_and_adapt_right_side(self, left, right, onclause, prop): 

2322 """transform the "right" side of the join as well as the onclause 

2323 according to polymorphic mapping translations, aliasing on the query 

2324 or on the join, special cases where the right and left side have 

2325 overlapping tables. 

2326 

2327 """ 

2328 

2329 l_info = inspect(left) 

2330 r_info = inspect(right) 

2331 

2332 overlap = False 

2333 

2334 right_mapper = getattr(r_info, "mapper", None) 

2335 # if the target is a joined inheritance mapping, 

2336 # be more liberal about auto-aliasing. 

2337 if right_mapper and ( 

2338 right_mapper.with_polymorphic 

2339 or isinstance(right_mapper.persist_selectable, expression.Join) 

2340 ): 

2341 for from_obj in self.from_clauses or [l_info.selectable]: 

2342 if sql_util.selectables_overlap( 

2343 l_info.selectable, from_obj 

2344 ) and sql_util.selectables_overlap( 

2345 from_obj, r_info.selectable 

2346 ): 

2347 overlap = True 

2348 break 

2349 

2350 if overlap and l_info.selectable is r_info.selectable: 

2351 raise sa_exc.InvalidRequestError( 

2352 "Can't join table/selectable '%s' to itself" 

2353 % l_info.selectable 

2354 ) 

2355 

2356 right_mapper, right_selectable, right_is_aliased = ( 

2357 getattr(r_info, "mapper", None), 

2358 r_info.selectable, 

2359 getattr(r_info, "is_aliased_class", False), 

2360 ) 

2361 

2362 if ( 

2363 right_mapper 

2364 and prop 

2365 and not right_mapper.common_parent(prop.mapper) 

2366 ): 

2367 raise sa_exc.InvalidRequestError( 

2368 "Join target %s does not correspond to " 

2369 "the right side of join condition %s" % (right, onclause) 

2370 ) 

2371 

2372 # _join_entities is used as a hint for single-table inheritance 

2373 # purposes at the moment 

2374 if hasattr(r_info, "mapper"): 

2375 self._join_entities += (r_info,) 

2376 

2377 need_adapter = False 

2378 

2379 # test for joining to an unmapped selectable as the target 

2380 if r_info.is_clause_element: 

2381 if prop: 

2382 right_mapper = prop.mapper 

2383 

2384 if right_selectable._is_lateral: 

2385 # orm_only is disabled to suit the case where we have to 

2386 # adapt an explicit correlate(Entity) - the select() loses 

2387 # the ORM-ness in this case right now, ideally it would not 

2388 current_adapter = self._get_current_adapter() 

2389 if current_adapter is not None: 

2390 # TODO: we had orm_only=False here before, removing 

2391 # it didn't break things. if we identify the rationale, 

2392 # may need to apply "_orm_only" annotation here. 

2393 right = current_adapter(right, True) 

2394 

2395 elif prop: 

2396 # joining to selectable with a mapper property given 

2397 # as the ON clause 

2398 

2399 if not right_selectable.is_derived_from( 

2400 right_mapper.persist_selectable 

2401 ): 

2402 raise sa_exc.InvalidRequestError( 

2403 "Selectable '%s' is not derived from '%s'" 

2404 % ( 

2405 right_selectable.description, 

2406 right_mapper.persist_selectable.description, 

2407 ) 

2408 ) 

2409 

2410 # if the destination selectable is a plain select(), 

2411 # turn it into an alias(). 

2412 if isinstance(right_selectable, expression.SelectBase): 

2413 right_selectable = coercions.expect( 

2414 roles.FromClauseRole, right_selectable 

2415 ) 

2416 need_adapter = True 

2417 

2418 # make the right hand side target into an ORM entity 

2419 right = AliasedClass(right_mapper, right_selectable) 

2420 

2421 util.warn_deprecated( 

2422 "An alias is being generated automatically against " 

2423 "joined entity %s for raw clauseelement, which is " 

2424 "deprecated and will be removed in a later release. " 

2425 "Use the aliased() " 

2426 "construct explicitly, see the linked example." 

2427 % right_mapper, 

2428 "1.4", 

2429 code="xaj1", 

2430 ) 

2431 

2432 # test for overlap: 

2433 # orm/inheritance/relationships.py 

2434 # SelfReferentialM2MTest 

2435 aliased_entity = right_mapper and not right_is_aliased and overlap 

2436 

2437 if not need_adapter and aliased_entity: 

2438 # there are a few places in the ORM that automatic aliasing 

2439 # is still desirable, and can't be automatic with a Core 

2440 # only approach. For illustrations of "overlaps" see 

2441 # test/orm/inheritance/test_relationships.py. There are also 

2442 # general overlap cases with many-to-many tables where automatic 

2443 # aliasing is desirable. 

2444 right = AliasedClass(right, flat=True) 

2445 need_adapter = True 

2446 

2447 util.warn( 

2448 "An alias is being generated automatically against " 

2449 "joined entity %s due to overlapping tables. This is a " 

2450 "legacy pattern which may be " 

2451 "deprecated in a later release. Use the " 

2452 "aliased(<entity>, flat=True) " 

2453 "construct explicitly, see the linked example." % right_mapper, 

2454 code="xaj2", 

2455 ) 

2456 

2457 if need_adapter: 

2458 # if need_adapter is True, we are in a deprecated case and 

2459 # a warning has been emitted. 

2460 assert right_mapper 

2461 

2462 adapter = ORMAdapter( 

2463 _TraceAdaptRole.DEPRECATED_JOIN_ADAPT_RIGHT_SIDE, 

2464 inspect(right), 

2465 equivalents=right_mapper._equivalent_columns, 

2466 ) 

2467 

2468 # if an alias() on the right side was generated, 

2469 # which is intended to wrap a the right side in a subquery, 

2470 # ensure that columns retrieved from this target in the result 

2471 # set are also adapted. 

2472 self._mapper_loads_polymorphically_with(right_mapper, adapter) 

2473 elif ( 

2474 not r_info.is_clause_element 

2475 and not right_is_aliased 

2476 and right_mapper._has_aliased_polymorphic_fromclause 

2477 ): 

2478 # for the case where the target mapper has a with_polymorphic 

2479 # set up, ensure an adapter is set up for criteria that works 

2480 # against this mapper. Previously, this logic used to 

2481 # use the "create_aliases or aliased_entity" case to generate 

2482 # an aliased() object, but this creates an alias that isn't 

2483 # strictly necessary. 

2484 # see test/orm/test_core_compilation.py 

2485 # ::RelNaturalAliasedJoinsTest::test_straight 

2486 # and similar 

2487 self._mapper_loads_polymorphically_with( 

2488 right_mapper, 

2489 ORMAdapter( 

2490 _TraceAdaptRole.WITH_POLYMORPHIC_ADAPTER_RIGHT_JOIN, 

2491 right_mapper, 

2492 selectable=right_mapper.selectable, 

2493 equivalents=right_mapper._equivalent_columns, 

2494 ), 

2495 ) 

2496 # if the onclause is a ClauseElement, adapt it with any 

2497 # adapters that are in place right now 

2498 if isinstance(onclause, expression.ClauseElement): 

2499 current_adapter = self._get_current_adapter() 

2500 if current_adapter: 

2501 onclause = current_adapter(onclause, True) 

2502 

2503 # if joining on a MapperProperty path, 

2504 # track the path to prevent redundant joins 

2505 if prop: 

2506 self._already_joined_edges += ((left, right, prop.key),) 

2507 

2508 return inspect(right), right, onclause 

2509 

2510 @property 

2511 def _select_args(self): 

2512 return { 

2513 "limit_clause": self.select_statement._limit_clause, 

2514 "offset_clause": self.select_statement._offset_clause, 

2515 "distinct": self.distinct, 

2516 "distinct_on": self.distinct_on, 

2517 "prefixes": self.select_statement._prefixes, 

2518 "suffixes": self.select_statement._suffixes, 

2519 "group_by": self.group_by or None, 

2520 "fetch_clause": self.select_statement._fetch_clause, 

2521 "fetch_clause_options": ( 

2522 self.select_statement._fetch_clause_options 

2523 ), 

2524 "independent_ctes": self.select_statement._independent_ctes, 

2525 "independent_ctes_opts": ( 

2526 self.select_statement._independent_ctes_opts 

2527 ), 

2528 "syntax_extensions": self.syntax_extensions, 

2529 } 

2530 

2531 @property 

2532 def _should_nest_selectable(self): 

2533 kwargs = self._select_args 

2534 

2535 if not self.eager_adding_joins: 

2536 return False 

2537 

2538 return ( 

2539 ( 

2540 kwargs.get("limit_clause") is not None 

2541 and self.multi_row_eager_loaders 

2542 ) 

2543 or ( 

2544 kwargs.get("offset_clause") is not None 

2545 and self.multi_row_eager_loaders 

2546 ) 

2547 or kwargs.get("distinct", False) 

2548 or kwargs.get("distinct_on", ()) 

2549 or kwargs.get("group_by", False) 

2550 ) 

2551 

2552 def _get_extra_criteria(self, ext_info): 

2553 if ( 

2554 "additional_entity_criteria", 

2555 ext_info.mapper, 

2556 ) in self.global_attributes: 

2557 return tuple( 

2558 ae._resolve_where_criteria(ext_info) 

2559 for ae in self.global_attributes[ 

2560 ("additional_entity_criteria", ext_info.mapper) 

2561 ] 

2562 if (ae.include_aliases or ae.entity is ext_info) 

2563 and ae._should_include(self) 

2564 ) 

2565 else: 

2566 return () 

2567 

2568 def _get_correlated_froms(self): 

2569 """Return the set of FROM objects that will be correlated away 

2570 from this SELECT, given its current state. 

2571 

2572 This runs ahead of the final statement being generated, so the 

2573 FROM list is derived from the ORM-level statement. 

2574 

2575 """ 

2576 if not self._explicit_correlate_froms and ( 

2577 not self._implicit_correlate_froms 

2578 ): 

2579 return util.EMPTY_SET 

2580 

2581 froms = self._get_froms(self.select_statement) 

2582 

2583 if self.correlate: 

2584 to_correlate = [c for c in self.correlate if c is not None] 

2585 else: 

2586 to_correlate = None 

2587 

2588 display_froms = set( 

2589 self._correlate_froms( 

2590 froms, 

2591 self.select_statement, 

2592 to_correlate, 

2593 self.correlate_except, 

2594 self.select_statement._auto_correlate, 

2595 self._explicit_correlate_froms, 

2596 self._implicit_correlate_froms, 

2597 ) 

2598 ) 

2599 

2600 return set( 

2601 _expand_cloned( 

2602 _from_objects(*[f for f in froms if f not in display_froms]) 

2603 ) 

2604 ) 

2605 

2606 def _adjust_for_extra_criteria(self): 

2607 """Apply extra criteria filtering. 

2608 

2609 For all distinct single-table-inheritance mappers represented in 

2610 the columns clause of this query, as well as the "select from entity", 

2611 add criterion to the WHERE 

2612 clause of the given QueryContext such that only the appropriate 

2613 subtypes are selected from the total results. 

2614 

2615 Additionally, add WHERE criteria originating from LoaderCriteriaOptions 

2616 associated with the global context. 

2617 

2618 """ 

2619 # entities that are referenced only within the WHERE clause also 

2620 # receive criteria (#13070), except when the entity is correlated 

2621 # to an enclosing SELECT, which has the criteria applied 

2622 # already (#13701) 

2623 correlated_froms = ( 

2624 self._get_correlated_froms() 

2625 if self.select_statement._where_criteria 

2626 else util.EMPTY_SET 

2627 ) 

2628 ext_infos = [ 

2629 fromclause._annotations.get("parententity", None) 

2630 for fromclause in self.from_clauses 

2631 ] + [ 

2632 ext_info 

2633 for elem, ext_info in [ 

2634 (elem, elem._annotations.get("parententity", None)) 

2635 for where_crit in self.select_statement._where_criteria 

2636 for elem in sql_util.surface_expressions(where_crit) 

2637 ] 

2638 if ext_info 

2639 and not ( 

2640 # if all FROMs for this expression are correlated to an 

2641 # enclosing query, then skip adding loader criteria for the 

2642 # entity on behalf of this element, since the actual FROM 

2643 # clause(s) for it are not local to this subquery 

2644 # (#13701) 

2645 correlated_froms 

2646 and elem._from_objects 

2647 and all( 

2648 correlated_froms.intersection(from_._cloned_set) 

2649 for from_ in elem._from_objects 

2650 ) 

2651 ) 

2652 ] 

2653 

2654 for ext_info in ext_infos: 

2655 

2656 if ( 

2657 ext_info 

2658 and ( 

2659 ext_info.mapper._single_table_criterion is not None 

2660 or ("additional_entity_criteria", ext_info.mapper) 

2661 in self.global_attributes 

2662 ) 

2663 and ext_info not in self.extra_criteria_entities 

2664 ): 

2665 self.extra_criteria_entities[ext_info] = ( 

2666 ext_info, 

2667 ext_info._adapter if ext_info.is_aliased_class else None, 

2668 ) 

2669 

2670 _where_criteria_to_add = () 

2671 

2672 merged_single_crit = collections.defaultdict( 

2673 lambda: (util.OrderedSet(), set()) 

2674 ) 

2675 

2676 for ext_info, adapter in util.OrderedSet( 

2677 self.extra_criteria_entities.values() 

2678 ): 

2679 if ext_info in self._join_entities: 

2680 continue 

2681 

2682 # assemble single table inheritance criteria. 

2683 if ( 

2684 ext_info.is_aliased_class 

2685 and ext_info._base_alias()._is_with_polymorphic 

2686 ): 

2687 # for a with_polymorphic(), we always include the full 

2688 # hierarchy from what's given as the base class for the wpoly. 

2689 # this is new in 2.1 for #12395 so that it matches the behavior 

2690 # of joined inheritance. 

2691 hierarchy_root = ext_info._base_alias() 

2692 else: 

2693 hierarchy_root = ext_info 

2694 

2695 single_crit_component = ( 

2696 hierarchy_root.mapper._single_table_criteria_component 

2697 ) 

2698 

2699 if single_crit_component is not None: 

2700 polymorphic_on, criteria = single_crit_component 

2701 

2702 polymorphic_on = polymorphic_on._annotate( 

2703 { 

2704 "parententity": hierarchy_root, 

2705 "parentmapper": hierarchy_root.mapper, 

2706 } 

2707 ) 

2708 

2709 list_of_single_crits, adapters = merged_single_crit[ 

2710 (hierarchy_root, polymorphic_on) 

2711 ] 

2712 list_of_single_crits.update(criteria) 

2713 if adapter: 

2714 adapters.add(adapter) 

2715 

2716 # assemble "additional entity criteria", which come from 

2717 # with_loader_criteria() options 

2718 if not self.compile_options._for_refresh_state: 

2719 additional_entity_criteria = self._get_extra_criteria(ext_info) 

2720 _where_criteria_to_add += tuple( 

2721 adapter.traverse(crit) if adapter else crit 

2722 for crit in additional_entity_criteria 

2723 ) 

2724 

2725 # merge together single table inheritance criteria keyed to 

2726 # top-level mapper / aliasedinsp (which may be a with_polymorphic()) 

2727 for (ext_info, polymorphic_on), ( 

2728 merged_crit, 

2729 adapters, 

2730 ) in merged_single_crit.items(): 

2731 new_crit = polymorphic_on.in_(merged_crit) 

2732 for adapter in adapters: 

2733 new_crit = adapter.traverse(new_crit) 

2734 _where_criteria_to_add += (new_crit,) 

2735 

2736 current_adapter = self._get_current_adapter() 

2737 if current_adapter: 

2738 # finally run all the criteria through the "main" adapter, if we 

2739 # have one, and concatenate to final WHERE criteria 

2740 for crit in _where_criteria_to_add: 

2741 crit = current_adapter(crit, False) 

2742 self._where_criteria += (crit,) 

2743 else: 

2744 # else just concatenate our criteria to the final WHERE criteria 

2745 self._where_criteria += _where_criteria_to_add 

2746 

2747 

2748def _column_descriptions( 

2749 query_or_select_stmt: Union[Query, Select, FromStatement], 

2750 compile_state: Optional[_ORMSelectCompileState] = None, 

2751 legacy: bool = False, 

2752) -> List[ORMColumnDescription]: 

2753 if compile_state is None: 

2754 compile_state = _ORMSelectCompileState._create_entities_collection( 

2755 query_or_select_stmt, legacy=legacy 

2756 ) 

2757 ctx = compile_state 

2758 d = [ 

2759 { 

2760 "name": ent._label_name, 

2761 "type": ent.type, 

2762 "aliased": getattr(insp_ent, "is_aliased_class", False), 

2763 "expr": ent.expr, 

2764 "entity": ( 

2765 getattr(insp_ent, "entity", None) 

2766 if ent.entity_zero is not None 

2767 and not insp_ent.is_clause_element 

2768 else None 

2769 ), 

2770 } 

2771 for ent, insp_ent in [ 

2772 (_ent, _ent.entity_zero) for _ent in ctx._entities 

2773 ] 

2774 ] 

2775 return d 

2776 

2777 

2778def _legacy_filter_by_entity_zero( 

2779 query_or_augmented_select: Union[Query[Any], Select[Unpack[TupleAny]]], 

2780) -> Optional[_InternalEntityType[Any]]: 

2781 self = query_or_augmented_select 

2782 if self._setup_joins: 

2783 _last_joined_entity = self._last_joined_entity 

2784 if _last_joined_entity is not None: 

2785 return _last_joined_entity 

2786 

2787 if self._from_obj and "parententity" in self._from_obj[0]._annotations: 

2788 return self._from_obj[0]._annotations["parententity"] 

2789 

2790 return _entity_from_pre_ent_zero(self) 

2791 

2792 

2793def _entity_from_pre_ent_zero( 

2794 query_or_augmented_select: Union[Query[Any], Select[Unpack[TupleAny]]], 

2795) -> Optional[_InternalEntityType[Any]]: 

2796 self = query_or_augmented_select 

2797 if not self._raw_columns: 

2798 return None 

2799 

2800 ent = self._raw_columns[0] 

2801 

2802 if "parententity" in ent._annotations: 

2803 return ent._annotations["parententity"] 

2804 elif isinstance(ent, ORMColumnsClauseRole): 

2805 return ent.entity 

2806 elif "bundle" in ent._annotations: 

2807 return ent._annotations["bundle"] 

2808 else: 

2809 return ent 

2810 

2811 

2812def _determine_last_joined_entity( 

2813 setup_joins: Tuple[_SetupJoinsElement, ...], 

2814 entity_zero: Optional[_InternalEntityType[Any]] = None, 

2815) -> Optional[Union[_InternalEntityType[Any], _JoinTargetElement]]: 

2816 if not setup_joins: 

2817 return None 

2818 

2819 target, onclause, from_, flags = setup_joins[-1] 

2820 

2821 if isinstance( 

2822 target, 

2823 attributes.QueryableAttribute, 

2824 ): 

2825 return target.entity 

2826 else: 

2827 return target 

2828 

2829 

2830class _QueryEntity: 

2831 """represent an entity column returned within a Query result.""" 

2832 

2833 __slots__ = () 

2834 

2835 supports_single_entity: bool 

2836 

2837 _non_hashable_value = False 

2838 _null_column_type = False 

2839 use_id_for_hash = False 

2840 

2841 _label_name: Optional[str] 

2842 type: Union[Type[Any], TypeEngine[Any]] 

2843 expr: Union[_InternalEntityType, ColumnElement[Any]] 

2844 entity_zero: Optional[_InternalEntityType] 

2845 

2846 def setup_compile_state(self, compile_state: _ORMCompileState) -> None: 

2847 raise NotImplementedError() 

2848 

2849 def setup_dml_returning_compile_state( 

2850 self, 

2851 compile_state: _ORMCompileState, 

2852 adapter: Optional[_DMLReturningColFilter], 

2853 ) -> None: 

2854 raise NotImplementedError() 

2855 

2856 def row_processor(self, context, result): 

2857 raise NotImplementedError() 

2858 

2859 @classmethod 

2860 def to_compile_state( 

2861 cls, compile_state, entities, entities_collection, is_current_entities 

2862 ): 

2863 for idx, entity in enumerate(entities): 

2864 if entity._is_lambda_element: 

2865 if entity._is_sequence: 

2866 cls.to_compile_state( 

2867 compile_state, 

2868 entity._resolved, 

2869 entities_collection, 

2870 is_current_entities, 

2871 ) 

2872 continue 

2873 else: 

2874 entity = entity._resolved 

2875 

2876 if entity.is_clause_element: 

2877 if entity.is_selectable: 

2878 if "parententity" in entity._annotations: 

2879 _MapperEntity( 

2880 compile_state, 

2881 entity, 

2882 entities_collection, 

2883 is_current_entities, 

2884 ) 

2885 else: 

2886 _ColumnEntity._for_columns( 

2887 compile_state, 

2888 entity._select_iterable, 

2889 entities_collection, 

2890 idx, 

2891 is_current_entities, 

2892 ) 

2893 else: 

2894 if entity._annotations.get("bundle", False): 

2895 _BundleEntity( 

2896 compile_state, 

2897 entity, 

2898 entities_collection, 

2899 is_current_entities, 

2900 ) 

2901 elif entity._is_clause_list: 

2902 # this is legacy only - test_composites.py 

2903 # test_query_cols_legacy 

2904 _ColumnEntity._for_columns( 

2905 compile_state, 

2906 entity._select_iterable, 

2907 entities_collection, 

2908 idx, 

2909 is_current_entities, 

2910 ) 

2911 else: 

2912 _ColumnEntity._for_columns( 

2913 compile_state, 

2914 [entity], 

2915 entities_collection, 

2916 idx, 

2917 is_current_entities, 

2918 ) 

2919 elif entity.is_bundle: 

2920 _BundleEntity(compile_state, entity, entities_collection) 

2921 

2922 return entities_collection 

2923 

2924 

2925class _MapperEntity(_QueryEntity): 

2926 """mapper/class/AliasedClass entity""" 

2927 

2928 __slots__ = ( 

2929 "expr", 

2930 "mapper", 

2931 "entity_zero", 

2932 "is_aliased_class", 

2933 "path", 

2934 "_extra_entities", 

2935 "_label_name", 

2936 "_with_polymorphic_mappers", 

2937 "selectable", 

2938 "_polymorphic_discriminator", 

2939 ) 

2940 

2941 expr: _InternalEntityType 

2942 mapper: Mapper[Any] 

2943 entity_zero: _InternalEntityType 

2944 is_aliased_class: bool 

2945 path: PathRegistry 

2946 _label_name: str 

2947 

2948 def __init__( 

2949 self, compile_state, entity, entities_collection, is_current_entities 

2950 ): 

2951 entities_collection.append(self) 

2952 if is_current_entities: 

2953 if compile_state._primary_entity is None: 

2954 compile_state._primary_entity = self 

2955 compile_state._has_mapper_entities = True 

2956 compile_state._has_orm_entities = True 

2957 

2958 entity = entity._annotations["parententity"] 

2959 entity._post_inspect 

2960 ext_info = self.entity_zero = entity 

2961 entity = ext_info.entity 

2962 

2963 self.expr = entity 

2964 self.mapper = mapper = ext_info.mapper 

2965 

2966 self._extra_entities = (self.expr,) 

2967 

2968 if ext_info.is_aliased_class: 

2969 self._label_name = ext_info.name 

2970 else: 

2971 self._label_name = mapper.class_.__name__ 

2972 

2973 self.is_aliased_class = ext_info.is_aliased_class 

2974 self.path = ext_info._path_registry 

2975 

2976 self.selectable = ext_info.selectable 

2977 self._with_polymorphic_mappers = ext_info.with_polymorphic_mappers 

2978 self._polymorphic_discriminator = ext_info.polymorphic_on 

2979 

2980 if mapper._should_select_with_poly_adapter: 

2981 compile_state._create_with_polymorphic_adapter( 

2982 ext_info, self.selectable 

2983 ) 

2984 

2985 supports_single_entity = True 

2986 

2987 _non_hashable_value = True 

2988 use_id_for_hash = True 

2989 

2990 @property 

2991 def type(self): 

2992 return self.mapper.class_ 

2993 

2994 @property 

2995 def entity_zero_or_selectable(self): 

2996 return self.entity_zero 

2997 

2998 def corresponds_to(self, entity): 

2999 return _entity_corresponds_to(self.entity_zero, entity) 

3000 

3001 def _get_entity_clauses(self, compile_state): 

3002 adapter = None 

3003 

3004 if not self.is_aliased_class: 

3005 if compile_state._polymorphic_adapters: 

3006 adapter = compile_state._polymorphic_adapters.get( 

3007 self.mapper, None 

3008 ) 

3009 else: 

3010 adapter = self.entity_zero._adapter 

3011 

3012 if adapter: 

3013 if compile_state._from_obj_alias: 

3014 ret = adapter.wrap(compile_state._from_obj_alias) 

3015 else: 

3016 ret = adapter 

3017 else: 

3018 ret = compile_state._from_obj_alias 

3019 

3020 return ret 

3021 

3022 def row_processor(self, context, result): 

3023 compile_state = context.compile_state 

3024 adapter = self._get_entity_clauses(compile_state) 

3025 

3026 if compile_state.compound_eager_adapter and adapter: 

3027 adapter = adapter.wrap(compile_state.compound_eager_adapter) 

3028 elif not adapter: 

3029 adapter = compile_state.compound_eager_adapter 

3030 

3031 if compile_state._primary_entity is self: 

3032 only_load_props = compile_state.compile_options._only_load_props 

3033 refresh_state = context.refresh_state 

3034 else: 

3035 only_load_props = refresh_state = None 

3036 

3037 _instance = loading._instance_processor( 

3038 self, 

3039 self.mapper, 

3040 context, 

3041 result, 

3042 self.path, 

3043 adapter, 

3044 only_load_props=only_load_props, 

3045 refresh_state=refresh_state, 

3046 polymorphic_discriminator=self._polymorphic_discriminator, 

3047 ) 

3048 

3049 return _instance, self._label_name, self._extra_entities 

3050 

3051 def setup_dml_returning_compile_state( 

3052 self, 

3053 compile_state: _ORMCompileState, 

3054 adapter: Optional[_DMLReturningColFilter], 

3055 ) -> None: 

3056 loading._setup_entity_query( 

3057 compile_state, 

3058 self.mapper, 

3059 self, 

3060 self.path, 

3061 adapter, 

3062 compile_state.primary_columns, 

3063 with_polymorphic=self._with_polymorphic_mappers, 

3064 only_load_props=compile_state.compile_options._only_load_props, 

3065 polymorphic_discriminator=self._polymorphic_discriminator, 

3066 ) 

3067 

3068 def setup_compile_state(self, compile_state): 

3069 adapter = self._get_entity_clauses(compile_state) 

3070 

3071 single_table_crit = self.mapper._single_table_criterion 

3072 if ( 

3073 single_table_crit is not None 

3074 or ("additional_entity_criteria", self.mapper) 

3075 in compile_state.global_attributes 

3076 ): 

3077 ext_info = self.entity_zero 

3078 compile_state.extra_criteria_entities[ext_info] = ( 

3079 ext_info, 

3080 ext_info._adapter if ext_info.is_aliased_class else None, 

3081 ) 

3082 

3083 loading._setup_entity_query( 

3084 compile_state, 

3085 self.mapper, 

3086 self, 

3087 self.path, 

3088 adapter, 

3089 compile_state.primary_columns, 

3090 with_polymorphic=self._with_polymorphic_mappers, 

3091 only_load_props=compile_state.compile_options._only_load_props, 

3092 polymorphic_discriminator=self._polymorphic_discriminator, 

3093 ) 

3094 compile_state._fallback_from_clauses.append(self.selectable) 

3095 

3096 

3097class _BundleEntity(_QueryEntity): 

3098 _extra_entities = () 

3099 

3100 __slots__ = ( 

3101 "bundle", 

3102 "expr", 

3103 "type", 

3104 "_label_name", 

3105 "_entities", 

3106 "supports_single_entity", 

3107 ) 

3108 

3109 _entities: List[_QueryEntity] 

3110 bundle: Bundle 

3111 type: Type[Any] 

3112 _label_name: str 

3113 supports_single_entity: bool 

3114 expr: Bundle 

3115 

3116 def __init__( 

3117 self, 

3118 compile_state, 

3119 expr, 

3120 entities_collection, 

3121 is_current_entities, 

3122 setup_entities=True, 

3123 parent_bundle=None, 

3124 ): 

3125 compile_state._has_orm_entities = True 

3126 

3127 expr = expr._annotations["bundle"] 

3128 if parent_bundle: 

3129 parent_bundle._entities.append(self) 

3130 else: 

3131 entities_collection.append(self) 

3132 

3133 if isinstance( 

3134 expr, (attributes.QueryableAttribute, interfaces.PropComparator) 

3135 ): 

3136 bundle = expr.__clause_element__() 

3137 else: 

3138 bundle = expr 

3139 

3140 self.bundle = self.expr = bundle 

3141 self.type = type(bundle) 

3142 self._label_name = bundle.name 

3143 self._entities = [] 

3144 

3145 if setup_entities: 

3146 for expr in bundle.exprs: 

3147 if "bundle" in expr._annotations: 

3148 _BundleEntity( 

3149 compile_state, 

3150 expr, 

3151 entities_collection, 

3152 is_current_entities, 

3153 parent_bundle=self, 

3154 ) 

3155 elif isinstance(expr, Bundle): 

3156 _BundleEntity( 

3157 compile_state, 

3158 expr, 

3159 entities_collection, 

3160 is_current_entities, 

3161 parent_bundle=self, 

3162 ) 

3163 else: 

3164 _ORMColumnEntity._for_columns( 

3165 compile_state, 

3166 [expr], 

3167 entities_collection, 

3168 None, 

3169 is_current_entities, 

3170 parent_bundle=self, 

3171 ) 

3172 

3173 self.supports_single_entity = self.bundle.single_entity 

3174 

3175 @property 

3176 def mapper(self): 

3177 ezero = self.entity_zero 

3178 if ezero is not None: 

3179 return ezero.mapper 

3180 else: 

3181 return None 

3182 

3183 @property 

3184 def entity_zero(self): 

3185 for ent in self._entities: 

3186 ezero = ent.entity_zero 

3187 if ezero is not None: 

3188 return ezero 

3189 else: 

3190 return None 

3191 

3192 def corresponds_to(self, entity): 

3193 # TODO: we might be able to implement this but for now 

3194 # we are working around it 

3195 return False 

3196 

3197 @property 

3198 def entity_zero_or_selectable(self): 

3199 for ent in self._entities: 

3200 ezero = ent.entity_zero_or_selectable 

3201 if ezero is not None: 

3202 return ezero 

3203 else: 

3204 return None 

3205 

3206 def setup_compile_state(self, compile_state): 

3207 for ent in self._entities: 

3208 ent.setup_compile_state(compile_state) 

3209 

3210 def setup_dml_returning_compile_state( 

3211 self, 

3212 compile_state: _ORMCompileState, 

3213 adapter: Optional[_DMLReturningColFilter], 

3214 ) -> None: 

3215 return self.setup_compile_state(compile_state) 

3216 

3217 def row_processor(self, context, result): 

3218 procs, labels, extra = zip( 

3219 *[ent.row_processor(context, result) for ent in self._entities] 

3220 ) 

3221 

3222 proc = self.bundle.create_row_processor(context.query, procs, labels) 

3223 

3224 return proc, self._label_name, self._extra_entities 

3225 

3226 

3227class _ColumnEntity(_QueryEntity): 

3228 __slots__ = ( 

3229 "_fetch_column", 

3230 "_row_processor", 

3231 "raw_column_index", 

3232 "translate_raw_column", 

3233 ) 

3234 

3235 @classmethod 

3236 def _for_columns( 

3237 cls, 

3238 compile_state, 

3239 columns, 

3240 entities_collection, 

3241 raw_column_index, 

3242 is_current_entities, 

3243 parent_bundle=None, 

3244 ): 

3245 for column in columns: 

3246 annotations = column._annotations 

3247 if "parententity" in annotations: 

3248 _entity = annotations["parententity"] 

3249 else: 

3250 _entity = sql_util.extract_first_column_annotation( 

3251 column, "parententity" 

3252 ) 

3253 

3254 if _entity: 

3255 if "identity_token" in column._annotations: 

3256 _IdentityTokenEntity( 

3257 compile_state, 

3258 column, 

3259 entities_collection, 

3260 _entity, 

3261 raw_column_index, 

3262 is_current_entities, 

3263 parent_bundle=parent_bundle, 

3264 ) 

3265 else: 

3266 _ORMColumnEntity( 

3267 compile_state, 

3268 column, 

3269 entities_collection, 

3270 _entity, 

3271 raw_column_index, 

3272 is_current_entities, 

3273 parent_bundle=parent_bundle, 

3274 ) 

3275 else: 

3276 _RawColumnEntity( 

3277 compile_state, 

3278 column, 

3279 entities_collection, 

3280 raw_column_index, 

3281 is_current_entities, 

3282 parent_bundle=parent_bundle, 

3283 ) 

3284 

3285 @property 

3286 def type(self): 

3287 return self.column.type 

3288 

3289 @property 

3290 def _non_hashable_value(self): 

3291 return not self.column.type.hashable 

3292 

3293 @property 

3294 def _null_column_type(self): 

3295 return self.column.type._isnull 

3296 

3297 def row_processor(self, context, result): 

3298 compile_state = context.compile_state 

3299 

3300 # the resulting callable is entirely cacheable so just return 

3301 # it if we already made one 

3302 if self._row_processor is not None: 

3303 getter, label_name, extra_entities = self._row_processor 

3304 if self.translate_raw_column: 

3305 extra_entities += ( 

3306 context.query._raw_columns[self.raw_column_index], 

3307 ) 

3308 

3309 return getter, label_name, extra_entities 

3310 

3311 # retrieve the column that would have been set up in 

3312 # setup_compile_state, to avoid doing redundant work 

3313 if self._fetch_column is not None: 

3314 column = self._fetch_column 

3315 else: 

3316 # fetch_column will be None when we are doing a from_statement 

3317 # and setup_compile_state may not have been called. 

3318 column = self.column 

3319 

3320 # previously, the RawColumnEntity didn't look for from_obj_alias 

3321 # however I can't think of a case where we would be here and 

3322 # we'd want to ignore it if this is the from_statement use case. 

3323 # it's not really a use case to have raw columns + from_statement 

3324 if compile_state._from_obj_alias: 

3325 column = compile_state._from_obj_alias.columns[column] 

3326 

3327 if column._annotations: 

3328 # annotated columns perform more slowly in compiler and 

3329 # result due to the __eq__() method, so use deannotated 

3330 column = column._deannotate() 

3331 

3332 if compile_state.compound_eager_adapter: 

3333 column = compile_state.compound_eager_adapter.columns[column] 

3334 

3335 getter = result._getter(column) 

3336 ret = getter, self._label_name, self._extra_entities 

3337 self._row_processor = ret 

3338 

3339 if self.translate_raw_column: 

3340 extra_entities = self._extra_entities + ( 

3341 context.query._raw_columns[self.raw_column_index], 

3342 ) 

3343 return getter, self._label_name, extra_entities 

3344 else: 

3345 return ret 

3346 

3347 

3348class _RawColumnEntity(_ColumnEntity): 

3349 entity_zero = None 

3350 mapper = None 

3351 supports_single_entity = False 

3352 

3353 __slots__ = ( 

3354 "expr", 

3355 "column", 

3356 "_label_name", 

3357 "entity_zero_or_selectable", 

3358 "_extra_entities", 

3359 ) 

3360 

3361 def __init__( 

3362 self, 

3363 compile_state, 

3364 column, 

3365 entities_collection, 

3366 raw_column_index, 

3367 is_current_entities, 

3368 parent_bundle=None, 

3369 ): 

3370 self.expr = column 

3371 self.raw_column_index = raw_column_index 

3372 self.translate_raw_column = raw_column_index is not None 

3373 

3374 if column._is_star: 

3375 compile_state.compile_options += {"_is_star": True} 

3376 

3377 if not is_current_entities or column._is_text_clause: 

3378 self._label_name = None 

3379 else: 

3380 if parent_bundle: 

3381 self._label_name = column._proxy_key 

3382 else: 

3383 self._label_name = compile_state._label_convention(column) 

3384 

3385 if parent_bundle: 

3386 parent_bundle._entities.append(self) 

3387 else: 

3388 entities_collection.append(self) 

3389 

3390 self.column = column 

3391 self.entity_zero_or_selectable = ( 

3392 self.column._from_objects[0] if self.column._from_objects else None 

3393 ) 

3394 self._extra_entities = (self.expr, self.column) 

3395 self._fetch_column = self._row_processor = None 

3396 

3397 def corresponds_to(self, entity): 

3398 return False 

3399 

3400 def setup_dml_returning_compile_state( 

3401 self, 

3402 compile_state: _ORMCompileState, 

3403 adapter: Optional[_DMLReturningColFilter], 

3404 ) -> None: 

3405 return self.setup_compile_state(compile_state) 

3406 

3407 def setup_compile_state(self, compile_state): 

3408 current_adapter = compile_state._get_current_adapter() 

3409 if current_adapter: 

3410 column = current_adapter(self.column, False) 

3411 if column is None: 

3412 return 

3413 else: 

3414 column = self.column 

3415 

3416 if column._annotations: 

3417 # annotated columns perform more slowly in compiler and 

3418 # result due to the __eq__() method, so use deannotated 

3419 column = column._deannotate() 

3420 

3421 compile_state.dedupe_columns.add(column) 

3422 compile_state.primary_columns.append(column) 

3423 self._fetch_column = column 

3424 

3425 

3426class _ORMColumnEntity(_ColumnEntity): 

3427 """Column/expression based entity.""" 

3428 

3429 supports_single_entity = False 

3430 

3431 __slots__ = ( 

3432 "expr", 

3433 "mapper", 

3434 "column", 

3435 "_label_name", 

3436 "entity_zero_or_selectable", 

3437 "entity_zero", 

3438 "_extra_entities", 

3439 ) 

3440 

3441 def __init__( 

3442 self, 

3443 compile_state, 

3444 column, 

3445 entities_collection, 

3446 parententity, 

3447 raw_column_index, 

3448 is_current_entities, 

3449 parent_bundle=None, 

3450 ): 

3451 annotations = column._annotations 

3452 

3453 _entity = parententity 

3454 

3455 # an AliasedClass won't have proxy_key in the annotations for 

3456 # a column if it was acquired using the class' adapter directly, 

3457 # such as using AliasedInsp._adapt_element(). this occurs 

3458 # within internal loaders. 

3459 

3460 orm_key = annotations.get("proxy_key", None) 

3461 proxy_owner = annotations.get("proxy_owner", _entity) 

3462 if orm_key: 

3463 self.expr = getattr(proxy_owner.entity, orm_key) 

3464 self.translate_raw_column = False 

3465 else: 

3466 # if orm_key is not present, that means this is an ad-hoc 

3467 # SQL ColumnElement, like a CASE() or other expression. 

3468 # include this column position from the invoked statement 

3469 # in the ORM-level ResultSetMetaData on each execute, so that 

3470 # it can be targeted by identity after caching 

3471 self.expr = column 

3472 self.translate_raw_column = raw_column_index is not None 

3473 

3474 self.raw_column_index = raw_column_index 

3475 

3476 if is_current_entities: 

3477 if parent_bundle: 

3478 self._label_name = orm_key if orm_key else column._proxy_key 

3479 else: 

3480 self._label_name = compile_state._label_convention( 

3481 column, col_name=orm_key 

3482 ) 

3483 else: 

3484 self._label_name = None 

3485 

3486 _entity._post_inspect 

3487 self.entity_zero = self.entity_zero_or_selectable = ezero = _entity 

3488 self.mapper = mapper = _entity.mapper 

3489 

3490 if parent_bundle: 

3491 parent_bundle._entities.append(self) 

3492 else: 

3493 entities_collection.append(self) 

3494 

3495 compile_state._has_orm_entities = True 

3496 

3497 self.column = column 

3498 

3499 self._fetch_column = self._row_processor = None 

3500 

3501 self._extra_entities = (self.expr, self.column) 

3502 

3503 if mapper._should_select_with_poly_adapter: 

3504 compile_state._create_with_polymorphic_adapter( 

3505 ezero, ezero.selectable 

3506 ) 

3507 

3508 def corresponds_to(self, entity): 

3509 if _is_aliased_class(entity): 

3510 # TODO: polymorphic subclasses ? 

3511 return entity is self.entity_zero 

3512 else: 

3513 return not _is_aliased_class( 

3514 self.entity_zero 

3515 ) and entity.common_parent(self.entity_zero) 

3516 

3517 def setup_dml_returning_compile_state( 

3518 self, 

3519 compile_state: _ORMCompileState, 

3520 adapter: Optional[_DMLReturningColFilter], 

3521 ) -> None: 

3522 

3523 self._fetch_column = column = self.column 

3524 if adapter: 

3525 column = adapter(column, False) 

3526 

3527 if column is not None: 

3528 compile_state.dedupe_columns.add(column) 

3529 compile_state.primary_columns.append(column) 

3530 

3531 def setup_compile_state(self, compile_state): 

3532 current_adapter = compile_state._get_current_adapter() 

3533 if current_adapter: 

3534 column = current_adapter(self.column, False) 

3535 if column is None: 

3536 assert compile_state.is_dml_returning 

3537 self._fetch_column = self.column 

3538 return 

3539 else: 

3540 column = self.column 

3541 

3542 ezero = self.entity_zero 

3543 

3544 single_table_crit = self.mapper._single_table_criterion 

3545 if ( 

3546 single_table_crit is not None 

3547 or ("additional_entity_criteria", self.mapper) 

3548 in compile_state.global_attributes 

3549 ): 

3550 compile_state.extra_criteria_entities[ezero] = ( 

3551 ezero, 

3552 ezero._adapter if ezero.is_aliased_class else None, 

3553 ) 

3554 

3555 if column._annotations and not column._expression_label: 

3556 # annotated columns perform more slowly in compiler and 

3557 # result due to the __eq__() method, so use deannotated 

3558 column = column._deannotate() 

3559 

3560 # use entity_zero as the from if we have it. this is necessary 

3561 # for polymorphic scenarios where our FROM is based on ORM entity, 

3562 # not the FROM of the column. but also, don't use it if our column 

3563 # doesn't actually have any FROMs that line up, such as when its 

3564 # a scalar subquery. 

3565 if set(self.column._from_objects).intersection( 

3566 ezero.selectable._from_objects 

3567 ): 

3568 compile_state._fallback_from_clauses.append(ezero.selectable) 

3569 

3570 compile_state.dedupe_columns.add(column) 

3571 compile_state.primary_columns.append(column) 

3572 self._fetch_column = column 

3573 

3574 

3575class _IdentityTokenEntity(_ORMColumnEntity): 

3576 translate_raw_column = False 

3577 

3578 def setup_compile_state(self, compile_state): 

3579 pass 

3580 

3581 def row_processor(self, context, result): 

3582 def getter(row): 

3583 return context.load_options._identity_token 

3584 

3585 return getter, self._label_name, self._extra_entities