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

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

1325 statements  

1# orm/context.py 

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

3# <see AUTHORS file> 

4# 

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

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

7# mypy: ignore-errors 

8 

9from __future__ import annotations 

10 

11import collections 

12import itertools 

13from typing import Any 

14from typing import cast 

15from typing import Dict 

16from typing import Iterable 

17from typing import List 

18from typing import Optional 

19from typing import Set 

20from typing import Tuple 

21from typing import Type 

22from typing import TYPE_CHECKING 

23from typing import TypeVar 

24from typing import Union 

25 

26from . import attributes 

27from . import interfaces 

28from . import loading 

29from .base import _is_aliased_class 

30from .interfaces import ORMColumnDescription 

31from .interfaces import ORMColumnsClauseRole 

32from .path_registry import PathRegistry 

33from .util import _entity_corresponds_to 

34from .util import _ORMJoin 

35from .util import _TraceAdaptRole 

36from .util import AliasedClass 

37from .util import Bundle 

38from .util import ORMAdapter 

39from .util import ORMStatementAdapter 

40from .. import exc as sa_exc 

41from .. import future 

42from .. import inspect 

43from .. import sql 

44from .. import util 

45from ..sql import coercions 

46from ..sql import expression 

47from ..sql import roles 

48from ..sql import util as sql_util 

49from ..sql import visitors 

50from ..sql._typing import is_dml 

51from ..sql._typing import is_insert_update 

52from ..sql._typing import is_select_base 

53from ..sql.base import _select_iterables 

54from ..sql.base import CacheableOptions 

55from ..sql.base import CompileState 

56from ..sql.base import Executable 

57from ..sql.base import ExecutableStatement 

58from ..sql.base import Generative 

59from ..sql.base import Options 

60from ..sql.dml import UpdateBase 

61from ..sql.elements import GroupedElement 

62from ..sql.elements import TextClause 

63from ..sql.selectable import CompoundSelectState 

64from ..sql.selectable import LABEL_STYLE_DISAMBIGUATE_ONLY 

65from ..sql.selectable import LABEL_STYLE_NONE 

66from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL 

67from ..sql.selectable import Select 

68from ..sql.selectable import SelectLabelStyle 

69from ..sql.selectable import SelectState 

70from ..sql.selectable import TypedReturnsRows 

71from ..sql.visitors import InternalTraversal 

72from ..util.typing import TupleAny 

73from ..util.typing import TypeVarTuple 

74from ..util.typing import Unpack 

75 

76 

77if TYPE_CHECKING: 

78 from ._typing import _InternalEntityType 

79 from ._typing import OrmExecuteOptionsParameter 

80 from .loading import _PostLoad 

81 from .mapper import Mapper 

82 from .query import Query 

83 from .session import _BindArguments 

84 from .session import Session 

85 from ..engine import Result 

86 from ..engine.interfaces import _CoreSingleExecuteParams 

87 from ..sql._typing import _ColumnsClauseArgument 

88 from ..sql.compiler import SQLCompiler 

89 from ..sql.dml import _DMLTableElement 

90 from ..sql.elements import ColumnElement 

91 from ..sql.selectable import _JoinTargetElement 

92 from ..sql.selectable import _LabelConventionCallable 

93 from ..sql.selectable import _SetupJoinsElement 

94 from ..sql.selectable import ExecutableReturnsRows 

95 from ..sql.selectable import SelectBase 

96 from ..sql.type_api import TypeEngine 

97 

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

99_Ts = TypeVarTuple("_Ts") 

100_path_registry = PathRegistry.root 

101 

102_EMPTY_DICT = util.immutabledict() 

103 

104 

105LABEL_STYLE_LEGACY_ORM = SelectLabelStyle.LABEL_STYLE_LEGACY_ORM 

106 

107 

108class QueryContext: 

109 __slots__ = ( 

110 "top_level_context", 

111 "compile_state", 

112 "query", 

113 "user_passed_query", 

114 "params", 

115 "load_options", 

116 "bind_arguments", 

117 "execution_options", 

118 "session", 

119 "autoflush", 

120 "populate_existing", 

121 "invoke_all_eagers", 

122 "version_check", 

123 "refresh_state", 

124 "create_eager_joins", 

125 "propagated_loader_options", 

126 "attributes", 

127 "runid", 

128 "partials", 

129 "post_load_paths", 

130 "identity_token", 

131 "yield_per", 

132 "loaders_require_buffering", 

133 "loaders_require_uniquing", 

134 ) 

135 

136 runid: int 

137 post_load_paths: Dict[PathRegistry, _PostLoad] 

138 compile_state: _ORMCompileState 

139 

140 class default_load_options(Options): 

141 _only_return_tuples = False 

142 _populate_existing = False 

143 _version_check = False 

144 _invoke_all_eagers = True 

145 _autoflush = True 

146 _identity_token = None 

147 _yield_per = None 

148 _refresh_state = None 

149 _lazy_loaded_from = None 

150 _legacy_uniquing = False 

151 _sa_top_level_orm_context = None 

152 _is_user_refresh = False 

153 

154 def __init__( 

155 self, 

156 compile_state: CompileState, 

157 statement: Union[ 

158 Select[Unpack[TupleAny]], 

159 FromStatement[Unpack[TupleAny]], 

160 UpdateBase, 

161 ], 

162 user_passed_query: Union[ 

163 Select[Unpack[TupleAny]], 

164 FromStatement[Unpack[TupleAny]], 

165 UpdateBase, 

166 ], 

167 params: _CoreSingleExecuteParams, 

168 session: Session, 

169 load_options: Union[ 

170 Type[QueryContext.default_load_options], 

171 QueryContext.default_load_options, 

172 ], 

173 execution_options: Optional[OrmExecuteOptionsParameter] = None, 

174 bind_arguments: Optional[_BindArguments] = None, 

175 ): 

176 self.load_options = load_options 

177 self.execution_options = execution_options or _EMPTY_DICT 

178 self.bind_arguments = bind_arguments or _EMPTY_DICT 

179 self.compile_state = compile_state 

180 self.query = statement 

181 

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

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

184 # routines where a separate FromStatement is manufactured in the 

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

186 self.user_passed_query = user_passed_query 

187 

188 self.session = session 

189 self.loaders_require_buffering = False 

190 self.loaders_require_uniquing = False 

191 self.params = params 

192 self.top_level_context = load_options._sa_top_level_orm_context 

193 

194 cached_options = compile_state.select_statement._with_options 

195 uncached_options = user_passed_query._with_options 

196 

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

198 # propagated loader options will be present on loaded InstanceState 

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

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

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

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

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

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

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

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

207 # AliasedClass 

208 self.propagated_loader_options = tuple( 

209 opt._adapt_cached_option_to_uncached_option(self, uncached_opt) 

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

211 if opt.propagate_to_loaders 

212 ) 

213 

214 self.attributes = dict(compile_state.attributes) 

215 

216 self.autoflush = load_options._autoflush 

217 self.populate_existing = load_options._populate_existing 

218 self.invoke_all_eagers = load_options._invoke_all_eagers 

219 self.version_check = load_options._version_check 

220 self.refresh_state = load_options._refresh_state 

221 self.yield_per = load_options._yield_per 

222 self.identity_token = load_options._identity_token 

223 

224 def _get_top_level_context(self) -> QueryContext: 

225 return self.top_level_context or self 

226 

227 

228_orm_load_exec_options = util.immutabledict( 

229 {"_result_disable_adapt_to_context": True} 

230) 

231 

232 

233class _AbstractORMCompileState(CompileState): 

234 is_dml_returning = False 

235 

236 def _init_global_attributes( 

237 self, statement, compiler, *, toplevel, process_criteria_for_toplevel 

238 ): 

239 self.attributes = {} 

240 

241 if compiler is None: 

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

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

244 self.global_attributes = {} 

245 assert toplevel 

246 return 

247 else: 

248 self.global_attributes = ga = compiler._global_attributes 

249 

250 if toplevel: 

251 ga["toplevel_orm"] = True 

252 

253 if process_criteria_for_toplevel: 

254 for opt in statement._with_options: 

255 if opt._is_criteria_option: 

256 opt.process_compile_state(self) 

257 

258 return 

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

260 return 

261 

262 stack_0 = compiler.stack[0] 

263 

264 try: 

265 toplevel_stmt = stack_0["selectable"] 

266 except KeyError: 

267 pass 

268 else: 

269 for opt in toplevel_stmt._with_options: 

270 if opt._is_compile_state and opt._is_criteria_option: 

271 opt.process_compile_state(self) 

272 

273 ga["toplevel_orm"] = True 

274 

275 @classmethod 

276 def create_for_statement( 

277 cls, 

278 statement: Executable, 

279 compiler: SQLCompiler, 

280 **kw: Any, 

281 ) -> CompileState: 

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

283 

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

285 

286 For a Select object, this would be invoked from 

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

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

289 FromStatement._compiler_dispatch() that would be called by 

290 SQLCompiler.process(). 

291 """ 

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

293 

294 @classmethod 

295 def orm_pre_session_exec( 

296 cls, 

297 session, 

298 statement, 

299 params, 

300 execution_options, 

301 bind_arguments, 

302 is_pre_event, 

303 ): 

304 raise NotImplementedError() 

305 

306 @classmethod 

307 def orm_execute_statement( 

308 cls, 

309 session, 

310 statement, 

311 params, 

312 execution_options, 

313 bind_arguments, 

314 conn, 

315 ) -> Result: 

316 result = conn.execute( 

317 statement, params or {}, execution_options=execution_options 

318 ) 

319 return cls.orm_setup_cursor_result( 

320 session, 

321 statement, 

322 params, 

323 execution_options, 

324 bind_arguments, 

325 result, 

326 ) 

327 

328 @classmethod 

329 def orm_setup_cursor_result( 

330 cls, 

331 session, 

332 statement, 

333 params, 

334 execution_options, 

335 bind_arguments, 

336 result, 

337 ): 

338 raise NotImplementedError() 

339 

340 

341class _AutoflushOnlyORMCompileState(_AbstractORMCompileState): 

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

343 

344 @classmethod 

345 def orm_pre_session_exec( 

346 cls, 

347 session, 

348 statement, 

349 params, 

350 execution_options, 

351 bind_arguments, 

352 is_pre_event, 

353 ): 

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

355 # in an ORMExecuteState hook 

356 ( 

357 load_options, 

358 execution_options, 

359 ) = QueryContext.default_load_options.from_execution_options( 

360 "_sa_orm_load_options", 

361 { 

362 "autoflush", 

363 }, 

364 execution_options, 

365 statement._execution_options, 

366 ) 

367 

368 if not is_pre_event and load_options._autoflush: 

369 session._autoflush() 

370 

371 return statement, execution_options, params 

372 

373 @classmethod 

374 def orm_setup_cursor_result( 

375 cls, 

376 session, 

377 statement, 

378 params, 

379 execution_options, 

380 bind_arguments, 

381 result, 

382 ): 

383 return result 

384 

385 

386class _ORMCompileState(_AbstractORMCompileState): 

387 class default_compile_options(CacheableOptions): 

388 _cache_key_traversal = [ 

389 ("_use_legacy_query_style", InternalTraversal.dp_boolean), 

390 ("_for_statement", InternalTraversal.dp_boolean), 

391 ("_bake_ok", InternalTraversal.dp_boolean), 

392 ("_current_path", InternalTraversal.dp_has_cache_key), 

393 ("_enable_single_crit", InternalTraversal.dp_boolean), 

394 ("_enable_eagerloads", InternalTraversal.dp_boolean), 

395 ("_only_load_props", InternalTraversal.dp_plain_obj), 

396 ("_set_base_alias", InternalTraversal.dp_boolean), 

397 ("_for_refresh_state", InternalTraversal.dp_boolean), 

398 ("_render_for_subquery", InternalTraversal.dp_boolean), 

399 ("_is_star", InternalTraversal.dp_boolean), 

400 ] 

401 

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

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

404 # now this basically indicates we should use tablename_columnname 

405 # style labels. Generally indicates the statement originated 

406 # from a Query object. 

407 _use_legacy_query_style = False 

408 

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

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

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

412 _for_statement = False 

413 

414 _bake_ok = True 

415 _current_path = _path_registry 

416 _enable_single_crit = True 

417 _enable_eagerloads = True 

418 _only_load_props = None 

419 _set_base_alias = False 

420 _for_refresh_state = False 

421 _render_for_subquery = False 

422 _is_star = False 

423 

424 attributes: Dict[Any, Any] 

425 global_attributes: Dict[Any, Any] 

426 

427 statement: Union[ 

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

429 ] 

430 select_statement: Union[ 

431 Select[Unpack[TupleAny]], FromStatement[Unpack[TupleAny]] 

432 ] 

433 _entities: List[_QueryEntity] 

434 _polymorphic_adapters: Dict[_InternalEntityType, ORMAdapter] 

435 compile_options: Union[ 

436 Type[default_compile_options], default_compile_options 

437 ] 

438 _primary_entity: Optional[_QueryEntity] 

439 use_legacy_query_style: bool 

440 _label_convention: _LabelConventionCallable 

441 primary_columns: List[ColumnElement[Any]] 

442 secondary_columns: List[ColumnElement[Any]] 

443 dedupe_columns: Set[ColumnElement[Any]] 

444 create_eager_joins: List[ 

445 # TODO: this structure is set up by JoinedLoader 

446 TupleAny 

447 ] 

448 current_path: PathRegistry = _path_registry 

449 _has_mapper_entities = False 

450 

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

452 raise NotImplementedError() 

453 

454 @classmethod 

455 def create_for_statement( 

456 cls, 

457 statement: Executable, 

458 compiler: SQLCompiler, 

459 **kw: Any, 

460 ) -> _ORMCompileState: 

461 return cls._create_orm_context( 

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

463 toplevel=not compiler.stack, 

464 compiler=compiler, 

465 **kw, 

466 ) 

467 

468 @classmethod 

469 def _create_orm_context( 

470 cls, 

471 statement: Union[Select, FromStatement], 

472 *, 

473 toplevel: bool, 

474 compiler: Optional[SQLCompiler], 

475 **kw: Any, 

476 ) -> _ORMCompileState: 

477 raise NotImplementedError() 

478 

479 def _append_dedupe_col_collection(self, obj, col_collection): 

480 dedupe = self.dedupe_columns 

481 if obj not in dedupe: 

482 dedupe.add(obj) 

483 col_collection.append(obj) 

484 

485 @classmethod 

486 def _column_naming_convention( 

487 cls, label_style: SelectLabelStyle, legacy: bool 

488 ) -> _LabelConventionCallable: 

489 if legacy: 

490 

491 def name(col, col_name=None): 

492 if col_name: 

493 return col_name 

494 else: 

495 return getattr(col, "key") 

496 

497 return name 

498 else: 

499 return SelectState._column_naming_convention(label_style) 

500 

501 @classmethod 

502 def get_column_descriptions(cls, statement): 

503 return _column_descriptions(statement) 

504 

505 @classmethod 

506 def orm_pre_session_exec( 

507 cls, 

508 session, 

509 statement, 

510 params, 

511 execution_options, 

512 bind_arguments, 

513 is_pre_event, 

514 ): 

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

516 # in an ORMExecuteState hook 

517 ( 

518 load_options, 

519 execution_options, 

520 ) = QueryContext.default_load_options.from_execution_options( 

521 "_sa_orm_load_options", 

522 { 

523 "populate_existing", 

524 "autoflush", 

525 "yield_per", 

526 "identity_token", 

527 "sa_top_level_orm_context", 

528 }, 

529 execution_options, 

530 statement._execution_options, 

531 ) 

532 

533 # default execution options for ORM results: 

534 # 1. _result_disable_adapt_to_context=True 

535 # this will disable the ResultSetMetadata._adapt_to_context() 

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

537 # against the original SELECT statement before caching. 

538 

539 if "sa_top_level_orm_context" in execution_options: 

540 ctx = execution_options["sa_top_level_orm_context"] 

541 execution_options = ctx.query._execution_options.merge_with( 

542 ctx.execution_options, execution_options 

543 ) 

544 

545 if not execution_options: 

546 execution_options = _orm_load_exec_options 

547 else: 

548 execution_options = execution_options.union(_orm_load_exec_options) 

549 

550 # would have been placed here by legacy Query only 

551 if load_options._yield_per: 

552 execution_options = execution_options.union( 

553 {"yield_per": load_options._yield_per} 

554 ) 

555 

556 if ( 

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

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

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

560 ): 

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

562 execution_options.union( 

563 { 

564 "compiled_cache": None, 

565 "_cache_disable_reason": "excess depth for " 

566 "ORM loader options", 

567 } 

568 ) 

569 ) 

570 

571 bind_arguments["clause"] = statement 

572 

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

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

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

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

577 

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

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

580 # needs to be present as well. 

581 

582 try: 

583 plugin_subject = statement._propagate_attrs["plugin_subject"] 

584 except KeyError: 

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

586 else: 

587 if plugin_subject: 

588 bind_arguments["mapper"] = plugin_subject.mapper 

589 

590 if not is_pre_event and load_options._autoflush: 

591 session._autoflush() 

592 

593 return statement, execution_options, params 

594 

595 @classmethod 

596 def orm_setup_cursor_result( 

597 cls, 

598 session, 

599 statement, 

600 params, 

601 execution_options, 

602 bind_arguments, 

603 result, 

604 ): 

605 execution_context = result.context 

606 compile_state = execution_context.compiled.compile_state 

607 

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

609 # were passed to session.execute: 

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

611 # see test_query->test_legacy_tuple_old_select 

612 

613 load_options = execution_options.get( 

614 "_sa_orm_load_options", QueryContext.default_load_options 

615 ) 

616 

617 if compile_state.compile_options._is_star: 

618 return result 

619 

620 querycontext = QueryContext( 

621 compile_state, 

622 statement, 

623 statement, 

624 params, 

625 session, 

626 load_options, 

627 execution_options, 

628 bind_arguments, 

629 ) 

630 return loading.instances(result, querycontext) 

631 

632 @property 

633 def _lead_mapper_entities(self): 

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

635 

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

637 with_entities(), with_only_columns() 

638 

639 """ 

640 return [ 

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

642 ] 

643 

644 def _create_with_polymorphic_adapter(self, ext_info, selectable): 

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

646 if called for by the Mapper. 

647 

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

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

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

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

652 loading, and joined inheritance where a subquery is 

653 passed to with_polymorphic (which is completely unnecessary in modern 

654 use). 

655 

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

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

658 to an alias or subquery "legacy" ? 

659 

660 """ 

661 if ( 

662 not ext_info.is_aliased_class 

663 and ext_info.mapper.persist_selectable 

664 not in self._polymorphic_adapters 

665 ): 

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

667 self._mapper_loads_polymorphically_with( 

668 mp, 

669 ORMAdapter( 

670 _TraceAdaptRole.WITH_POLYMORPHIC_ADAPTER, 

671 mp, 

672 equivalents=mp._equivalent_columns, 

673 selectable=selectable, 

674 ), 

675 ) 

676 

677 def _mapper_loads_polymorphically_with(self, mapper, adapter): 

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

679 self._polymorphic_adapters[m2] = adapter 

680 

681 for m in m2.iterate_to_root(): 

682 self._polymorphic_adapters[m.local_table] = adapter 

683 

684 @classmethod 

685 def _create_entities_collection(cls, query, legacy): 

686 raise NotImplementedError( 

687 "this method only works for ORMSelectCompileState" 

688 ) 

689 

690 

691class _DMLReturningColFilter: 

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

693 

694 Has a subset of the interface used by 

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

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

697 DML statement. 

698 

699 """ 

700 

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

702 

703 def __init__(self, target_mapper, immediate_dml_mapper): 

704 if ( 

705 immediate_dml_mapper is not None 

706 and target_mapper.local_table 

707 is not immediate_dml_mapper.local_table 

708 ): 

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

710 self.mapper = immediate_dml_mapper 

711 else: 

712 # single inh, normal mappings, etc. 

713 self.mapper = target_mapper 

714 self.columns = self.columns = util.WeakPopulateDict( 

715 self.adapt_check_present # type: ignore 

716 ) 

717 

718 def __call__(self, col, as_filter): 

719 for cc in sql_util._find_columns(col): 

720 c2 = self.adapt_check_present(cc) 

721 if c2 is not None: 

722 return col 

723 else: 

724 return None 

725 

726 def adapt_check_present(self, col): 

727 raise NotImplementedError() 

728 

729 

730class _DMLBulkInsertReturningColFilter(_DMLReturningColFilter): 

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

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

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

734 example right now) 

735 

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

737 mapped table in a hierarchy. 

738 

739 """ 

740 

741 def adapt_check_present(self, col): 

742 mapper = self.mapper 

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

744 if prop is None: 

745 return None 

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

747 

748 

749class _DMLUpdateDeleteReturningColFilter(_DMLReturningColFilter): 

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

751 for ORM enabled UPDATE/DELETE 

752 

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

754 only direct persisted columns from the immediate selectable, not 

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

756 mappers for the UPDATE..FROM use case. 

757 

758 """ 

759 

760 def adapt_check_present(self, col): 

761 mapper = self.mapper 

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

763 if prop is not None: 

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

765 # column, not any kind of column_property expression 

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

767 

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

769 # user knows what they are doing 

770 return col 

771 

772 

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

774class _ORMFromStatementCompileState(_ORMCompileState): 

775 _from_obj_alias = None 

776 _has_mapper_entities = False 

777 

778 statement_container: FromStatement 

779 requested_statement: Union[SelectBase, TextClause, UpdateBase] 

780 dml_table: Optional[_DMLTableElement] = None 

781 

782 _has_orm_entities = False 

783 multi_row_eager_loaders = False 

784 eager_adding_joins = False 

785 compound_eager_adapter = None 

786 

787 extra_criteria_entities = _EMPTY_DICT 

788 eager_joins = _EMPTY_DICT 

789 

790 @classmethod 

791 def _create_orm_context( 

792 cls, 

793 statement: Union[Select, FromStatement], 

794 *, 

795 toplevel: bool, 

796 compiler: Optional[SQLCompiler], 

797 **kw: Any, 

798 ) -> _ORMFromStatementCompileState: 

799 statement_container = statement 

800 

801 assert isinstance(statement_container, FromStatement) 

802 

803 if compiler is not None and compiler.stack: 

804 raise sa_exc.CompileError( 

805 "The ORM FromStatement construct only supports being " 

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

807 "define how result rows should be returned." 

808 ) 

809 

810 self = cls.__new__(cls) 

811 self._primary_entity = None 

812 

813 self.use_legacy_query_style = ( 

814 statement_container._compile_options._use_legacy_query_style 

815 ) 

816 self.statement_container = self.select_statement = statement_container 

817 self.requested_statement = statement = statement_container.element 

818 

819 if statement.is_dml: 

820 self.dml_table = statement.table 

821 self.is_dml_returning = True 

822 

823 self._entities = [] 

824 self._polymorphic_adapters = {} 

825 

826 self.compile_options = statement_container._compile_options 

827 

828 if ( 

829 self.use_legacy_query_style 

830 and isinstance(statement, expression.SelectBase) 

831 and not statement._is_textual 

832 and not statement.is_dml 

833 and statement._label_style is LABEL_STYLE_NONE 

834 ): 

835 self.statement = statement.set_label_style( 

836 LABEL_STYLE_TABLENAME_PLUS_COL 

837 ) 

838 else: 

839 self.statement = statement 

840 

841 self._label_convention = self._column_naming_convention( 

842 ( 

843 statement._label_style 

844 if not statement._is_textual and not statement.is_dml 

845 else LABEL_STYLE_NONE 

846 ), 

847 self.use_legacy_query_style, 

848 ) 

849 

850 _QueryEntity.to_compile_state( 

851 self, 

852 statement_container._raw_columns, 

853 self._entities, 

854 is_current_entities=True, 

855 ) 

856 

857 self.current_path = statement_container._compile_options._current_path 

858 

859 self._init_global_attributes( 

860 statement_container, 

861 compiler, 

862 process_criteria_for_toplevel=False, 

863 toplevel=True, 

864 ) 

865 

866 if statement_container._with_options: 

867 for opt in statement_container._with_options: 

868 if opt._is_compile_state: 

869 opt.process_compile_state(self) 

870 

871 if statement_container._compile_state_funcs: 

872 for fn, key in statement_container._compile_state_funcs: 

873 fn(self) 

874 

875 self.primary_columns = [] 

876 self.secondary_columns = [] 

877 self.dedupe_columns = set() 

878 self.create_eager_joins = [] 

879 self._fallback_from_clauses = [] 

880 

881 self.order_by = None 

882 

883 if isinstance(self.statement, expression.TextClause): 

884 # TextClause has no "column" objects at all. for this case, 

885 # we generate columns from our _QueryEntity objects, then 

886 # flip on all the "please match no matter what" parameters. 

887 self.extra_criteria_entities = {} 

888 

889 for entity in self._entities: 

890 entity.setup_compile_state(self) 

891 

892 compiler._ordered_columns = compiler._textual_ordered_columns = ( 

893 False 

894 ) 

895 

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

897 # needed by test_query.py::TextTest 

898 compiler._loose_column_name_matching = True 

899 

900 for c in self.primary_columns: 

901 compiler.process( 

902 c, 

903 within_columns_clause=True, 

904 add_to_result_map=compiler._add_to_result_map, 

905 ) 

906 else: 

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

908 # have column objects already. After much 

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

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

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

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

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

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

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

916 self._from_obj_alias = ORMStatementAdapter( 

917 _TraceAdaptRole.ADAPT_FROM_STATEMENT, 

918 self.statement, 

919 adapt_on_names=statement_container._adapt_on_names, 

920 ) 

921 

922 return self 

923 

924 def _adapt_col_list(self, cols, current_adapter): 

925 return cols 

926 

927 def _get_current_adapter(self): 

928 return None 

929 

930 def setup_dml_returning_compile_state(self, dml_mapper): 

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

932 for RETURNING to return ORM objects and expressions 

933 

934 """ 

935 target_mapper = self.statement._propagate_attrs.get( 

936 "plugin_subject", None 

937 ) 

938 

939 if self.statement.is_insert: 

940 adapter = _DMLBulkInsertReturningColFilter( 

941 target_mapper, dml_mapper 

942 ) 

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

944 adapter = _DMLUpdateDeleteReturningColFilter( 

945 target_mapper, dml_mapper 

946 ) 

947 else: 

948 adapter = None 

949 

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

951 raise sa_exc.CompileError( 

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

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

954 ) 

955 

956 for entity in self._entities: 

957 entity.setup_dml_returning_compile_state(self, adapter) 

958 

959 

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

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

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

963 

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

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

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

967 

968 """ 

969 

970 __visit_name__ = "orm_from_statement" 

971 

972 _compile_options = _ORMFromStatementCompileState.default_compile_options 

973 

974 _compile_state_factory = _ORMFromStatementCompileState.create_for_statement 

975 

976 _for_update_arg = None 

977 

978 element: Union[ExecutableReturnsRows, TextClause] 

979 

980 _adapt_on_names: bool 

981 

982 _traverse_internals = [ 

983 ("_raw_columns", InternalTraversal.dp_clauseelement_list), 

984 ("element", InternalTraversal.dp_clauseelement), 

985 ] + ExecutableStatement._executable_traverse_internals 

986 

987 _cache_key_traversal = _traverse_internals + [ 

988 ("_compile_options", InternalTraversal.dp_has_cache_key) 

989 ] 

990 

991 is_from_statement = True 

992 

993 def __init__( 

994 self, 

995 entities: Iterable[_ColumnsClauseArgument[Any]], 

996 element: Union[ExecutableReturnsRows, TextClause], 

997 _adapt_on_names: bool = True, 

998 ): 

999 self._raw_columns = [ 

1000 coercions.expect( 

1001 roles.ColumnsClauseRole, 

1002 ent, 

1003 apply_propagate_attrs=self, 

1004 post_inspect=True, 

1005 ) 

1006 for ent in util.to_list(entities) 

1007 ] 

1008 self.element = element 

1009 self.is_dml = element.is_dml 

1010 self.is_select = element.is_select 

1011 self.is_delete = element.is_delete 

1012 self.is_insert = element.is_insert 

1013 self.is_update = element.is_update 

1014 self._label_style = ( 

1015 element._label_style if is_select_base(element) else None 

1016 ) 

1017 self._adapt_on_names = _adapt_on_names 

1018 

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

1020 """provide a fixed _compiler_dispatch method. 

1021 

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

1023 ``@compiles`` extension. 

1024 

1025 """ 

1026 

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

1028 

1029 toplevel = not compiler.stack 

1030 

1031 if toplevel: 

1032 compiler.compile_state = compile_state 

1033 

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

1035 

1036 @property 

1037 def column_descriptions(self): 

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

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

1040 

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

1042 of this feature. 

1043 

1044 .. seealso:: 

1045 

1046 :ref:`queryguide_inspection` - ORM background 

1047 

1048 """ 

1049 meth = cast( 

1050 _ORMSelectCompileState, SelectState.get_plugin_class(self) 

1051 ).get_column_descriptions 

1052 return meth(self) 

1053 

1054 def _ensure_disambiguated_names(self): 

1055 return self 

1056 

1057 def get_children(self, **kw): 

1058 yield from itertools.chain.from_iterable( 

1059 element._from_objects for element in self._raw_columns 

1060 ) 

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

1062 

1063 @property 

1064 def _all_selected_columns(self): 

1065 return self.element._all_selected_columns 

1066 

1067 @property 

1068 def _return_defaults(self): 

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

1070 

1071 @property 

1072 def _returning(self): 

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

1074 

1075 @property 

1076 def _inline(self): 

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

1078 

1079 

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

1081class _CompoundSelectCompileState( 

1082 _AutoflushOnlyORMCompileState, CompoundSelectState 

1083): 

1084 pass 

1085 

1086 

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

1088class _ORMSelectCompileState(_ORMCompileState, SelectState): 

1089 _already_joined_edges = () 

1090 

1091 _memoized_entities = _EMPTY_DICT 

1092 

1093 _from_obj_alias = None 

1094 _has_mapper_entities = False 

1095 

1096 _has_orm_entities = False 

1097 multi_row_eager_loaders = False 

1098 eager_adding_joins = False 

1099 compound_eager_adapter = None 

1100 

1101 correlate = None 

1102 correlate_except = None 

1103 _where_criteria = () 

1104 _having_criteria = () 

1105 

1106 @classmethod 

1107 def _create_orm_context( 

1108 cls, 

1109 statement: Union[Select, FromStatement], 

1110 *, 

1111 toplevel: bool, 

1112 compiler: Optional[SQLCompiler], 

1113 **kw: Any, 

1114 ) -> _ORMSelectCompileState: 

1115 

1116 self = cls.__new__(cls) 

1117 

1118 select_statement = statement 

1119 

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

1121 # have ORM level compile options. 

1122 statement._compile_options = cls.default_compile_options.safe_merge( 

1123 statement._compile_options 

1124 ) 

1125 

1126 if select_statement._execution_options: 

1127 # execution options should not impact the compilation of a 

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

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

1130 self.select_statement = select_statement._clone() 

1131 self.select_statement._execution_options = util.immutabledict() 

1132 else: 

1133 self.select_statement = select_statement 

1134 

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

1136 self.for_statement = select_statement._compile_options._for_statement 

1137 

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

1139 self.use_legacy_query_style = ( 

1140 select_statement._compile_options._use_legacy_query_style 

1141 ) 

1142 

1143 self._entities = [] 

1144 self._primary_entity = None 

1145 self._polymorphic_adapters = {} 

1146 

1147 self.compile_options = select_statement._compile_options 

1148 

1149 if not toplevel: 

1150 # for subqueries, turn off eagerloads and set 

1151 # "render_for_subquery". 

1152 self.compile_options += { 

1153 "_enable_eagerloads": False, 

1154 "_render_for_subquery": True, 

1155 } 

1156 

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

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

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

1160 # for new style ORM selects too. 

1161 if ( 

1162 self.use_legacy_query_style 

1163 and self.select_statement._label_style is LABEL_STYLE_LEGACY_ORM 

1164 ): 

1165 if not self.for_statement: 

1166 self.label_style = LABEL_STYLE_TABLENAME_PLUS_COL 

1167 else: 

1168 self.label_style = LABEL_STYLE_DISAMBIGUATE_ONLY 

1169 else: 

1170 self.label_style = self.select_statement._label_style 

1171 

1172 if select_statement._memoized_select_entities: 

1173 self._memoized_entities = { 

1174 memoized_entities: _QueryEntity.to_compile_state( 

1175 self, 

1176 memoized_entities._raw_columns, 

1177 [], 

1178 is_current_entities=False, 

1179 ) 

1180 for memoized_entities in ( 

1181 select_statement._memoized_select_entities 

1182 ) 

1183 } 

1184 

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

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

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

1188 # in the columns clause 

1189 self._label_convention = self._column_naming_convention( 

1190 statement._label_style, self.use_legacy_query_style 

1191 ) 

1192 

1193 _QueryEntity.to_compile_state( 

1194 self, 

1195 select_statement._raw_columns, 

1196 self._entities, 

1197 is_current_entities=True, 

1198 ) 

1199 

1200 self.current_path = select_statement._compile_options._current_path 

1201 

1202 self.eager_order_by = () 

1203 

1204 self._init_global_attributes( 

1205 select_statement, 

1206 compiler, 

1207 toplevel=toplevel, 

1208 process_criteria_for_toplevel=False, 

1209 ) 

1210 

1211 if toplevel and ( 

1212 select_statement._with_options 

1213 or select_statement._memoized_select_entities 

1214 ): 

1215 for ( 

1216 memoized_entities 

1217 ) in select_statement._memoized_select_entities: 

1218 for opt in memoized_entities._with_options: 

1219 if opt._is_compile_state: 

1220 opt.process_compile_state_replaced_entities( 

1221 self, 

1222 [ 

1223 ent 

1224 for ent in self._memoized_entities[ 

1225 memoized_entities 

1226 ] 

1227 if isinstance(ent, _MapperEntity) 

1228 ], 

1229 ) 

1230 

1231 for opt in self.select_statement._with_options: 

1232 if opt._is_compile_state: 

1233 opt.process_compile_state(self) 

1234 

1235 # uncomment to print out the context.attributes structure 

1236 # after it's been set up above 

1237 # self._dump_option_struct() 

1238 

1239 if select_statement._compile_state_funcs: 

1240 for fn, key in select_statement._compile_state_funcs: 

1241 fn(self) 

1242 

1243 self.primary_columns = [] 

1244 self.secondary_columns = [] 

1245 self.dedupe_columns = set() 

1246 self.eager_joins = {} 

1247 self.extra_criteria_entities = {} 

1248 self.create_eager_joins = [] 

1249 self._fallback_from_clauses = [] 

1250 

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

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

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

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

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

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

1257 self.from_clauses = self._normalize_froms( 

1258 info.selectable for info in select_statement._from_obj 

1259 ) 

1260 

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

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

1263 # and _setup_for_generate into three or four logical sections 

1264 self._setup_for_generate() 

1265 

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

1267 return self 

1268 

1269 def _dump_option_struct(self): 

1270 print("\n---------------------------------------------------\n") 

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

1272 for key in self.attributes: 

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

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

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

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

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

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

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

1280 

1281 def _setup_for_generate(self): 

1282 query = self.select_statement 

1283 

1284 self.statement = None 

1285 self._join_entities = () 

1286 

1287 if self.compile_options._set_base_alias: 

1288 # legacy Query only 

1289 self._set_select_from_alias() 

1290 

1291 for memoized_entities in query._memoized_select_entities: 

1292 if memoized_entities._setup_joins: 

1293 self._join( 

1294 memoized_entities._setup_joins, 

1295 self._memoized_entities[memoized_entities], 

1296 ) 

1297 

1298 if query._setup_joins: 

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

1300 

1301 current_adapter = self._get_current_adapter() 

1302 

1303 if query._where_criteria: 

1304 self._where_criteria = query._where_criteria 

1305 

1306 if current_adapter: 

1307 self._where_criteria = tuple( 

1308 current_adapter(crit, True) 

1309 for crit in self._where_criteria 

1310 ) 

1311 

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

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

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

1315 self.order_by = ( 

1316 self._adapt_col_list(query._order_by_clauses, current_adapter) 

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

1318 else query._order_by_clauses 

1319 ) 

1320 

1321 if query._having_criteria: 

1322 self._having_criteria = tuple( 

1323 current_adapter(crit, True) if current_adapter else crit 

1324 for crit in query._having_criteria 

1325 ) 

1326 

1327 self.group_by = ( 

1328 self._adapt_col_list( 

1329 util.flatten_iterator(query._group_by_clauses), current_adapter 

1330 ) 

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

1332 else query._group_by_clauses or None 

1333 ) 

1334 

1335 if self.eager_order_by: 

1336 adapter = self.from_clauses[0]._target_adapter 

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

1338 

1339 if query._distinct_on: 

1340 self.distinct_on = self._adapt_col_list( 

1341 query._distinct_on, current_adapter 

1342 ) 

1343 else: 

1344 self.distinct_on = () 

1345 

1346 self.distinct = query._distinct 

1347 

1348 self.syntax_extensions = { 

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

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

1351 } 

1352 

1353 if query._correlate: 

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

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

1356 # tables. 

1357 self.correlate = tuple( 

1358 util.flatten_iterator( 

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

1360 for s in query._correlate 

1361 ) 

1362 ) 

1363 elif query._correlate_except is not None: 

1364 self.correlate_except = tuple( 

1365 util.flatten_iterator( 

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

1367 for s in query._correlate_except 

1368 ) 

1369 ) 

1370 elif not query._auto_correlate: 

1371 self.correlate = (None,) 

1372 

1373 # PART II 

1374 

1375 self._for_update_arg = query._for_update_arg 

1376 

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

1378 raise sa_exc.CompileError( 

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

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

1381 ) 

1382 for entity in self._entities: 

1383 entity.setup_compile_state(self) 

1384 

1385 for rec in self.create_eager_joins: 

1386 strategy = rec[0] 

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

1388 

1389 # else "load from discrete FROMs" mode, 

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

1391 

1392 if self.compile_options._enable_single_crit: 

1393 self._adjust_for_extra_criteria() 

1394 

1395 if not self.primary_columns: 

1396 if self.compile_options._only_load_props: 

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

1398 

1399 raise sa_exc.InvalidRequestError( 

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

1401 ) 

1402 

1403 if not self.from_clauses: 

1404 self.from_clauses = list(self._fallback_from_clauses) 

1405 

1406 if self.order_by is False: 

1407 self.order_by = None 

1408 

1409 if self._should_nest_selectable: 

1410 self.statement = self._compound_eager_statement() 

1411 else: 

1412 self.statement = self._simple_statement() 

1413 

1414 if self.for_statement: 

1415 ezero = self._mapper_zero() 

1416 if ezero is not None: 

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

1418 # thing 

1419 self.statement = self.statement._annotate( 

1420 {"deepentity": ezero} 

1421 ) 

1422 

1423 @classmethod 

1424 def _create_entities_collection(cls, query, legacy): 

1425 """Creates a partial ORMSelectCompileState that includes 

1426 the full collection of _MapperEntity and other _QueryEntity objects. 

1427 

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

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

1430 

1431 """ 

1432 self = cls.__new__(cls) 

1433 

1434 self._entities = [] 

1435 self._primary_entity = None 

1436 self._polymorphic_adapters = {} 

1437 

1438 self._label_convention = self._column_naming_convention( 

1439 query._label_style, legacy 

1440 ) 

1441 

1442 # entities will also set up polymorphic adapters for mappers 

1443 # that have with_polymorphic configured 

1444 _QueryEntity.to_compile_state( 

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

1446 ) 

1447 return self 

1448 

1449 @classmethod 

1450 def determine_last_joined_entity(cls, statement): 

1451 setup_joins = statement._setup_joins 

1452 

1453 return _determine_last_joined_entity(setup_joins, None) 

1454 

1455 @classmethod 

1456 def all_selected_columns(cls, statement): 

1457 for element in statement._raw_columns: 

1458 if ( 

1459 element.is_selectable 

1460 and "entity_namespace" in element._annotations 

1461 ): 

1462 ens = element._annotations["entity_namespace"] 

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

1464 yield from _select_iterables([element]) 

1465 else: 

1466 yield from _select_iterables(ens._all_column_expressions) 

1467 else: 

1468 yield from _select_iterables([element]) 

1469 

1470 @classmethod 

1471 def get_columns_clause_froms(cls, statement): 

1472 return cls._normalize_froms( 

1473 itertools.chain.from_iterable( 

1474 ( 

1475 element._from_objects 

1476 if "parententity" not in element._annotations 

1477 else [ 

1478 element._annotations[ 

1479 "parententity" 

1480 ].__clause_element__() 

1481 ] 

1482 ) 

1483 for element in statement._raw_columns 

1484 ) 

1485 ) 

1486 

1487 @classmethod 

1488 def from_statement(cls, statement, from_statement): 

1489 from_statement = coercions.expect( 

1490 roles.ReturnsRowsRole, 

1491 from_statement, 

1492 apply_propagate_attrs=statement, 

1493 ) 

1494 

1495 stmt = FromStatement(statement._raw_columns, from_statement) 

1496 

1497 stmt.__dict__.update( 

1498 _with_options=statement._with_options, 

1499 _compile_state_funcs=statement._compile_state_funcs, 

1500 _execution_options=statement._execution_options, 

1501 _propagate_attrs=statement._propagate_attrs, 

1502 ) 

1503 return stmt 

1504 

1505 def _set_select_from_alias(self): 

1506 """used only for legacy Query cases""" 

1507 

1508 query = self.select_statement # query 

1509 

1510 assert self.compile_options._set_base_alias 

1511 assert len(query._from_obj) == 1 

1512 

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

1514 if adapter: 

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

1516 self._from_obj_alias = adapter 

1517 

1518 def _get_select_from_alias_from_obj(self, from_obj): 

1519 """used only for legacy Query cases""" 

1520 

1521 info = from_obj 

1522 

1523 if "parententity" in info._annotations: 

1524 info = info._annotations["parententity"] 

1525 

1526 if hasattr(info, "mapper"): 

1527 if not info.is_aliased_class: 

1528 raise sa_exc.ArgumentError( 

1529 "A selectable (FromClause) instance is " 

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

1531 ) 

1532 else: 

1533 return info._adapter 

1534 

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

1536 equivs = self._all_equivs() 

1537 assert info is info.selectable 

1538 return ORMStatementAdapter( 

1539 _TraceAdaptRole.LEGACY_SELECT_FROM_ALIAS, 

1540 info.selectable, 

1541 equivalents=equivs, 

1542 ) 

1543 else: 

1544 return None 

1545 

1546 def _mapper_zero(self): 

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

1548 return self._entities[0].mapper 

1549 

1550 def _entity_zero(self): 

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

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

1553 entity if specified.""" 

1554 

1555 for ent in self.from_clauses: 

1556 if "parententity" in ent._annotations: 

1557 return ent._annotations["parententity"] 

1558 for qent in self._entities: 

1559 if qent.entity_zero: 

1560 return qent.entity_zero 

1561 

1562 return None 

1563 

1564 def _only_full_mapper_zero(self, methname): 

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

1566 raise sa_exc.InvalidRequestError( 

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

1568 "a single mapped class." % methname 

1569 ) 

1570 return self._primary_entity.entity_zero 

1571 

1572 def _only_entity_zero(self, rationale=None): 

1573 if len(self._entities) > 1: 

1574 raise sa_exc.InvalidRequestError( 

1575 rationale 

1576 or "This operation requires a Query " 

1577 "against a single mapper." 

1578 ) 

1579 return self._entity_zero() 

1580 

1581 def _all_equivs(self): 

1582 equivs = {} 

1583 

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

1585 for ent in [ 

1586 ent 

1587 for ent in memoized_entities 

1588 if isinstance(ent, _MapperEntity) 

1589 ]: 

1590 equivs.update(ent.mapper._equivalent_columns) 

1591 

1592 for ent in [ 

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

1594 ]: 

1595 equivs.update(ent.mapper._equivalent_columns) 

1596 return equivs 

1597 

1598 def _compound_eager_statement(self): 

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

1600 # wrap the query inside a select, 

1601 # then append eager joins onto that 

1602 

1603 if self.order_by: 

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

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

1606 # elements are converted into label references. For the 

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

1608 # the original expressions outside of the label references 

1609 # in order to have them render. 

1610 unwrapped_order_by = [ 

1611 ( 

1612 elem.element 

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

1614 else elem 

1615 ) 

1616 for elem in self.order_by 

1617 ] 

1618 

1619 order_by_col_expr = sql_util.expand_column_list_from_order_by( 

1620 self.primary_columns, unwrapped_order_by 

1621 ) 

1622 else: 

1623 order_by_col_expr = [] 

1624 unwrapped_order_by = None 

1625 

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

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

1628 inner = self._select_statement( 

1629 self.primary_columns 

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

1631 self.from_clauses, 

1632 self._where_criteria, 

1633 self._having_criteria, 

1634 self.label_style, 

1635 self.order_by, 

1636 for_update=self._for_update_arg, 

1637 hints=self.select_statement._hints, 

1638 statement_hints=self.select_statement._statement_hints, 

1639 correlate=self.correlate, 

1640 correlate_except=self.correlate_except, 

1641 **self._select_args, 

1642 ) 

1643 

1644 inner = inner.alias() 

1645 

1646 equivs = self._all_equivs() 

1647 

1648 self.compound_eager_adapter = ORMStatementAdapter( 

1649 _TraceAdaptRole.COMPOUND_EAGER_STATEMENT, inner, equivalents=equivs 

1650 ) 

1651 

1652 statement = future.select( 

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

1654 ) 

1655 statement._label_style = self.label_style 

1656 

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

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

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

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

1661 if ( 

1662 self._for_update_arg is not None 

1663 and self._for_update_arg.of is None 

1664 ): 

1665 statement._for_update_arg = self._for_update_arg 

1666 

1667 from_clause = inner 

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

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

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

1671 # the join should be 

1672 from_clause = sql_util.splice_joins( 

1673 from_clause, eager_join, eager_join.stop_on 

1674 ) 

1675 

1676 statement.select_from.non_generative(statement, from_clause) 

1677 

1678 if unwrapped_order_by: 

1679 statement.order_by.non_generative( 

1680 statement, 

1681 *self.compound_eager_adapter.copy_and_process( 

1682 unwrapped_order_by 

1683 ), 

1684 ) 

1685 

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

1687 return statement 

1688 

1689 def _simple_statement(self): 

1690 statement = self._select_statement( 

1691 self.primary_columns + self.secondary_columns, 

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

1693 self._where_criteria, 

1694 self._having_criteria, 

1695 self.label_style, 

1696 self.order_by, 

1697 for_update=self._for_update_arg, 

1698 hints=self.select_statement._hints, 

1699 statement_hints=self.select_statement._statement_hints, 

1700 correlate=self.correlate, 

1701 correlate_except=self.correlate_except, 

1702 **self._select_args, 

1703 ) 

1704 

1705 if self.eager_order_by: 

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

1707 return statement 

1708 

1709 def _select_statement( 

1710 self, 

1711 raw_columns, 

1712 from_obj, 

1713 where_criteria, 

1714 having_criteria, 

1715 label_style, 

1716 order_by, 

1717 for_update, 

1718 hints, 

1719 statement_hints, 

1720 correlate, 

1721 correlate_except, 

1722 limit_clause, 

1723 offset_clause, 

1724 fetch_clause, 

1725 fetch_clause_options, 

1726 distinct, 

1727 distinct_on, 

1728 prefixes, 

1729 suffixes, 

1730 group_by, 

1731 independent_ctes, 

1732 independent_ctes_opts, 

1733 syntax_extensions, 

1734 ): 

1735 statement = Select._create_raw_select( 

1736 _raw_columns=raw_columns, 

1737 _from_obj=from_obj, 

1738 _label_style=label_style, 

1739 ) 

1740 

1741 if where_criteria: 

1742 statement._where_criteria = where_criteria 

1743 if having_criteria: 

1744 statement._having_criteria = having_criteria 

1745 

1746 if order_by: 

1747 statement._order_by_clauses += tuple(order_by) 

1748 

1749 if distinct_on: 

1750 statement._distinct = True 

1751 statement._distinct_on = distinct_on 

1752 elif distinct: 

1753 statement._distinct = True 

1754 

1755 if group_by: 

1756 statement._group_by_clauses += tuple(group_by) 

1757 

1758 statement._limit_clause = limit_clause 

1759 statement._offset_clause = offset_clause 

1760 statement._fetch_clause = fetch_clause 

1761 statement._fetch_clause_options = fetch_clause_options 

1762 statement._independent_ctes = independent_ctes 

1763 statement._independent_ctes_opts = independent_ctes_opts 

1764 if syntax_extensions: 

1765 statement._set_syntax_extensions(**syntax_extensions) 

1766 

1767 if prefixes: 

1768 statement._prefixes = prefixes 

1769 

1770 if suffixes: 

1771 statement._suffixes = suffixes 

1772 

1773 statement._for_update_arg = for_update 

1774 

1775 if hints: 

1776 statement._hints = hints 

1777 if statement_hints: 

1778 statement._statement_hints = statement_hints 

1779 

1780 if correlate: 

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

1782 

1783 if correlate_except is not None: 

1784 statement.correlate_except.non_generative( 

1785 statement, *correlate_except 

1786 ) 

1787 

1788 return statement 

1789 

1790 def _adapt_polymorphic_element(self, element): 

1791 if "parententity" in element._annotations: 

1792 search = element._annotations["parententity"] 

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

1794 if alias: 

1795 return alias.adapt_clause(element) 

1796 

1797 if isinstance(element, expression.FromClause): 

1798 search = element 

1799 elif hasattr(element, "table"): 

1800 search = element.table 

1801 else: 

1802 return None 

1803 

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

1805 if alias: 

1806 return alias.adapt_clause(element) 

1807 

1808 def _adapt_col_list(self, cols, current_adapter): 

1809 if current_adapter: 

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

1811 else: 

1812 return cols 

1813 

1814 def _get_current_adapter(self): 

1815 adapters = [] 

1816 

1817 if self._from_obj_alias: 

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

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

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

1821 # select_entity_from() 

1822 # 

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

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

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

1826 # to all SQL constructs. 

1827 adapters.append( 

1828 self._from_obj_alias.replace, 

1829 ) 

1830 

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

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

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

1834 if self._polymorphic_adapters: 

1835 adapters.append(self._adapt_polymorphic_element) 

1836 

1837 if not adapters: 

1838 return None 

1839 

1840 def _adapt_clause(clause, as_filter): 

1841 # do we adapt all expression elements or only those 

1842 # tagged as 'ORM' constructs ? 

1843 

1844 def replace(elem): 

1845 for adapter in adapters: 

1846 e = adapter(elem) 

1847 if e is not None: 

1848 return e 

1849 

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

1851 

1852 return _adapt_clause 

1853 

1854 def _join(self, args, entities_collection): 

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

1856 isouter = flags["isouter"] 

1857 full = flags["full"] 

1858 

1859 right = inspect(right) 

1860 if onclause is not None: 

1861 onclause = inspect(onclause) 

1862 

1863 if isinstance(right, interfaces.PropComparator): 

1864 if onclause is not None: 

1865 raise sa_exc.InvalidRequestError( 

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

1867 "to a relationship path as a target" 

1868 ) 

1869 

1870 onclause = right 

1871 right = None 

1872 elif "parententity" in right._annotations: 

1873 right = right._annotations["parententity"] 

1874 

1875 if onclause is None: 

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

1877 raise sa_exc.ArgumentError( 

1878 "Expected mapped entity or " 

1879 "selectable/table as join target" 

1880 ) 

1881 

1882 if isinstance(onclause, interfaces.PropComparator): 

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

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

1885 

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

1887 

1888 if right is None: 

1889 if of_type: 

1890 right = of_type 

1891 else: 

1892 right = onclause.property 

1893 

1894 try: 

1895 right = right.entity 

1896 except AttributeError as err: 

1897 raise sa_exc.ArgumentError( 

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

1899 "mapped entity" % right 

1900 ) from err 

1901 

1902 left = onclause._parententity 

1903 

1904 prop = onclause.property 

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

1906 onclause = prop 

1907 

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

1909 # case. 

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

1911 continue 

1912 

1913 if from_ is not None: 

1914 if ( 

1915 from_ is not left 

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

1917 is not left 

1918 ): 

1919 raise sa_exc.InvalidRequestError( 

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

1921 "of relationship attribute %s" 

1922 % ( 

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

1924 onclause, 

1925 ) 

1926 ) 

1927 elif from_ is not None: 

1928 prop = None 

1929 left = from_ 

1930 else: 

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

1932 # what the effective "left" side is 

1933 prop = left = None 

1934 

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

1936 # ORMJoin to add to our _from_obj tuple 

1937 self._join_left_to_right( 

1938 entities_collection, 

1939 left, 

1940 right, 

1941 onclause, 

1942 prop, 

1943 isouter, 

1944 full, 

1945 ) 

1946 

1947 def _join_left_to_right( 

1948 self, 

1949 entities_collection, 

1950 left, 

1951 right, 

1952 onclause, 

1953 prop, 

1954 outerjoin, 

1955 full, 

1956 ): 

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

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

1959 our _from_obj list (or augment an existing one) 

1960 

1961 """ 

1962 

1963 explicit_left = left 

1964 if left is None: 

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

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

1967 # entities 

1968 assert prop is None 

1969 ( 

1970 left, 

1971 replace_from_obj_index, 

1972 use_entity_index, 

1973 ) = self._join_determine_implicit_left_side( 

1974 entities_collection, left, right, onclause 

1975 ) 

1976 else: 

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

1978 # Determine where in our 

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

1980 # existing entity it corresponds to. 

1981 ( 

1982 replace_from_obj_index, 

1983 use_entity_index, 

1984 ) = self._join_place_explicit_left_side(entities_collection, left) 

1985 

1986 if left is right: 

1987 raise sa_exc.InvalidRequestError( 

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

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

1990 ) 

1991 

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

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

1994 # get back the new effective "right" side 

1995 r_info, right, onclause = self._join_check_and_adapt_right_side( 

1996 left, right, onclause, prop 

1997 ) 

1998 

1999 if not r_info.is_selectable: 

2000 extra_criteria = self._get_extra_criteria(r_info) 

2001 else: 

2002 extra_criteria = () 

2003 

2004 if replace_from_obj_index is not None: 

2005 # splice into an existing element in the 

2006 # self._from_obj list 

2007 left_clause = self.from_clauses[replace_from_obj_index] 

2008 

2009 if explicit_left is not None and onclause is None: 

2010 onclause = _ORMJoin._join_condition(explicit_left, right) 

2011 

2012 self.from_clauses = ( 

2013 self.from_clauses[:replace_from_obj_index] 

2014 + [ 

2015 _ORMJoin( 

2016 left_clause, 

2017 right, 

2018 onclause, 

2019 isouter=outerjoin, 

2020 full=full, 

2021 _extra_criteria=extra_criteria, 

2022 ) 

2023 ] 

2024 + self.from_clauses[replace_from_obj_index + 1 :] 

2025 ) 

2026 else: 

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

2028 if use_entity_index is not None: 

2029 # make use of _MapperEntity selectable, which is usually 

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

2031 # might be distinct 

2032 assert isinstance( 

2033 entities_collection[use_entity_index], _MapperEntity 

2034 ) 

2035 left_clause = entities_collection[use_entity_index].selectable 

2036 else: 

2037 left_clause = left 

2038 

2039 self.from_clauses = self.from_clauses + [ 

2040 _ORMJoin( 

2041 left_clause, 

2042 r_info, 

2043 onclause, 

2044 isouter=outerjoin, 

2045 full=full, 

2046 _extra_criteria=extra_criteria, 

2047 ) 

2048 ] 

2049 

2050 def _join_determine_implicit_left_side( 

2051 self, entities_collection, left, right, onclause 

2052 ): 

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

2054 determine if an existing FROM or entity in this query 

2055 can serve as the left hand side. 

2056 

2057 """ 

2058 

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

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

2061 # 

2062 # join(RightEntity) 

2063 # 

2064 # or 

2065 # 

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

2067 # 

2068 

2069 r_info = inspect(right) 

2070 

2071 replace_from_obj_index = use_entity_index = None 

2072 

2073 if self.from_clauses: 

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

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

2076 

2077 indexes = sql_util.find_left_clause_to_join_from( 

2078 self.from_clauses, r_info.selectable, onclause 

2079 ) 

2080 

2081 if len(indexes) == 1: 

2082 replace_from_obj_index = indexes[0] 

2083 left = self.from_clauses[replace_from_obj_index] 

2084 elif len(indexes) > 1: 

2085 raise sa_exc.InvalidRequestError( 

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

2087 "from, there are multiple FROMS which can " 

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

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

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

2091 "to help resolve the ambiguity." 

2092 ) 

2093 else: 

2094 raise sa_exc.InvalidRequestError( 

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

2096 "Please use the .select_from() " 

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

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

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

2100 ) 

2101 

2102 elif entities_collection: 

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

2104 # come from our list of entities. 

2105 

2106 potential = {} 

2107 for entity_index, ent in enumerate(entities_collection): 

2108 entity = ent.entity_zero_or_selectable 

2109 if entity is None: 

2110 continue 

2111 ent_info = inspect(entity) 

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

2113 continue 

2114 

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

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

2117 # against a series of columns from the same selectable 

2118 if isinstance(ent, _MapperEntity): 

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

2120 else: 

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

2122 

2123 all_clauses = list(potential.keys()) 

2124 indexes = sql_util.find_left_clause_to_join_from( 

2125 all_clauses, r_info.selectable, onclause 

2126 ) 

2127 

2128 if len(indexes) == 1: 

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

2130 elif len(indexes) > 1: 

2131 raise sa_exc.InvalidRequestError( 

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

2133 "from, there are multiple FROMS which can " 

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

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

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

2137 "to help resolve the ambiguity." 

2138 ) 

2139 else: 

2140 raise sa_exc.InvalidRequestError( 

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

2142 "Please use the .select_from() " 

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

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

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

2146 ) 

2147 else: 

2148 raise sa_exc.InvalidRequestError( 

2149 "No entities to join from; please use " 

2150 "select_from() to establish the left " 

2151 "entity/selectable of this join" 

2152 ) 

2153 

2154 return left, replace_from_obj_index, use_entity_index 

2155 

2156 def _join_place_explicit_left_side(self, entities_collection, left): 

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

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

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

2160 existing entities. 

2161 

2162 """ 

2163 

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

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

2166 # Relationship was given, e.g.: 

2167 # 

2168 # join(RightEntity, LeftEntity.right) 

2169 # 

2170 # or 

2171 # 

2172 # join(LeftEntity.right) 

2173 # 

2174 # as well as string forms: 

2175 # 

2176 # join(RightEntity, "right") 

2177 # 

2178 # etc. 

2179 # 

2180 

2181 replace_from_obj_index = use_entity_index = None 

2182 

2183 l_info = inspect(left) 

2184 if self.from_clauses: 

2185 indexes = sql_util.find_left_clause_that_matches_given( 

2186 self.from_clauses, l_info.selectable 

2187 ) 

2188 

2189 if len(indexes) > 1: 

2190 raise sa_exc.InvalidRequestError( 

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

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

2193 "ON clause." 

2194 ) 

2195 

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

2197 # an existing FROM in the self._from_obj tuple 

2198 if indexes: 

2199 replace_from_obj_index = indexes[0] 

2200 

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

2202 # self._from_obj tuple 

2203 

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

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

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

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

2208 if ( 

2209 replace_from_obj_index is None 

2210 and entities_collection 

2211 and hasattr(l_info, "mapper") 

2212 ): 

2213 for idx, ent in enumerate(entities_collection): 

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

2215 # matching? 

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

2217 use_entity_index = idx 

2218 break 

2219 

2220 return replace_from_obj_index, use_entity_index 

2221 

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

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

2224 according to polymorphic mapping translations, aliasing on the query 

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

2226 overlapping tables. 

2227 

2228 """ 

2229 

2230 l_info = inspect(left) 

2231 r_info = inspect(right) 

2232 

2233 overlap = False 

2234 

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

2236 # if the target is a joined inheritance mapping, 

2237 # be more liberal about auto-aliasing. 

2238 if right_mapper and ( 

2239 right_mapper.with_polymorphic 

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

2241 ): 

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

2243 if sql_util.selectables_overlap( 

2244 l_info.selectable, from_obj 

2245 ) and sql_util.selectables_overlap( 

2246 from_obj, r_info.selectable 

2247 ): 

2248 overlap = True 

2249 break 

2250 

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

2252 raise sa_exc.InvalidRequestError( 

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

2254 % l_info.selectable 

2255 ) 

2256 

2257 right_mapper, right_selectable, right_is_aliased = ( 

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

2259 r_info.selectable, 

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

2261 ) 

2262 

2263 if ( 

2264 right_mapper 

2265 and prop 

2266 and not right_mapper.common_parent(prop.mapper) 

2267 ): 

2268 raise sa_exc.InvalidRequestError( 

2269 "Join target %s does not correspond to " 

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

2271 ) 

2272 

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

2274 # purposes at the moment 

2275 if hasattr(r_info, "mapper"): 

2276 self._join_entities += (r_info,) 

2277 

2278 need_adapter = False 

2279 

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

2281 if r_info.is_clause_element: 

2282 if prop: 

2283 right_mapper = prop.mapper 

2284 

2285 if right_selectable._is_lateral: 

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

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

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

2289 current_adapter = self._get_current_adapter() 

2290 if current_adapter is not None: 

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

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

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

2294 right = current_adapter(right, True) 

2295 

2296 elif prop: 

2297 # joining to selectable with a mapper property given 

2298 # as the ON clause 

2299 

2300 if not right_selectable.is_derived_from( 

2301 right_mapper.persist_selectable 

2302 ): 

2303 raise sa_exc.InvalidRequestError( 

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

2305 % ( 

2306 right_selectable.description, 

2307 right_mapper.persist_selectable.description, 

2308 ) 

2309 ) 

2310 

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

2312 # turn it into an alias(). 

2313 if isinstance(right_selectable, expression.SelectBase): 

2314 right_selectable = coercions.expect( 

2315 roles.FromClauseRole, right_selectable 

2316 ) 

2317 need_adapter = True 

2318 

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

2320 right = AliasedClass(right_mapper, right_selectable) 

2321 

2322 util.warn_deprecated( 

2323 "An alias is being generated automatically against " 

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

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

2326 "Use the aliased() " 

2327 "construct explicitly, see the linked example." 

2328 % right_mapper, 

2329 "1.4", 

2330 code="xaj1", 

2331 ) 

2332 

2333 # test for overlap: 

2334 # orm/inheritance/relationships.py 

2335 # SelfReferentialM2MTest 

2336 aliased_entity = right_mapper and not right_is_aliased and overlap 

2337 

2338 if not need_adapter and aliased_entity: 

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

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

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

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

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

2344 # aliasing is desirable. 

2345 right = AliasedClass(right, flat=True) 

2346 need_adapter = True 

2347 

2348 util.warn( 

2349 "An alias is being generated automatically against " 

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

2351 "legacy pattern which may be " 

2352 "deprecated in a later release. Use the " 

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

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

2355 code="xaj2", 

2356 ) 

2357 

2358 if need_adapter: 

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

2360 # a warning has been emitted. 

2361 assert right_mapper 

2362 

2363 adapter = ORMAdapter( 

2364 _TraceAdaptRole.DEPRECATED_JOIN_ADAPT_RIGHT_SIDE, 

2365 inspect(right), 

2366 equivalents=right_mapper._equivalent_columns, 

2367 ) 

2368 

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

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

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

2372 # set are also adapted. 

2373 self._mapper_loads_polymorphically_with(right_mapper, adapter) 

2374 elif ( 

2375 not r_info.is_clause_element 

2376 and not right_is_aliased 

2377 and right_mapper._has_aliased_polymorphic_fromclause 

2378 ): 

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

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

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

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

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

2384 # strictly necessary. 

2385 # see test/orm/test_core_compilation.py 

2386 # ::RelNaturalAliasedJoinsTest::test_straight 

2387 # and similar 

2388 self._mapper_loads_polymorphically_with( 

2389 right_mapper, 

2390 ORMAdapter( 

2391 _TraceAdaptRole.WITH_POLYMORPHIC_ADAPTER_RIGHT_JOIN, 

2392 right_mapper, 

2393 selectable=right_mapper.selectable, 

2394 equivalents=right_mapper._equivalent_columns, 

2395 ), 

2396 ) 

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

2398 # adapters that are in place right now 

2399 if isinstance(onclause, expression.ClauseElement): 

2400 current_adapter = self._get_current_adapter() 

2401 if current_adapter: 

2402 onclause = current_adapter(onclause, True) 

2403 

2404 # if joining on a MapperProperty path, 

2405 # track the path to prevent redundant joins 

2406 if prop: 

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

2408 

2409 return inspect(right), right, onclause 

2410 

2411 @property 

2412 def _select_args(self): 

2413 return { 

2414 "limit_clause": self.select_statement._limit_clause, 

2415 "offset_clause": self.select_statement._offset_clause, 

2416 "distinct": self.distinct, 

2417 "distinct_on": self.distinct_on, 

2418 "prefixes": self.select_statement._prefixes, 

2419 "suffixes": self.select_statement._suffixes, 

2420 "group_by": self.group_by or None, 

2421 "fetch_clause": self.select_statement._fetch_clause, 

2422 "fetch_clause_options": ( 

2423 self.select_statement._fetch_clause_options 

2424 ), 

2425 "independent_ctes": self.select_statement._independent_ctes, 

2426 "independent_ctes_opts": ( 

2427 self.select_statement._independent_ctes_opts 

2428 ), 

2429 "syntax_extensions": self.syntax_extensions, 

2430 } 

2431 

2432 @property 

2433 def _should_nest_selectable(self): 

2434 kwargs = self._select_args 

2435 

2436 if not self.eager_adding_joins: 

2437 return False 

2438 

2439 return ( 

2440 ( 

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

2442 and self.multi_row_eager_loaders 

2443 ) 

2444 or ( 

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

2446 and self.multi_row_eager_loaders 

2447 ) 

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

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

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

2451 ) 

2452 

2453 def _get_extra_criteria(self, ext_info): 

2454 if ( 

2455 "additional_entity_criteria", 

2456 ext_info.mapper, 

2457 ) in self.global_attributes: 

2458 return tuple( 

2459 ae._resolve_where_criteria(ext_info) 

2460 for ae in self.global_attributes[ 

2461 ("additional_entity_criteria", ext_info.mapper) 

2462 ] 

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

2464 and ae._should_include(self) 

2465 ) 

2466 else: 

2467 return () 

2468 

2469 def _adjust_for_extra_criteria(self): 

2470 """Apply extra criteria filtering. 

2471 

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

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

2474 add criterion to the WHERE 

2475 clause of the given QueryContext such that only the appropriate 

2476 subtypes are selected from the total results. 

2477 

2478 Additionally, add WHERE criteria originating from LoaderCriteriaOptions 

2479 associated with the global context. 

2480 

2481 """ 

2482 

2483 for fromclause in self.from_clauses: 

2484 ext_info = fromclause._annotations.get("parententity", None) 

2485 

2486 if ( 

2487 ext_info 

2488 and ( 

2489 ext_info.mapper._single_table_criterion is not None 

2490 or ("additional_entity_criteria", ext_info.mapper) 

2491 in self.global_attributes 

2492 ) 

2493 and ext_info not in self.extra_criteria_entities 

2494 ): 

2495 self.extra_criteria_entities[ext_info] = ( 

2496 ext_info, 

2497 ext_info._adapter if ext_info.is_aliased_class else None, 

2498 ) 

2499 

2500 _where_criteria_to_add = () 

2501 

2502 merged_single_crit = collections.defaultdict( 

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

2504 ) 

2505 

2506 for ext_info, adapter in util.OrderedSet( 

2507 self.extra_criteria_entities.values() 

2508 ): 

2509 if ext_info in self._join_entities: 

2510 continue 

2511 

2512 # assemble single table inheritance criteria. 

2513 if ( 

2514 ext_info.is_aliased_class 

2515 and ext_info._base_alias()._is_with_polymorphic 

2516 ): 

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

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

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

2520 # of joined inheritance. 

2521 hierarchy_root = ext_info._base_alias() 

2522 else: 

2523 hierarchy_root = ext_info 

2524 

2525 single_crit_component = ( 

2526 hierarchy_root.mapper._single_table_criteria_component 

2527 ) 

2528 

2529 if single_crit_component is not None: 

2530 polymorphic_on, criteria = single_crit_component 

2531 

2532 polymorphic_on = polymorphic_on._annotate( 

2533 { 

2534 "parententity": hierarchy_root, 

2535 "parentmapper": hierarchy_root.mapper, 

2536 } 

2537 ) 

2538 

2539 list_of_single_crits, adapters = merged_single_crit[ 

2540 (hierarchy_root, polymorphic_on) 

2541 ] 

2542 list_of_single_crits.update(criteria) 

2543 if adapter: 

2544 adapters.add(adapter) 

2545 

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

2547 # with_loader_criteria() options 

2548 if not self.compile_options._for_refresh_state: 

2549 additional_entity_criteria = self._get_extra_criteria(ext_info) 

2550 _where_criteria_to_add += tuple( 

2551 adapter.traverse(crit) if adapter else crit 

2552 for crit in additional_entity_criteria 

2553 ) 

2554 

2555 # merge together single table inheritance criteria keyed to 

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

2557 for (ext_info, polymorphic_on), ( 

2558 merged_crit, 

2559 adapters, 

2560 ) in merged_single_crit.items(): 

2561 new_crit = polymorphic_on.in_(merged_crit) 

2562 for adapter in adapters: 

2563 new_crit = adapter.traverse(new_crit) 

2564 _where_criteria_to_add += (new_crit,) 

2565 

2566 current_adapter = self._get_current_adapter() 

2567 if current_adapter: 

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

2569 # have one, and concatenate to final WHERE criteria 

2570 for crit in _where_criteria_to_add: 

2571 crit = current_adapter(crit, False) 

2572 self._where_criteria += (crit,) 

2573 else: 

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

2575 self._where_criteria += _where_criteria_to_add 

2576 

2577 

2578def _column_descriptions( 

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

2580 compile_state: Optional[_ORMSelectCompileState] = None, 

2581 legacy: bool = False, 

2582) -> List[ORMColumnDescription]: 

2583 if compile_state is None: 

2584 compile_state = _ORMSelectCompileState._create_entities_collection( 

2585 query_or_select_stmt, legacy=legacy 

2586 ) 

2587 ctx = compile_state 

2588 d = [ 

2589 { 

2590 "name": ent._label_name, 

2591 "type": ent.type, 

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

2593 "expr": ent.expr, 

2594 "entity": ( 

2595 getattr(insp_ent, "entity", None) 

2596 if ent.entity_zero is not None 

2597 and not insp_ent.is_clause_element 

2598 else None 

2599 ), 

2600 } 

2601 for ent, insp_ent in [ 

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

2603 ] 

2604 ] 

2605 return d 

2606 

2607 

2608def _legacy_filter_by_entity_zero( 

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

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

2611 self = query_or_augmented_select 

2612 if self._setup_joins: 

2613 _last_joined_entity = self._last_joined_entity 

2614 if _last_joined_entity is not None: 

2615 return _last_joined_entity 

2616 

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

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

2619 

2620 return _entity_from_pre_ent_zero(self) 

2621 

2622 

2623def _entity_from_pre_ent_zero( 

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

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

2626 self = query_or_augmented_select 

2627 if not self._raw_columns: 

2628 return None 

2629 

2630 ent = self._raw_columns[0] 

2631 

2632 if "parententity" in ent._annotations: 

2633 return ent._annotations["parententity"] 

2634 elif isinstance(ent, ORMColumnsClauseRole): 

2635 return ent.entity 

2636 elif "bundle" in ent._annotations: 

2637 return ent._annotations["bundle"] 

2638 else: 

2639 return ent 

2640 

2641 

2642def _determine_last_joined_entity( 

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

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

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

2646 if not setup_joins: 

2647 return None 

2648 

2649 (target, onclause, from_, flags) = setup_joins[-1] 

2650 

2651 if isinstance( 

2652 target, 

2653 attributes.QueryableAttribute, 

2654 ): 

2655 return target.entity 

2656 else: 

2657 return target 

2658 

2659 

2660class _QueryEntity: 

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

2662 

2663 __slots__ = () 

2664 

2665 supports_single_entity: bool 

2666 

2667 _non_hashable_value = False 

2668 _null_column_type = False 

2669 use_id_for_hash = False 

2670 

2671 _label_name: Optional[str] 

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

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

2674 entity_zero: Optional[_InternalEntityType] 

2675 

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

2677 raise NotImplementedError() 

2678 

2679 def setup_dml_returning_compile_state( 

2680 self, 

2681 compile_state: _ORMCompileState, 

2682 adapter: Optional[_DMLReturningColFilter], 

2683 ) -> None: 

2684 raise NotImplementedError() 

2685 

2686 def row_processor(self, context, result): 

2687 raise NotImplementedError() 

2688 

2689 @classmethod 

2690 def to_compile_state( 

2691 cls, compile_state, entities, entities_collection, is_current_entities 

2692 ): 

2693 for idx, entity in enumerate(entities): 

2694 if entity._is_lambda_element: 

2695 if entity._is_sequence: 

2696 cls.to_compile_state( 

2697 compile_state, 

2698 entity._resolved, 

2699 entities_collection, 

2700 is_current_entities, 

2701 ) 

2702 continue 

2703 else: 

2704 entity = entity._resolved 

2705 

2706 if entity.is_clause_element: 

2707 if entity.is_selectable: 

2708 if "parententity" in entity._annotations: 

2709 _MapperEntity( 

2710 compile_state, 

2711 entity, 

2712 entities_collection, 

2713 is_current_entities, 

2714 ) 

2715 else: 

2716 _ColumnEntity._for_columns( 

2717 compile_state, 

2718 entity._select_iterable, 

2719 entities_collection, 

2720 idx, 

2721 is_current_entities, 

2722 ) 

2723 else: 

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

2725 _BundleEntity( 

2726 compile_state, 

2727 entity, 

2728 entities_collection, 

2729 is_current_entities, 

2730 ) 

2731 elif entity._is_clause_list: 

2732 # this is legacy only - test_composites.py 

2733 # test_query_cols_legacy 

2734 _ColumnEntity._for_columns( 

2735 compile_state, 

2736 entity._select_iterable, 

2737 entities_collection, 

2738 idx, 

2739 is_current_entities, 

2740 ) 

2741 else: 

2742 _ColumnEntity._for_columns( 

2743 compile_state, 

2744 [entity], 

2745 entities_collection, 

2746 idx, 

2747 is_current_entities, 

2748 ) 

2749 elif entity.is_bundle: 

2750 _BundleEntity(compile_state, entity, entities_collection) 

2751 

2752 return entities_collection 

2753 

2754 

2755class _MapperEntity(_QueryEntity): 

2756 """mapper/class/AliasedClass entity""" 

2757 

2758 __slots__ = ( 

2759 "expr", 

2760 "mapper", 

2761 "entity_zero", 

2762 "is_aliased_class", 

2763 "path", 

2764 "_extra_entities", 

2765 "_label_name", 

2766 "_with_polymorphic_mappers", 

2767 "selectable", 

2768 "_polymorphic_discriminator", 

2769 ) 

2770 

2771 expr: _InternalEntityType 

2772 mapper: Mapper[Any] 

2773 entity_zero: _InternalEntityType 

2774 is_aliased_class: bool 

2775 path: PathRegistry 

2776 _label_name: str 

2777 

2778 def __init__( 

2779 self, compile_state, entity, entities_collection, is_current_entities 

2780 ): 

2781 entities_collection.append(self) 

2782 if is_current_entities: 

2783 if compile_state._primary_entity is None: 

2784 compile_state._primary_entity = self 

2785 compile_state._has_mapper_entities = True 

2786 compile_state._has_orm_entities = True 

2787 

2788 entity = entity._annotations["parententity"] 

2789 entity._post_inspect 

2790 ext_info = self.entity_zero = entity 

2791 entity = ext_info.entity 

2792 

2793 self.expr = entity 

2794 self.mapper = mapper = ext_info.mapper 

2795 

2796 self._extra_entities = (self.expr,) 

2797 

2798 if ext_info.is_aliased_class: 

2799 self._label_name = ext_info.name 

2800 else: 

2801 self._label_name = mapper.class_.__name__ 

2802 

2803 self.is_aliased_class = ext_info.is_aliased_class 

2804 self.path = ext_info._path_registry 

2805 

2806 self.selectable = ext_info.selectable 

2807 self._with_polymorphic_mappers = ext_info.with_polymorphic_mappers 

2808 self._polymorphic_discriminator = ext_info.polymorphic_on 

2809 

2810 if mapper._should_select_with_poly_adapter: 

2811 compile_state._create_with_polymorphic_adapter( 

2812 ext_info, self.selectable 

2813 ) 

2814 

2815 supports_single_entity = True 

2816 

2817 _non_hashable_value = True 

2818 use_id_for_hash = True 

2819 

2820 @property 

2821 def type(self): 

2822 return self.mapper.class_ 

2823 

2824 @property 

2825 def entity_zero_or_selectable(self): 

2826 return self.entity_zero 

2827 

2828 def corresponds_to(self, entity): 

2829 return _entity_corresponds_to(self.entity_zero, entity) 

2830 

2831 def _get_entity_clauses(self, compile_state): 

2832 adapter = None 

2833 

2834 if not self.is_aliased_class: 

2835 if compile_state._polymorphic_adapters: 

2836 adapter = compile_state._polymorphic_adapters.get( 

2837 self.mapper, None 

2838 ) 

2839 else: 

2840 adapter = self.entity_zero._adapter 

2841 

2842 if adapter: 

2843 if compile_state._from_obj_alias: 

2844 ret = adapter.wrap(compile_state._from_obj_alias) 

2845 else: 

2846 ret = adapter 

2847 else: 

2848 ret = compile_state._from_obj_alias 

2849 

2850 return ret 

2851 

2852 def row_processor(self, context, result): 

2853 compile_state = context.compile_state 

2854 adapter = self._get_entity_clauses(compile_state) 

2855 

2856 if compile_state.compound_eager_adapter and adapter: 

2857 adapter = adapter.wrap(compile_state.compound_eager_adapter) 

2858 elif not adapter: 

2859 adapter = compile_state.compound_eager_adapter 

2860 

2861 if compile_state._primary_entity is self: 

2862 only_load_props = compile_state.compile_options._only_load_props 

2863 refresh_state = context.refresh_state 

2864 else: 

2865 only_load_props = refresh_state = None 

2866 

2867 _instance = loading._instance_processor( 

2868 self, 

2869 self.mapper, 

2870 context, 

2871 result, 

2872 self.path, 

2873 adapter, 

2874 only_load_props=only_load_props, 

2875 refresh_state=refresh_state, 

2876 polymorphic_discriminator=self._polymorphic_discriminator, 

2877 ) 

2878 

2879 return _instance, self._label_name, self._extra_entities 

2880 

2881 def setup_dml_returning_compile_state( 

2882 self, 

2883 compile_state: _ORMCompileState, 

2884 adapter: Optional[_DMLReturningColFilter], 

2885 ) -> None: 

2886 loading._setup_entity_query( 

2887 compile_state, 

2888 self.mapper, 

2889 self, 

2890 self.path, 

2891 adapter, 

2892 compile_state.primary_columns, 

2893 with_polymorphic=self._with_polymorphic_mappers, 

2894 only_load_props=compile_state.compile_options._only_load_props, 

2895 polymorphic_discriminator=self._polymorphic_discriminator, 

2896 ) 

2897 

2898 def setup_compile_state(self, compile_state): 

2899 adapter = self._get_entity_clauses(compile_state) 

2900 

2901 single_table_crit = self.mapper._single_table_criterion 

2902 if ( 

2903 single_table_crit is not None 

2904 or ("additional_entity_criteria", self.mapper) 

2905 in compile_state.global_attributes 

2906 ): 

2907 ext_info = self.entity_zero 

2908 compile_state.extra_criteria_entities[ext_info] = ( 

2909 ext_info, 

2910 ext_info._adapter if ext_info.is_aliased_class else None, 

2911 ) 

2912 

2913 loading._setup_entity_query( 

2914 compile_state, 

2915 self.mapper, 

2916 self, 

2917 self.path, 

2918 adapter, 

2919 compile_state.primary_columns, 

2920 with_polymorphic=self._with_polymorphic_mappers, 

2921 only_load_props=compile_state.compile_options._only_load_props, 

2922 polymorphic_discriminator=self._polymorphic_discriminator, 

2923 ) 

2924 compile_state._fallback_from_clauses.append(self.selectable) 

2925 

2926 

2927class _BundleEntity(_QueryEntity): 

2928 _extra_entities = () 

2929 

2930 __slots__ = ( 

2931 "bundle", 

2932 "expr", 

2933 "type", 

2934 "_label_name", 

2935 "_entities", 

2936 "supports_single_entity", 

2937 ) 

2938 

2939 _entities: List[_QueryEntity] 

2940 bundle: Bundle 

2941 type: Type[Any] 

2942 _label_name: str 

2943 supports_single_entity: bool 

2944 expr: Bundle 

2945 

2946 def __init__( 

2947 self, 

2948 compile_state, 

2949 expr, 

2950 entities_collection, 

2951 is_current_entities, 

2952 setup_entities=True, 

2953 parent_bundle=None, 

2954 ): 

2955 compile_state._has_orm_entities = True 

2956 

2957 expr = expr._annotations["bundle"] 

2958 if parent_bundle: 

2959 parent_bundle._entities.append(self) 

2960 else: 

2961 entities_collection.append(self) 

2962 

2963 if isinstance( 

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

2965 ): 

2966 bundle = expr.__clause_element__() 

2967 else: 

2968 bundle = expr 

2969 

2970 self.bundle = self.expr = bundle 

2971 self.type = type(bundle) 

2972 self._label_name = bundle.name 

2973 self._entities = [] 

2974 

2975 if setup_entities: 

2976 for expr in bundle.exprs: 

2977 if "bundle" in expr._annotations: 

2978 _BundleEntity( 

2979 compile_state, 

2980 expr, 

2981 entities_collection, 

2982 is_current_entities, 

2983 parent_bundle=self, 

2984 ) 

2985 elif isinstance(expr, Bundle): 

2986 _BundleEntity( 

2987 compile_state, 

2988 expr, 

2989 entities_collection, 

2990 is_current_entities, 

2991 parent_bundle=self, 

2992 ) 

2993 else: 

2994 _ORMColumnEntity._for_columns( 

2995 compile_state, 

2996 [expr], 

2997 entities_collection, 

2998 None, 

2999 is_current_entities, 

3000 parent_bundle=self, 

3001 ) 

3002 

3003 self.supports_single_entity = self.bundle.single_entity 

3004 

3005 @property 

3006 def mapper(self): 

3007 ezero = self.entity_zero 

3008 if ezero is not None: 

3009 return ezero.mapper 

3010 else: 

3011 return None 

3012 

3013 @property 

3014 def entity_zero(self): 

3015 for ent in self._entities: 

3016 ezero = ent.entity_zero 

3017 if ezero is not None: 

3018 return ezero 

3019 else: 

3020 return None 

3021 

3022 def corresponds_to(self, entity): 

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

3024 # we are working around it 

3025 return False 

3026 

3027 @property 

3028 def entity_zero_or_selectable(self): 

3029 for ent in self._entities: 

3030 ezero = ent.entity_zero_or_selectable 

3031 if ezero is not None: 

3032 return ezero 

3033 else: 

3034 return None 

3035 

3036 def setup_compile_state(self, compile_state): 

3037 for ent in self._entities: 

3038 ent.setup_compile_state(compile_state) 

3039 

3040 def setup_dml_returning_compile_state( 

3041 self, 

3042 compile_state: _ORMCompileState, 

3043 adapter: Optional[_DMLReturningColFilter], 

3044 ) -> None: 

3045 return self.setup_compile_state(compile_state) 

3046 

3047 def row_processor(self, context, result): 

3048 procs, labels, extra = zip( 

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

3050 ) 

3051 

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

3053 

3054 return proc, self._label_name, self._extra_entities 

3055 

3056 

3057class _ColumnEntity(_QueryEntity): 

3058 __slots__ = ( 

3059 "_fetch_column", 

3060 "_row_processor", 

3061 "raw_column_index", 

3062 "translate_raw_column", 

3063 ) 

3064 

3065 @classmethod 

3066 def _for_columns( 

3067 cls, 

3068 compile_state, 

3069 columns, 

3070 entities_collection, 

3071 raw_column_index, 

3072 is_current_entities, 

3073 parent_bundle=None, 

3074 ): 

3075 for column in columns: 

3076 annotations = column._annotations 

3077 if "parententity" in annotations: 

3078 _entity = annotations["parententity"] 

3079 else: 

3080 _entity = sql_util.extract_first_column_annotation( 

3081 column, "parententity" 

3082 ) 

3083 

3084 if _entity: 

3085 if "identity_token" in column._annotations: 

3086 _IdentityTokenEntity( 

3087 compile_state, 

3088 column, 

3089 entities_collection, 

3090 _entity, 

3091 raw_column_index, 

3092 is_current_entities, 

3093 parent_bundle=parent_bundle, 

3094 ) 

3095 else: 

3096 _ORMColumnEntity( 

3097 compile_state, 

3098 column, 

3099 entities_collection, 

3100 _entity, 

3101 raw_column_index, 

3102 is_current_entities, 

3103 parent_bundle=parent_bundle, 

3104 ) 

3105 else: 

3106 _RawColumnEntity( 

3107 compile_state, 

3108 column, 

3109 entities_collection, 

3110 raw_column_index, 

3111 is_current_entities, 

3112 parent_bundle=parent_bundle, 

3113 ) 

3114 

3115 @property 

3116 def type(self): 

3117 return self.column.type 

3118 

3119 @property 

3120 def _non_hashable_value(self): 

3121 return not self.column.type.hashable 

3122 

3123 @property 

3124 def _null_column_type(self): 

3125 return self.column.type._isnull 

3126 

3127 def row_processor(self, context, result): 

3128 compile_state = context.compile_state 

3129 

3130 # the resulting callable is entirely cacheable so just return 

3131 # it if we already made one 

3132 if self._row_processor is not None: 

3133 getter, label_name, extra_entities = self._row_processor 

3134 if self.translate_raw_column: 

3135 extra_entities += ( 

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

3137 ) 

3138 

3139 return getter, label_name, extra_entities 

3140 

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

3142 # setup_compile_state, to avoid doing redundant work 

3143 if self._fetch_column is not None: 

3144 column = self._fetch_column 

3145 else: 

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

3147 # and setup_compile_state may not have been called. 

3148 column = self.column 

3149 

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

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

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

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

3154 if compile_state._from_obj_alias: 

3155 column = compile_state._from_obj_alias.columns[column] 

3156 

3157 if column._annotations: 

3158 # annotated columns perform more slowly in compiler and 

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

3160 column = column._deannotate() 

3161 

3162 if compile_state.compound_eager_adapter: 

3163 column = compile_state.compound_eager_adapter.columns[column] 

3164 

3165 getter = result._getter(column) 

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

3167 self._row_processor = ret 

3168 

3169 if self.translate_raw_column: 

3170 extra_entities = self._extra_entities + ( 

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

3172 ) 

3173 return getter, self._label_name, extra_entities 

3174 else: 

3175 return ret 

3176 

3177 

3178class _RawColumnEntity(_ColumnEntity): 

3179 entity_zero = None 

3180 mapper = None 

3181 supports_single_entity = False 

3182 

3183 __slots__ = ( 

3184 "expr", 

3185 "column", 

3186 "_label_name", 

3187 "entity_zero_or_selectable", 

3188 "_extra_entities", 

3189 ) 

3190 

3191 def __init__( 

3192 self, 

3193 compile_state, 

3194 column, 

3195 entities_collection, 

3196 raw_column_index, 

3197 is_current_entities, 

3198 parent_bundle=None, 

3199 ): 

3200 self.expr = column 

3201 self.raw_column_index = raw_column_index 

3202 self.translate_raw_column = raw_column_index is not None 

3203 

3204 if column._is_star: 

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

3206 

3207 if not is_current_entities or column._is_text_clause: 

3208 self._label_name = None 

3209 else: 

3210 if parent_bundle: 

3211 self._label_name = column._proxy_key 

3212 else: 

3213 self._label_name = compile_state._label_convention(column) 

3214 

3215 if parent_bundle: 

3216 parent_bundle._entities.append(self) 

3217 else: 

3218 entities_collection.append(self) 

3219 

3220 self.column = column 

3221 self.entity_zero_or_selectable = ( 

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

3223 ) 

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

3225 self._fetch_column = self._row_processor = None 

3226 

3227 def corresponds_to(self, entity): 

3228 return False 

3229 

3230 def setup_dml_returning_compile_state( 

3231 self, 

3232 compile_state: _ORMCompileState, 

3233 adapter: Optional[_DMLReturningColFilter], 

3234 ) -> None: 

3235 return self.setup_compile_state(compile_state) 

3236 

3237 def setup_compile_state(self, compile_state): 

3238 current_adapter = compile_state._get_current_adapter() 

3239 if current_adapter: 

3240 column = current_adapter(self.column, False) 

3241 if column is None: 

3242 return 

3243 else: 

3244 column = self.column 

3245 

3246 if column._annotations: 

3247 # annotated columns perform more slowly in compiler and 

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

3249 column = column._deannotate() 

3250 

3251 compile_state.dedupe_columns.add(column) 

3252 compile_state.primary_columns.append(column) 

3253 self._fetch_column = column 

3254 

3255 

3256class _ORMColumnEntity(_ColumnEntity): 

3257 """Column/expression based entity.""" 

3258 

3259 supports_single_entity = False 

3260 

3261 __slots__ = ( 

3262 "expr", 

3263 "mapper", 

3264 "column", 

3265 "_label_name", 

3266 "entity_zero_or_selectable", 

3267 "entity_zero", 

3268 "_extra_entities", 

3269 ) 

3270 

3271 def __init__( 

3272 self, 

3273 compile_state, 

3274 column, 

3275 entities_collection, 

3276 parententity, 

3277 raw_column_index, 

3278 is_current_entities, 

3279 parent_bundle=None, 

3280 ): 

3281 annotations = column._annotations 

3282 

3283 _entity = parententity 

3284 

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

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

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

3288 # within internal loaders. 

3289 

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

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

3292 if orm_key: 

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

3294 self.translate_raw_column = False 

3295 else: 

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

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

3298 # include this column position from the invoked statement 

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

3300 # it can be targeted by identity after caching 

3301 self.expr = column 

3302 self.translate_raw_column = raw_column_index is not None 

3303 

3304 self.raw_column_index = raw_column_index 

3305 

3306 if is_current_entities: 

3307 if parent_bundle: 

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

3309 else: 

3310 self._label_name = compile_state._label_convention( 

3311 column, col_name=orm_key 

3312 ) 

3313 else: 

3314 self._label_name = None 

3315 

3316 _entity._post_inspect 

3317 self.entity_zero = self.entity_zero_or_selectable = ezero = _entity 

3318 self.mapper = mapper = _entity.mapper 

3319 

3320 if parent_bundle: 

3321 parent_bundle._entities.append(self) 

3322 else: 

3323 entities_collection.append(self) 

3324 

3325 compile_state._has_orm_entities = True 

3326 

3327 self.column = column 

3328 

3329 self._fetch_column = self._row_processor = None 

3330 

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

3332 

3333 if mapper._should_select_with_poly_adapter: 

3334 compile_state._create_with_polymorphic_adapter( 

3335 ezero, ezero.selectable 

3336 ) 

3337 

3338 def corresponds_to(self, entity): 

3339 if _is_aliased_class(entity): 

3340 # TODO: polymorphic subclasses ? 

3341 return entity is self.entity_zero 

3342 else: 

3343 return not _is_aliased_class( 

3344 self.entity_zero 

3345 ) and entity.common_parent(self.entity_zero) 

3346 

3347 def setup_dml_returning_compile_state( 

3348 self, 

3349 compile_state: _ORMCompileState, 

3350 adapter: Optional[_DMLReturningColFilter], 

3351 ) -> None: 

3352 

3353 self._fetch_column = column = self.column 

3354 if adapter: 

3355 column = adapter(column, False) 

3356 

3357 if column is not None: 

3358 compile_state.dedupe_columns.add(column) 

3359 compile_state.primary_columns.append(column) 

3360 

3361 def setup_compile_state(self, compile_state): 

3362 current_adapter = compile_state._get_current_adapter() 

3363 if current_adapter: 

3364 column = current_adapter(self.column, False) 

3365 if column is None: 

3366 assert compile_state.is_dml_returning 

3367 self._fetch_column = self.column 

3368 return 

3369 else: 

3370 column = self.column 

3371 

3372 ezero = self.entity_zero 

3373 

3374 single_table_crit = self.mapper._single_table_criterion 

3375 if ( 

3376 single_table_crit is not None 

3377 or ("additional_entity_criteria", self.mapper) 

3378 in compile_state.global_attributes 

3379 ): 

3380 compile_state.extra_criteria_entities[ezero] = ( 

3381 ezero, 

3382 ezero._adapter if ezero.is_aliased_class else None, 

3383 ) 

3384 

3385 if column._annotations and not column._expression_label: 

3386 # annotated columns perform more slowly in compiler and 

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

3388 column = column._deannotate() 

3389 

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

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

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

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

3394 # a scalar subquery. 

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

3396 ezero.selectable._from_objects 

3397 ): 

3398 compile_state._fallback_from_clauses.append(ezero.selectable) 

3399 

3400 compile_state.dedupe_columns.add(column) 

3401 compile_state.primary_columns.append(column) 

3402 self._fetch_column = column 

3403 

3404 

3405class _IdentityTokenEntity(_ORMColumnEntity): 

3406 translate_raw_column = False 

3407 

3408 def setup_compile_state(self, compile_state): 

3409 pass 

3410 

3411 def row_processor(self, context, result): 

3412 def getter(row): 

3413 return context.load_options._identity_token 

3414 

3415 return getter, self._label_name, self._extra_entities