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

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

1301 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 itertools 

12from typing import Any 

13from typing import cast 

14from typing import Dict 

15from typing import Iterable 

16from typing import List 

17from typing import Optional 

18from typing import Set 

19from typing import Tuple 

20from typing import Type 

21from typing import TYPE_CHECKING 

22from typing import TypeVar 

23from typing import Union 

24 

25from . import attributes 

26from . import interfaces 

27from . import loading 

28from .base import _is_aliased_class 

29from .interfaces import ORMColumnDescription 

30from .interfaces import ORMColumnsClauseRole 

31from .path_registry import PathRegistry 

32from .util import _entity_corresponds_to 

33from .util import _ORMJoin 

34from .util import _TraceAdaptRole 

35from .util import AliasedClass 

36from .util import Bundle 

37from .util import ORMAdapter 

38from .util import ORMStatementAdapter 

39from .. import exc as sa_exc 

40from .. import future 

41from .. import inspect 

42from .. import sql 

43from .. import util 

44from ..sql import coercions 

45from ..sql import expression 

46from ..sql import roles 

47from ..sql import util as sql_util 

48from ..sql import visitors 

49from ..sql._typing import _TP 

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 Generative 

58from ..sql.base import Options 

59from ..sql.dml import UpdateBase 

60from ..sql.elements import GroupedElement 

61from ..sql.elements import TextClause 

62from ..sql.selectable import CompoundSelectState 

63from ..sql.selectable import LABEL_STYLE_DISAMBIGUATE_ONLY 

64from ..sql.selectable import LABEL_STYLE_NONE 

65from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL 

66from ..sql.selectable import Select 

67from ..sql.selectable import SelectLabelStyle 

68from ..sql.selectable import SelectState 

69from ..sql.selectable import TypedReturnsRows 

70from ..sql.visitors import InternalTraversal 

71 

72if TYPE_CHECKING: 

73 from ._typing import _InternalEntityType 

74 from ._typing import OrmExecuteOptionsParameter 

75 from .loading import PostLoad 

76 from .mapper import Mapper 

77 from .query import Query 

78 from .session import _BindArguments 

79 from .session import Session 

80 from ..engine import Result 

81 from ..engine.interfaces import _CoreSingleExecuteParams 

82 from ..sql._typing import _ColumnsClauseArgument 

83 from ..sql.compiler import SQLCompiler 

84 from ..sql.dml import _DMLTableElement 

85 from ..sql.elements import ColumnElement 

86 from ..sql.selectable import _JoinTargetElement 

87 from ..sql.selectable import _LabelConventionCallable 

88 from ..sql.selectable import _SetupJoinsElement 

89 from ..sql.selectable import ExecutableReturnsRows 

90 from ..sql.selectable import SelectBase 

91 from ..sql.type_api import TypeEngine 

92 

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

94_path_registry = PathRegistry.root 

95 

96_EMPTY_DICT = util.immutabledict() 

97 

98 

99LABEL_STYLE_LEGACY_ORM = SelectLabelStyle.LABEL_STYLE_LEGACY_ORM 

100 

101 

102class QueryContext: 

103 __slots__ = ( 

104 "top_level_context", 

105 "compile_state", 

106 "query", 

107 "user_passed_query", 

108 "params", 

109 "load_options", 

110 "bind_arguments", 

111 "execution_options", 

112 "session", 

113 "autoflush", 

114 "populate_existing", 

115 "invoke_all_eagers", 

116 "version_check", 

117 "refresh_state", 

118 "create_eager_joins", 

119 "propagated_loader_options", 

120 "attributes", 

121 "runid", 

122 "partials", 

123 "post_load_paths", 

124 "identity_token", 

125 "yield_per", 

126 "loaders_require_buffering", 

127 "loaders_require_uniquing", 

128 ) 

129 

130 runid: int 

131 post_load_paths: Dict[PathRegistry, PostLoad] 

132 compile_state: ORMCompileState 

133 

134 class default_load_options(Options): 

135 _only_return_tuples = False 

136 _populate_existing = False 

137 _version_check = False 

138 _invoke_all_eagers = True 

139 _autoflush = True 

140 _identity_token = None 

141 _yield_per = None 

142 _refresh_state = None 

143 _lazy_loaded_from = None 

144 _legacy_uniquing = False 

145 _sa_top_level_orm_context = None 

146 _is_user_refresh = False 

147 

148 def __init__( 

149 self, 

150 compile_state: CompileState, 

151 statement: Union[Select[Any], FromStatement[Any], UpdateBase], 

152 user_passed_query: Union[ 

153 Select[Any], 

154 FromStatement[Any], 

155 UpdateBase, 

156 ], 

157 params: _CoreSingleExecuteParams, 

158 session: Session, 

159 load_options: Union[ 

160 Type[QueryContext.default_load_options], 

161 QueryContext.default_load_options, 

162 ], 

163 execution_options: Optional[OrmExecuteOptionsParameter] = None, 

164 bind_arguments: Optional[_BindArguments] = None, 

165 ): 

166 self.load_options = load_options 

167 self.execution_options = execution_options or _EMPTY_DICT 

168 self.bind_arguments = bind_arguments or _EMPTY_DICT 

169 self.compile_state = compile_state 

170 self.query = statement 

171 

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

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

174 # routines where a separate FromStatement is manufactured in the 

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

176 self.user_passed_query = user_passed_query 

177 

178 self.session = session 

179 self.loaders_require_buffering = False 

180 self.loaders_require_uniquing = False 

181 self.params = params 

182 self.top_level_context = load_options._sa_top_level_orm_context 

183 

184 cached_options = compile_state.select_statement._with_options 

185 uncached_options = user_passed_query._with_options 

186 

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

188 # propagated loader options will be present on loaded InstanceState 

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

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

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

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

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

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

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

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

197 # AliasedClass 

198 self.propagated_loader_options = tuple( 

199 opt._adapt_cached_option_to_uncached_option(self, uncached_opt) 

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

201 if opt.propagate_to_loaders 

202 ) 

203 

204 self.attributes = dict(compile_state.attributes) 

205 

206 self.autoflush = load_options._autoflush 

207 self.populate_existing = load_options._populate_existing 

208 self.invoke_all_eagers = load_options._invoke_all_eagers 

209 self.version_check = load_options._version_check 

210 self.refresh_state = load_options._refresh_state 

211 self.yield_per = load_options._yield_per 

212 self.identity_token = load_options._identity_token 

213 

214 def _get_top_level_context(self) -> QueryContext: 

215 return self.top_level_context or self 

216 

217 

218_orm_load_exec_options = util.immutabledict( 

219 {"_result_disable_adapt_to_context": True} 

220) 

221 

222 

223class AbstractORMCompileState(CompileState): 

224 is_dml_returning = False 

225 

226 def _init_global_attributes( 

227 self, statement, compiler, *, toplevel, process_criteria_for_toplevel 

228 ): 

229 self.attributes = {} 

230 

231 if compiler is None: 

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

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

234 self.global_attributes = {} 

235 assert toplevel 

236 return 

237 else: 

238 self.global_attributes = ga = compiler._global_attributes 

239 

240 if toplevel: 

241 ga["toplevel_orm"] = True 

242 

243 if process_criteria_for_toplevel: 

244 for opt in statement._with_options: 

245 if opt._is_criteria_option: 

246 opt.process_compile_state(self) 

247 

248 return 

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

250 return 

251 

252 stack_0 = compiler.stack[0] 

253 

254 try: 

255 toplevel_stmt = stack_0["selectable"] 

256 except KeyError: 

257 pass 

258 else: 

259 for opt in toplevel_stmt._with_options: 

260 if opt._is_compile_state and opt._is_criteria_option: 

261 opt.process_compile_state(self) 

262 

263 ga["toplevel_orm"] = True 

264 

265 @classmethod 

266 def create_for_statement( 

267 cls, 

268 statement: Executable, 

269 compiler: SQLCompiler, 

270 **kw: Any, 

271 ) -> CompileState: 

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

273 

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

275 

276 For a Select object, this would be invoked from 

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

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

279 FromStatement._compiler_dispatch() that would be called by 

280 SQLCompiler.process(). 

281 """ 

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

283 

284 @classmethod 

285 def orm_pre_session_exec( 

286 cls, 

287 session, 

288 statement, 

289 params, 

290 execution_options, 

291 bind_arguments, 

292 is_pre_event, 

293 ): 

294 raise NotImplementedError() 

295 

296 @classmethod 

297 def orm_execute_statement( 

298 cls, 

299 session, 

300 statement, 

301 params, 

302 execution_options, 

303 bind_arguments, 

304 conn, 

305 ) -> Result: 

306 result = conn.execute( 

307 statement, params or {}, execution_options=execution_options 

308 ) 

309 return cls.orm_setup_cursor_result( 

310 session, 

311 statement, 

312 params, 

313 execution_options, 

314 bind_arguments, 

315 result, 

316 ) 

317 

318 @classmethod 

319 def orm_setup_cursor_result( 

320 cls, 

321 session, 

322 statement, 

323 params, 

324 execution_options, 

325 bind_arguments, 

326 result, 

327 ): 

328 raise NotImplementedError() 

329 

330 

331class AutoflushOnlyORMCompileState(AbstractORMCompileState): 

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

333 

334 @classmethod 

335 def orm_pre_session_exec( 

336 cls, 

337 session, 

338 statement, 

339 params, 

340 execution_options, 

341 bind_arguments, 

342 is_pre_event, 

343 ): 

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

345 # in an ORMExecuteState hook 

346 ( 

347 load_options, 

348 execution_options, 

349 ) = QueryContext.default_load_options.from_execution_options( 

350 "_sa_orm_load_options", 

351 { 

352 "autoflush", 

353 }, 

354 execution_options, 

355 statement._execution_options, 

356 ) 

357 

358 if not is_pre_event and load_options._autoflush: 

359 session._autoflush() 

360 

361 return statement, execution_options 

362 

363 @classmethod 

364 def orm_setup_cursor_result( 

365 cls, 

366 session, 

367 statement, 

368 params, 

369 execution_options, 

370 bind_arguments, 

371 result, 

372 ): 

373 return result 

374 

375 

376class ORMCompileState(AbstractORMCompileState): 

377 class default_compile_options(CacheableOptions): 

378 _cache_key_traversal = [ 

379 ("_use_legacy_query_style", InternalTraversal.dp_boolean), 

380 ("_for_statement", InternalTraversal.dp_boolean), 

381 ("_bake_ok", InternalTraversal.dp_boolean), 

382 ("_current_path", InternalTraversal.dp_has_cache_key), 

383 ("_enable_single_crit", InternalTraversal.dp_boolean), 

384 ("_enable_eagerloads", InternalTraversal.dp_boolean), 

385 ("_only_load_props", InternalTraversal.dp_plain_obj), 

386 ("_set_base_alias", InternalTraversal.dp_boolean), 

387 ("_for_refresh_state", InternalTraversal.dp_boolean), 

388 ("_render_for_subquery", InternalTraversal.dp_boolean), 

389 ("_is_star", InternalTraversal.dp_boolean), 

390 ] 

391 

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

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

394 # now this basically indicates we should use tablename_columnname 

395 # style labels. Generally indicates the statement originated 

396 # from a Query object. 

397 _use_legacy_query_style = False 

398 

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

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

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

402 _for_statement = False 

403 

404 _bake_ok = True 

405 _current_path = _path_registry 

406 _enable_single_crit = True 

407 _enable_eagerloads = True 

408 _only_load_props = None 

409 _set_base_alias = False 

410 _for_refresh_state = False 

411 _render_for_subquery = False 

412 _is_star = False 

413 

414 attributes: Dict[Any, Any] 

415 global_attributes: Dict[Any, Any] 

416 

417 statement: Union[Select[Any], FromStatement[Any], UpdateBase] 

418 select_statement: Union[Select[Any], FromStatement[Any], UpdateBase] 

419 _entities: List[_QueryEntity] 

420 _polymorphic_adapters: Dict[_InternalEntityType, ORMAdapter] 

421 compile_options: Union[ 

422 Type[default_compile_options], default_compile_options 

423 ] 

424 _primary_entity: Optional[_QueryEntity] 

425 use_legacy_query_style: bool 

426 _label_convention: _LabelConventionCallable 

427 primary_columns: List[ColumnElement[Any]] 

428 secondary_columns: List[ColumnElement[Any]] 

429 dedupe_columns: Set[ColumnElement[Any]] 

430 create_eager_joins: List[ 

431 # TODO: this structure is set up by JoinedLoader 

432 Tuple[Any, ...] 

433 ] 

434 current_path: PathRegistry = _path_registry 

435 _has_mapper_entities = False 

436 

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

438 raise NotImplementedError() 

439 

440 @classmethod 

441 def create_for_statement( 

442 cls, 

443 statement: Executable, 

444 compiler: SQLCompiler, 

445 **kw: Any, 

446 ) -> ORMCompileState: 

447 return cls._create_orm_context( 

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

449 toplevel=not compiler.stack, 

450 compiler=compiler, 

451 **kw, 

452 ) 

453 

454 @classmethod 

455 def _create_orm_context( 

456 cls, 

457 statement: Union[Select, FromStatement], 

458 *, 

459 toplevel: bool, 

460 compiler: Optional[SQLCompiler], 

461 **kw: Any, 

462 ) -> ORMCompileState: 

463 raise NotImplementedError() 

464 

465 def _append_dedupe_col_collection(self, obj, col_collection): 

466 dedupe = self.dedupe_columns 

467 if obj not in dedupe: 

468 dedupe.add(obj) 

469 col_collection.append(obj) 

470 

471 @classmethod 

472 def _column_naming_convention( 

473 cls, label_style: SelectLabelStyle, legacy: bool 

474 ) -> _LabelConventionCallable: 

475 if legacy: 

476 

477 def name(col, col_name=None): 

478 if col_name: 

479 return col_name 

480 else: 

481 return getattr(col, "key") 

482 

483 return name 

484 else: 

485 return SelectState._column_naming_convention(label_style) 

486 

487 @classmethod 

488 def get_column_descriptions(cls, statement): 

489 return _column_descriptions(statement) 

490 

491 @classmethod 

492 def orm_pre_session_exec( 

493 cls, 

494 session, 

495 statement, 

496 params, 

497 execution_options, 

498 bind_arguments, 

499 is_pre_event, 

500 ): 

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

502 # in an ORMExecuteState hook 

503 ( 

504 load_options, 

505 execution_options, 

506 ) = QueryContext.default_load_options.from_execution_options( 

507 "_sa_orm_load_options", 

508 { 

509 "populate_existing", 

510 "autoflush", 

511 "yield_per", 

512 "identity_token", 

513 "sa_top_level_orm_context", 

514 }, 

515 execution_options, 

516 statement._execution_options, 

517 ) 

518 

519 # default execution options for ORM results: 

520 # 1. _result_disable_adapt_to_context=True 

521 # this will disable the ResultSetMetadata._adapt_to_context() 

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

523 # against the original SELECT statement before caching. 

524 

525 if "sa_top_level_orm_context" in execution_options: 

526 ctx = execution_options["sa_top_level_orm_context"] 

527 execution_options = ctx.query._execution_options.merge_with( 

528 ctx.execution_options, execution_options 

529 ) 

530 

531 if not execution_options: 

532 execution_options = _orm_load_exec_options 

533 else: 

534 execution_options = execution_options.union(_orm_load_exec_options) 

535 

536 # would have been placed here by legacy Query only 

537 if load_options._yield_per: 

538 execution_options = execution_options.union( 

539 {"yield_per": load_options._yield_per} 

540 ) 

541 

542 if ( 

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

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

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

546 ): 

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

548 execution_options.union( 

549 { 

550 "compiled_cache": None, 

551 "_cache_disable_reason": "excess depth for " 

552 "ORM loader options", 

553 } 

554 ) 

555 ) 

556 

557 bind_arguments["clause"] = statement 

558 

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

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

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

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

563 

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

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

566 # needs to be present as well. 

567 

568 try: 

569 plugin_subject = statement._propagate_attrs["plugin_subject"] 

570 except KeyError: 

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

572 else: 

573 if plugin_subject: 

574 bind_arguments["mapper"] = plugin_subject.mapper 

575 

576 if not is_pre_event and load_options._autoflush: 

577 session._autoflush() 

578 

579 return statement, execution_options 

580 

581 @classmethod 

582 def orm_setup_cursor_result( 

583 cls, 

584 session, 

585 statement, 

586 params, 

587 execution_options, 

588 bind_arguments, 

589 result, 

590 ): 

591 execution_context = result.context 

592 compile_state = execution_context.compiled.compile_state 

593 

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

595 # were passed to session.execute: 

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

597 # see test_query->test_legacy_tuple_old_select 

598 

599 load_options = execution_options.get( 

600 "_sa_orm_load_options", QueryContext.default_load_options 

601 ) 

602 

603 if compile_state.compile_options._is_star: 

604 return result 

605 

606 querycontext = QueryContext( 

607 compile_state, 

608 statement, 

609 statement, 

610 params, 

611 session, 

612 load_options, 

613 execution_options, 

614 bind_arguments, 

615 ) 

616 return loading.instances(result, querycontext) 

617 

618 @property 

619 def _lead_mapper_entities(self): 

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

621 

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

623 with_entities(), with_only_columns() 

624 

625 """ 

626 return [ 

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

628 ] 

629 

630 def _create_with_polymorphic_adapter(self, ext_info, selectable): 

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

632 if called for by the Mapper. 

633 

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

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

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

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

638 loading, and joined inheritance where a subquery is 

639 passed to with_polymorphic (which is completely unnecessary in modern 

640 use). 

641 

642 """ 

643 if ( 

644 not ext_info.is_aliased_class 

645 and ext_info.mapper.persist_selectable 

646 not in self._polymorphic_adapters 

647 ): 

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

649 self._mapper_loads_polymorphically_with( 

650 mp, 

651 ORMAdapter( 

652 _TraceAdaptRole.WITH_POLYMORPHIC_ADAPTER, 

653 mp, 

654 equivalents=mp._equivalent_columns, 

655 selectable=selectable, 

656 ), 

657 ) 

658 

659 def _mapper_loads_polymorphically_with(self, mapper, adapter): 

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

661 self._polymorphic_adapters[m2] = adapter 

662 

663 for m in m2.iterate_to_root(): 

664 self._polymorphic_adapters[m.local_table] = adapter 

665 

666 @classmethod 

667 def _create_entities_collection(cls, query, legacy): 

668 raise NotImplementedError( 

669 "this method only works for ORMSelectCompileState" 

670 ) 

671 

672 

673class _DMLReturningColFilter: 

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

675 

676 Has a subset of the interface used by 

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

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

679 DML statement. 

680 

681 """ 

682 

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

684 

685 def __init__(self, target_mapper, immediate_dml_mapper): 

686 if ( 

687 immediate_dml_mapper is not None 

688 and target_mapper.local_table 

689 is not immediate_dml_mapper.local_table 

690 ): 

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

692 self.mapper = immediate_dml_mapper 

693 else: 

694 # single inh, normal mappings, etc. 

695 self.mapper = target_mapper 

696 self.columns = self.columns = util.WeakPopulateDict( 

697 self.adapt_check_present # type: ignore 

698 ) 

699 

700 def __call__(self, col, as_filter): 

701 for cc in sql_util._find_columns(col): 

702 c2 = self.adapt_check_present(cc) 

703 if c2 is not None: 

704 return col 

705 else: 

706 return None 

707 

708 def adapt_check_present(self, col): 

709 raise NotImplementedError() 

710 

711 

712class _DMLBulkInsertReturningColFilter(_DMLReturningColFilter): 

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

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

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

716 example right now) 

717 

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

719 mapped table in a hierarchy. 

720 

721 """ 

722 

723 def adapt_check_present(self, col): 

724 mapper = self.mapper 

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

726 if prop is None: 

727 return None 

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

729 

730 

731class _DMLUpdateDeleteReturningColFilter(_DMLReturningColFilter): 

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

733 for ORM enabled UPDATE/DELETE 

734 

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

736 only direct persisted columns from the immediate selectable, not 

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

738 mappers for the UPDATE..FROM use case. 

739 

740 """ 

741 

742 def adapt_check_present(self, col): 

743 mapper = self.mapper 

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

745 if prop is not None: 

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

747 # column, not any kind of column_property expression 

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

749 

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

751 # user knows what they are doing 

752 return col 

753 

754 

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

756class ORMFromStatementCompileState(ORMCompileState): 

757 _from_obj_alias = None 

758 _has_mapper_entities = False 

759 

760 statement_container: FromStatement 

761 requested_statement: Union[SelectBase, TextClause, UpdateBase] 

762 dml_table: Optional[_DMLTableElement] = None 

763 

764 _has_orm_entities = False 

765 multi_row_eager_loaders = False 

766 eager_adding_joins = False 

767 compound_eager_adapter = None 

768 

769 extra_criteria_entities = _EMPTY_DICT 

770 eager_joins = _EMPTY_DICT 

771 

772 @classmethod 

773 def _create_orm_context( 

774 cls, 

775 statement: Union[Select, FromStatement], 

776 *, 

777 toplevel: bool, 

778 compiler: Optional[SQLCompiler], 

779 **kw: Any, 

780 ) -> ORMFromStatementCompileState: 

781 statement_container = statement 

782 

783 assert isinstance(statement_container, FromStatement) 

784 

785 if compiler is not None and compiler.stack: 

786 raise sa_exc.CompileError( 

787 "The ORM FromStatement construct only supports being " 

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

789 "define how result rows should be returned." 

790 ) 

791 

792 self = cls.__new__(cls) 

793 self._primary_entity = None 

794 

795 self.use_legacy_query_style = ( 

796 statement_container._compile_options._use_legacy_query_style 

797 ) 

798 self.statement_container = self.select_statement = statement_container 

799 self.requested_statement = statement = statement_container.element 

800 

801 if statement.is_dml: 

802 self.dml_table = statement.table 

803 self.is_dml_returning = True 

804 

805 self._entities = [] 

806 self._polymorphic_adapters = {} 

807 

808 self.compile_options = statement_container._compile_options 

809 

810 if ( 

811 self.use_legacy_query_style 

812 and isinstance(statement, expression.SelectBase) 

813 and not statement._is_textual 

814 and not statement.is_dml 

815 and statement._label_style is LABEL_STYLE_NONE 

816 ): 

817 self.statement = statement.set_label_style( 

818 LABEL_STYLE_TABLENAME_PLUS_COL 

819 ) 

820 else: 

821 self.statement = statement 

822 

823 self._label_convention = self._column_naming_convention( 

824 ( 

825 statement._label_style 

826 if not statement._is_textual and not statement.is_dml 

827 else LABEL_STYLE_NONE 

828 ), 

829 self.use_legacy_query_style, 

830 ) 

831 

832 _QueryEntity.to_compile_state( 

833 self, 

834 statement_container._raw_columns, 

835 self._entities, 

836 is_current_entities=True, 

837 ) 

838 

839 self.current_path = statement_container._compile_options._current_path 

840 

841 self._init_global_attributes( 

842 statement_container, 

843 compiler, 

844 process_criteria_for_toplevel=False, 

845 toplevel=True, 

846 ) 

847 

848 if statement_container._with_options: 

849 for opt in statement_container._with_options: 

850 if opt._is_compile_state: 

851 opt.process_compile_state(self) 

852 

853 if statement_container._with_context_options: 

854 for fn, key in statement_container._with_context_options: 

855 fn(self) 

856 

857 self.primary_columns = [] 

858 self.secondary_columns = [] 

859 self.dedupe_columns = set() 

860 self.create_eager_joins = [] 

861 self._fallback_from_clauses = [] 

862 

863 self.order_by = None 

864 

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

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

867 # we generate columns from our _QueryEntity objects, then 

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

869 self.extra_criteria_entities = {} 

870 

871 for entity in self._entities: 

872 entity.setup_compile_state(self) 

873 

874 compiler._ordered_columns = compiler._textual_ordered_columns = ( 

875 False 

876 ) 

877 

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

879 # needed by test_query.py::TextTest 

880 compiler._loose_column_name_matching = True 

881 

882 for c in self.primary_columns: 

883 compiler.process( 

884 c, 

885 within_columns_clause=True, 

886 add_to_result_map=compiler._add_to_result_map, 

887 ) 

888 else: 

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

890 # have column objects already. After much 

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

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

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

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

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

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

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

898 self._from_obj_alias = ORMStatementAdapter( 

899 _TraceAdaptRole.ADAPT_FROM_STATEMENT, 

900 self.statement, 

901 adapt_on_names=statement_container._adapt_on_names, 

902 ) 

903 

904 return self 

905 

906 def _adapt_col_list(self, cols, current_adapter): 

907 return cols 

908 

909 def _get_current_adapter(self): 

910 return None 

911 

912 def setup_dml_returning_compile_state(self, dml_mapper): 

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

914 for RETURNING to return ORM objects and expressions 

915 

916 """ 

917 target_mapper = self.statement._propagate_attrs.get( 

918 "plugin_subject", None 

919 ) 

920 

921 if self.statement.is_insert: 

922 adapter = _DMLBulkInsertReturningColFilter( 

923 target_mapper, dml_mapper 

924 ) 

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

926 adapter = _DMLUpdateDeleteReturningColFilter( 

927 target_mapper, dml_mapper 

928 ) 

929 else: 

930 adapter = None 

931 

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

933 raise sa_exc.CompileError( 

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

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

936 ) 

937 

938 for entity in self._entities: 

939 entity.setup_dml_returning_compile_state(self, adapter) 

940 

941 

942class FromStatement(GroupedElement, Generative, TypedReturnsRows[_TP]): 

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

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

945 

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

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

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

949 

950 """ 

951 

952 __visit_name__ = "orm_from_statement" 

953 

954 _compile_options = ORMFromStatementCompileState.default_compile_options 

955 

956 _compile_state_factory = ORMFromStatementCompileState.create_for_statement 

957 

958 _for_update_arg = None 

959 

960 element: Union[ExecutableReturnsRows, TextClause] 

961 

962 _adapt_on_names: bool 

963 

964 _traverse_internals = [ 

965 ("_raw_columns", InternalTraversal.dp_clauseelement_list), 

966 ("element", InternalTraversal.dp_clauseelement), 

967 ] + Executable._executable_traverse_internals 

968 

969 _cache_key_traversal = _traverse_internals + [ 

970 ("_compile_options", InternalTraversal.dp_has_cache_key) 

971 ] 

972 

973 is_from_statement = True 

974 

975 def __init__( 

976 self, 

977 entities: Iterable[_ColumnsClauseArgument[Any]], 

978 element: Union[ExecutableReturnsRows, TextClause], 

979 _adapt_on_names: bool = True, 

980 ): 

981 self._raw_columns = [ 

982 coercions.expect( 

983 roles.ColumnsClauseRole, 

984 ent, 

985 apply_propagate_attrs=self, 

986 post_inspect=True, 

987 ) 

988 for ent in util.to_list(entities) 

989 ] 

990 self.element = element 

991 self.is_dml = element.is_dml 

992 self.is_select = element.is_select 

993 self.is_delete = element.is_delete 

994 self.is_insert = element.is_insert 

995 self.is_update = element.is_update 

996 self._label_style = ( 

997 element._label_style if is_select_base(element) else None 

998 ) 

999 self._adapt_on_names = _adapt_on_names 

1000 

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

1002 """provide a fixed _compiler_dispatch method. 

1003 

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

1005 ``@compiles`` extension. 

1006 

1007 """ 

1008 

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

1010 

1011 toplevel = not compiler.stack 

1012 

1013 if toplevel: 

1014 compiler.compile_state = compile_state 

1015 

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

1017 

1018 @property 

1019 def column_descriptions(self): 

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

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

1022 

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

1024 of this feature. 

1025 

1026 .. seealso:: 

1027 

1028 :ref:`queryguide_inspection` - ORM background 

1029 

1030 """ 

1031 meth = cast( 

1032 ORMSelectCompileState, SelectState.get_plugin_class(self) 

1033 ).get_column_descriptions 

1034 return meth(self) 

1035 

1036 def _ensure_disambiguated_names(self): 

1037 return self 

1038 

1039 def get_children(self, **kw): 

1040 yield from itertools.chain.from_iterable( 

1041 element._from_objects for element in self._raw_columns 

1042 ) 

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

1044 

1045 @property 

1046 def _all_selected_columns(self): 

1047 return self.element._all_selected_columns 

1048 

1049 @property 

1050 def _return_defaults(self): 

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

1052 

1053 @property 

1054 def _returning(self): 

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

1056 

1057 @property 

1058 def _inline(self): 

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

1060 

1061 

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

1063class CompoundSelectCompileState( 

1064 AutoflushOnlyORMCompileState, CompoundSelectState 

1065): 

1066 pass 

1067 

1068 

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

1070class ORMSelectCompileState(ORMCompileState, SelectState): 

1071 _already_joined_edges = () 

1072 

1073 _memoized_entities = _EMPTY_DICT 

1074 

1075 _from_obj_alias = None 

1076 _has_mapper_entities = False 

1077 

1078 _has_orm_entities = False 

1079 multi_row_eager_loaders = False 

1080 eager_adding_joins = False 

1081 compound_eager_adapter = None 

1082 

1083 correlate = None 

1084 correlate_except = None 

1085 _where_criteria = () 

1086 _having_criteria = () 

1087 

1088 @classmethod 

1089 def _create_orm_context( 

1090 cls, 

1091 statement: Union[Select, FromStatement], 

1092 *, 

1093 toplevel: bool, 

1094 compiler: Optional[SQLCompiler], 

1095 **kw: Any, 

1096 ) -> ORMSelectCompileState: 

1097 

1098 self = cls.__new__(cls) 

1099 

1100 select_statement = statement 

1101 

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

1103 # have ORM level compile options. 

1104 statement._compile_options = cls.default_compile_options.safe_merge( 

1105 statement._compile_options 

1106 ) 

1107 

1108 if select_statement._execution_options: 

1109 # execution options should not impact the compilation of a 

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

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

1112 self.select_statement = select_statement._clone() 

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

1114 else: 

1115 self.select_statement = select_statement 

1116 

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

1118 self.for_statement = select_statement._compile_options._for_statement 

1119 

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

1121 self.use_legacy_query_style = ( 

1122 select_statement._compile_options._use_legacy_query_style 

1123 ) 

1124 

1125 self._entities = [] 

1126 self._primary_entity = None 

1127 self._polymorphic_adapters = {} 

1128 

1129 self.compile_options = select_statement._compile_options 

1130 

1131 if not toplevel: 

1132 # for subqueries, turn off eagerloads and set 

1133 # "render_for_subquery". 

1134 self.compile_options += { 

1135 "_enable_eagerloads": False, 

1136 "_render_for_subquery": True, 

1137 } 

1138 

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

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

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

1142 # for new style ORM selects too. 

1143 if ( 

1144 self.use_legacy_query_style 

1145 and self.select_statement._label_style is LABEL_STYLE_LEGACY_ORM 

1146 ): 

1147 if not self.for_statement: 

1148 self.label_style = LABEL_STYLE_TABLENAME_PLUS_COL 

1149 else: 

1150 self.label_style = LABEL_STYLE_DISAMBIGUATE_ONLY 

1151 else: 

1152 self.label_style = self.select_statement._label_style 

1153 

1154 if select_statement._memoized_select_entities: 

1155 self._memoized_entities = { 

1156 memoized_entities: _QueryEntity.to_compile_state( 

1157 self, 

1158 memoized_entities._raw_columns, 

1159 [], 

1160 is_current_entities=False, 

1161 ) 

1162 for memoized_entities in ( 

1163 select_statement._memoized_select_entities 

1164 ) 

1165 } 

1166 

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

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

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

1170 # in the columns clause 

1171 self._label_convention = self._column_naming_convention( 

1172 statement._label_style, self.use_legacy_query_style 

1173 ) 

1174 

1175 _QueryEntity.to_compile_state( 

1176 self, 

1177 select_statement._raw_columns, 

1178 self._entities, 

1179 is_current_entities=True, 

1180 ) 

1181 

1182 self.current_path = select_statement._compile_options._current_path 

1183 

1184 self.eager_order_by = () 

1185 

1186 self._init_global_attributes( 

1187 select_statement, 

1188 compiler, 

1189 toplevel=toplevel, 

1190 process_criteria_for_toplevel=False, 

1191 ) 

1192 

1193 if toplevel and ( 

1194 select_statement._with_options 

1195 or select_statement._memoized_select_entities 

1196 ): 

1197 for ( 

1198 memoized_entities 

1199 ) in select_statement._memoized_select_entities: 

1200 for opt in memoized_entities._with_options: 

1201 if opt._is_compile_state: 

1202 opt.process_compile_state_replaced_entities( 

1203 self, 

1204 [ 

1205 ent 

1206 for ent in self._memoized_entities[ 

1207 memoized_entities 

1208 ] 

1209 if isinstance(ent, _MapperEntity) 

1210 ], 

1211 ) 

1212 

1213 for opt in self.select_statement._with_options: 

1214 if opt._is_compile_state: 

1215 opt.process_compile_state(self) 

1216 

1217 # uncomment to print out the context.attributes structure 

1218 # after it's been set up above 

1219 # self._dump_option_struct() 

1220 

1221 if select_statement._with_context_options: 

1222 for fn, key in select_statement._with_context_options: 

1223 fn(self) 

1224 

1225 self.primary_columns = [] 

1226 self.secondary_columns = [] 

1227 self.dedupe_columns = set() 

1228 self.eager_joins = {} 

1229 self.extra_criteria_entities = {} 

1230 self.create_eager_joins = [] 

1231 self._fallback_from_clauses = [] 

1232 

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

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

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

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

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

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

1239 self.from_clauses = self._normalize_froms( 

1240 info.selectable for info in select_statement._from_obj 

1241 ) 

1242 

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

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

1245 # and _setup_for_generate into three or four logical sections 

1246 self._setup_for_generate() 

1247 

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

1249 return self 

1250 

1251 def _dump_option_struct(self): 

1252 print("\n---------------------------------------------------\n") 

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

1254 for key in self.attributes: 

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

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

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

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

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

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

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

1262 

1263 def _setup_for_generate(self): 

1264 query = self.select_statement 

1265 

1266 self.statement = None 

1267 self._join_entities = () 

1268 

1269 if self.compile_options._set_base_alias: 

1270 # legacy Query only 

1271 self._set_select_from_alias() 

1272 

1273 for memoized_entities in query._memoized_select_entities: 

1274 if memoized_entities._setup_joins: 

1275 self._join( 

1276 memoized_entities._setup_joins, 

1277 self._memoized_entities[memoized_entities], 

1278 ) 

1279 

1280 if query._setup_joins: 

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

1282 

1283 current_adapter = self._get_current_adapter() 

1284 

1285 if query._where_criteria: 

1286 self._where_criteria = query._where_criteria 

1287 

1288 if current_adapter: 

1289 self._where_criteria = tuple( 

1290 current_adapter(crit, True) 

1291 for crit in self._where_criteria 

1292 ) 

1293 

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

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

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

1297 self.order_by = ( 

1298 self._adapt_col_list(query._order_by_clauses, current_adapter) 

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

1300 else query._order_by_clauses 

1301 ) 

1302 

1303 if query._having_criteria: 

1304 self._having_criteria = tuple( 

1305 current_adapter(crit, True) if current_adapter else crit 

1306 for crit in query._having_criteria 

1307 ) 

1308 

1309 self.group_by = ( 

1310 self._adapt_col_list( 

1311 util.flatten_iterator(query._group_by_clauses), current_adapter 

1312 ) 

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

1314 else query._group_by_clauses or None 

1315 ) 

1316 

1317 if self.eager_order_by: 

1318 adapter = self.from_clauses[0]._target_adapter 

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

1320 

1321 if query._distinct_on: 

1322 self.distinct_on = self._adapt_col_list( 

1323 query._distinct_on, current_adapter 

1324 ) 

1325 else: 

1326 self.distinct_on = () 

1327 

1328 self.distinct = query._distinct 

1329 

1330 if query._correlate: 

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

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

1333 # tables. 

1334 self.correlate = tuple( 

1335 util.flatten_iterator( 

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

1337 for s in query._correlate 

1338 ) 

1339 ) 

1340 elif query._correlate_except is not None: 

1341 self.correlate_except = tuple( 

1342 util.flatten_iterator( 

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

1344 for s in query._correlate_except 

1345 ) 

1346 ) 

1347 elif not query._auto_correlate: 

1348 self.correlate = (None,) 

1349 

1350 # PART II 

1351 

1352 self._for_update_arg = query._for_update_arg 

1353 

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

1355 raise sa_exc.CompileError( 

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

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

1358 ) 

1359 for entity in self._entities: 

1360 entity.setup_compile_state(self) 

1361 

1362 for rec in self.create_eager_joins: 

1363 strategy = rec[0] 

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

1365 

1366 # else "load from discrete FROMs" mode, 

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

1368 

1369 if self.compile_options._enable_single_crit: 

1370 self._adjust_for_extra_criteria() 

1371 

1372 if not self.primary_columns: 

1373 if self.compile_options._only_load_props: 

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

1375 

1376 raise sa_exc.InvalidRequestError( 

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

1378 ) 

1379 

1380 if not self.from_clauses: 

1381 self.from_clauses = list(self._fallback_from_clauses) 

1382 

1383 if self.order_by is False: 

1384 self.order_by = None 

1385 

1386 if ( 

1387 self.multi_row_eager_loaders 

1388 and self.eager_adding_joins 

1389 and self._should_nest_selectable 

1390 ): 

1391 self.statement = self._compound_eager_statement() 

1392 else: 

1393 self.statement = self._simple_statement() 

1394 

1395 if self.for_statement: 

1396 ezero = self._mapper_zero() 

1397 if ezero is not None: 

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

1399 # thing 

1400 self.statement = self.statement._annotate( 

1401 {"deepentity": ezero} 

1402 ) 

1403 

1404 @classmethod 

1405 def _create_entities_collection(cls, query, legacy): 

1406 """Creates a partial ORMSelectCompileState that includes 

1407 the full collection of _MapperEntity and other _QueryEntity objects. 

1408 

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

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

1411 

1412 """ 

1413 self = cls.__new__(cls) 

1414 

1415 self._entities = [] 

1416 self._primary_entity = None 

1417 self._polymorphic_adapters = {} 

1418 

1419 self._label_convention = self._column_naming_convention( 

1420 query._label_style, legacy 

1421 ) 

1422 

1423 # entities will also set up polymorphic adapters for mappers 

1424 # that have with_polymorphic configured 

1425 _QueryEntity.to_compile_state( 

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

1427 ) 

1428 return self 

1429 

1430 @classmethod 

1431 def determine_last_joined_entity(cls, statement): 

1432 setup_joins = statement._setup_joins 

1433 

1434 return _determine_last_joined_entity(setup_joins, None) 

1435 

1436 @classmethod 

1437 def all_selected_columns(cls, statement): 

1438 for element in statement._raw_columns: 

1439 if ( 

1440 element.is_selectable 

1441 and "entity_namespace" in element._annotations 

1442 ): 

1443 ens = element._annotations["entity_namespace"] 

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

1445 yield from _select_iterables([element]) 

1446 else: 

1447 yield from _select_iterables(ens._all_column_expressions) 

1448 else: 

1449 yield from _select_iterables([element]) 

1450 

1451 @classmethod 

1452 def get_columns_clause_froms(cls, statement): 

1453 return cls._normalize_froms( 

1454 itertools.chain.from_iterable( 

1455 ( 

1456 element._from_objects 

1457 if "parententity" not in element._annotations 

1458 else [ 

1459 element._annotations[ 

1460 "parententity" 

1461 ].__clause_element__() 

1462 ] 

1463 ) 

1464 for element in statement._raw_columns 

1465 ) 

1466 ) 

1467 

1468 @classmethod 

1469 def from_statement(cls, statement, from_statement): 

1470 from_statement = coercions.expect( 

1471 roles.ReturnsRowsRole, 

1472 from_statement, 

1473 apply_propagate_attrs=statement, 

1474 ) 

1475 

1476 stmt = FromStatement(statement._raw_columns, from_statement) 

1477 

1478 stmt.__dict__.update( 

1479 _with_options=statement._with_options, 

1480 _with_context_options=statement._with_context_options, 

1481 _execution_options=statement._execution_options, 

1482 _propagate_attrs=statement._propagate_attrs, 

1483 ) 

1484 return stmt 

1485 

1486 def _set_select_from_alias(self): 

1487 """used only for legacy Query cases""" 

1488 

1489 query = self.select_statement # query 

1490 

1491 assert self.compile_options._set_base_alias 

1492 assert len(query._from_obj) == 1 

1493 

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

1495 if adapter: 

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

1497 self._from_obj_alias = adapter 

1498 

1499 def _get_select_from_alias_from_obj(self, from_obj): 

1500 """used only for legacy Query cases""" 

1501 

1502 info = from_obj 

1503 

1504 if "parententity" in info._annotations: 

1505 info = info._annotations["parententity"] 

1506 

1507 if hasattr(info, "mapper"): 

1508 if not info.is_aliased_class: 

1509 raise sa_exc.ArgumentError( 

1510 "A selectable (FromClause) instance is " 

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

1512 ) 

1513 else: 

1514 return info._adapter 

1515 

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

1517 equivs = self._all_equivs() 

1518 assert info is info.selectable 

1519 return ORMStatementAdapter( 

1520 _TraceAdaptRole.LEGACY_SELECT_FROM_ALIAS, 

1521 info.selectable, 

1522 equivalents=equivs, 

1523 ) 

1524 else: 

1525 return None 

1526 

1527 def _mapper_zero(self): 

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

1529 return self._entities[0].mapper 

1530 

1531 def _entity_zero(self): 

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

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

1534 entity if specified.""" 

1535 

1536 for ent in self.from_clauses: 

1537 if "parententity" in ent._annotations: 

1538 return ent._annotations["parententity"] 

1539 for qent in self._entities: 

1540 if qent.entity_zero: 

1541 return qent.entity_zero 

1542 

1543 return None 

1544 

1545 def _only_full_mapper_zero(self, methname): 

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

1547 raise sa_exc.InvalidRequestError( 

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

1549 "a single mapped class." % methname 

1550 ) 

1551 return self._primary_entity.entity_zero 

1552 

1553 def _only_entity_zero(self, rationale=None): 

1554 if len(self._entities) > 1: 

1555 raise sa_exc.InvalidRequestError( 

1556 rationale 

1557 or "This operation requires a Query " 

1558 "against a single mapper." 

1559 ) 

1560 return self._entity_zero() 

1561 

1562 def _all_equivs(self): 

1563 equivs = {} 

1564 

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

1566 for ent in [ 

1567 ent 

1568 for ent in memoized_entities 

1569 if isinstance(ent, _MapperEntity) 

1570 ]: 

1571 equivs.update(ent.mapper._equivalent_columns) 

1572 

1573 for ent in [ 

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

1575 ]: 

1576 equivs.update(ent.mapper._equivalent_columns) 

1577 return equivs 

1578 

1579 def _compound_eager_statement(self): 

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

1581 # wrap the query inside a select, 

1582 # then append eager joins onto that 

1583 

1584 if self.order_by: 

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

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

1587 # elements are converted into label references. For the 

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

1589 # the original expressions outside of the label references 

1590 # in order to have them render. 

1591 unwrapped_order_by = [ 

1592 ( 

1593 elem.element 

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

1595 else elem 

1596 ) 

1597 for elem in self.order_by 

1598 ] 

1599 

1600 order_by_col_expr = sql_util.expand_column_list_from_order_by( 

1601 self.primary_columns, unwrapped_order_by 

1602 ) 

1603 else: 

1604 order_by_col_expr = [] 

1605 unwrapped_order_by = None 

1606 

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

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

1609 inner = self._select_statement( 

1610 self.primary_columns 

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

1612 self.from_clauses, 

1613 self._where_criteria, 

1614 self._having_criteria, 

1615 self.label_style, 

1616 self.order_by, 

1617 for_update=self._for_update_arg, 

1618 hints=self.select_statement._hints, 

1619 statement_hints=self.select_statement._statement_hints, 

1620 correlate=self.correlate, 

1621 correlate_except=self.correlate_except, 

1622 **self._select_args, 

1623 ) 

1624 

1625 inner = inner.alias() 

1626 

1627 equivs = self._all_equivs() 

1628 

1629 self.compound_eager_adapter = ORMStatementAdapter( 

1630 _TraceAdaptRole.COMPOUND_EAGER_STATEMENT, inner, equivalents=equivs 

1631 ) 

1632 

1633 statement = future.select( 

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

1635 ) 

1636 statement._label_style = self.label_style 

1637 

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

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

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

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

1642 if ( 

1643 self._for_update_arg is not None 

1644 and self._for_update_arg.of is None 

1645 ): 

1646 statement._for_update_arg = self._for_update_arg 

1647 

1648 from_clause = inner 

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

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

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

1652 # the join should be 

1653 from_clause = sql_util.splice_joins( 

1654 from_clause, eager_join, eager_join.stop_on 

1655 ) 

1656 

1657 statement.select_from.non_generative(statement, from_clause) 

1658 

1659 if unwrapped_order_by: 

1660 statement.order_by.non_generative( 

1661 statement, 

1662 *self.compound_eager_adapter.copy_and_process( 

1663 unwrapped_order_by 

1664 ), 

1665 ) 

1666 

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

1668 return statement 

1669 

1670 def _simple_statement(self): 

1671 statement = self._select_statement( 

1672 self.primary_columns + self.secondary_columns, 

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

1674 self._where_criteria, 

1675 self._having_criteria, 

1676 self.label_style, 

1677 self.order_by, 

1678 for_update=self._for_update_arg, 

1679 hints=self.select_statement._hints, 

1680 statement_hints=self.select_statement._statement_hints, 

1681 correlate=self.correlate, 

1682 correlate_except=self.correlate_except, 

1683 **self._select_args, 

1684 ) 

1685 

1686 if self.eager_order_by: 

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

1688 return statement 

1689 

1690 def _select_statement( 

1691 self, 

1692 raw_columns, 

1693 from_obj, 

1694 where_criteria, 

1695 having_criteria, 

1696 label_style, 

1697 order_by, 

1698 for_update, 

1699 hints, 

1700 statement_hints, 

1701 correlate, 

1702 correlate_except, 

1703 limit_clause, 

1704 offset_clause, 

1705 fetch_clause, 

1706 fetch_clause_options, 

1707 distinct, 

1708 distinct_on, 

1709 prefixes, 

1710 suffixes, 

1711 group_by, 

1712 independent_ctes, 

1713 independent_ctes_opts, 

1714 ): 

1715 statement = Select._create_raw_select( 

1716 _raw_columns=raw_columns, 

1717 _from_obj=from_obj, 

1718 _label_style=label_style, 

1719 ) 

1720 

1721 if where_criteria: 

1722 statement._where_criteria = where_criteria 

1723 if having_criteria: 

1724 statement._having_criteria = having_criteria 

1725 

1726 if order_by: 

1727 statement._order_by_clauses += tuple(order_by) 

1728 

1729 if distinct_on: 

1730 statement.distinct.non_generative(statement, *distinct_on) 

1731 elif distinct: 

1732 statement.distinct.non_generative(statement) 

1733 

1734 if group_by: 

1735 statement._group_by_clauses += tuple(group_by) 

1736 

1737 statement._limit_clause = limit_clause 

1738 statement._offset_clause = offset_clause 

1739 statement._fetch_clause = fetch_clause 

1740 statement._fetch_clause_options = fetch_clause_options 

1741 statement._independent_ctes = independent_ctes 

1742 statement._independent_ctes_opts = independent_ctes_opts 

1743 

1744 if prefixes: 

1745 statement._prefixes = prefixes 

1746 

1747 if suffixes: 

1748 statement._suffixes = suffixes 

1749 

1750 statement._for_update_arg = for_update 

1751 

1752 if hints: 

1753 statement._hints = hints 

1754 if statement_hints: 

1755 statement._statement_hints = statement_hints 

1756 

1757 if correlate: 

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

1759 

1760 if correlate_except is not None: 

1761 statement.correlate_except.non_generative( 

1762 statement, *correlate_except 

1763 ) 

1764 

1765 return statement 

1766 

1767 def _adapt_polymorphic_element(self, element): 

1768 if "parententity" in element._annotations: 

1769 search = element._annotations["parententity"] 

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

1771 if alias: 

1772 return alias.adapt_clause(element) 

1773 

1774 if isinstance(element, expression.FromClause): 

1775 search = element 

1776 elif hasattr(element, "table"): 

1777 search = element.table 

1778 else: 

1779 return None 

1780 

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

1782 if alias: 

1783 return alias.adapt_clause(element) 

1784 

1785 def _adapt_col_list(self, cols, current_adapter): 

1786 if current_adapter: 

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

1788 else: 

1789 return cols 

1790 

1791 def _get_current_adapter(self): 

1792 adapters = [] 

1793 

1794 if self._from_obj_alias: 

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

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

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

1798 # select_entity_from() 

1799 # 

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

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

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

1803 # to all SQL constructs. 

1804 adapters.append( 

1805 ( 

1806 True, 

1807 self._from_obj_alias.replace, 

1808 ) 

1809 ) 

1810 

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

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

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

1814 if self._polymorphic_adapters: 

1815 adapters.append((False, self._adapt_polymorphic_element)) 

1816 

1817 if not adapters: 

1818 return None 

1819 

1820 def _adapt_clause(clause, as_filter): 

1821 # do we adapt all expression elements or only those 

1822 # tagged as 'ORM' constructs ? 

1823 

1824 def replace(elem): 

1825 is_orm_adapt = ( 

1826 "_orm_adapt" in elem._annotations 

1827 or "parententity" in elem._annotations 

1828 ) 

1829 for always_adapt, adapter in adapters: 

1830 if is_orm_adapt or always_adapt: 

1831 e = adapter(elem) 

1832 if e is not None: 

1833 return e 

1834 

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

1836 

1837 return _adapt_clause 

1838 

1839 def _join(self, args, entities_collection): 

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

1841 isouter = flags["isouter"] 

1842 full = flags["full"] 

1843 

1844 right = inspect(right) 

1845 if onclause is not None: 

1846 onclause = inspect(onclause) 

1847 

1848 if isinstance(right, interfaces.PropComparator): 

1849 if onclause is not None: 

1850 raise sa_exc.InvalidRequestError( 

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

1852 "to a relationship path as a target" 

1853 ) 

1854 

1855 onclause = right 

1856 right = None 

1857 elif "parententity" in right._annotations: 

1858 right = right._annotations["parententity"] 

1859 

1860 if onclause is None: 

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

1862 raise sa_exc.ArgumentError( 

1863 "Expected mapped entity or " 

1864 "selectable/table as join target" 

1865 ) 

1866 

1867 if isinstance(onclause, interfaces.PropComparator): 

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

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

1870 

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

1872 

1873 if right is None: 

1874 if of_type: 

1875 right = of_type 

1876 else: 

1877 right = onclause.property 

1878 

1879 try: 

1880 right = right.entity 

1881 except AttributeError as err: 

1882 raise sa_exc.ArgumentError( 

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

1884 "mapped entity" % right 

1885 ) from err 

1886 

1887 left = onclause._parententity 

1888 

1889 prop = onclause.property 

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

1891 onclause = prop 

1892 

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

1894 # case. 

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

1896 continue 

1897 

1898 if from_ is not None: 

1899 if ( 

1900 from_ is not left 

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

1902 is not left 

1903 ): 

1904 raise sa_exc.InvalidRequestError( 

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

1906 "of relationship attribute %s" 

1907 % ( 

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

1909 onclause, 

1910 ) 

1911 ) 

1912 elif from_ is not None: 

1913 prop = None 

1914 left = from_ 

1915 else: 

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

1917 # what the effective "left" side is 

1918 prop = left = None 

1919 

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

1921 # ORMJoin to add to our _from_obj tuple 

1922 self._join_left_to_right( 

1923 entities_collection, 

1924 left, 

1925 right, 

1926 onclause, 

1927 prop, 

1928 isouter, 

1929 full, 

1930 ) 

1931 

1932 def _join_left_to_right( 

1933 self, 

1934 entities_collection, 

1935 left, 

1936 right, 

1937 onclause, 

1938 prop, 

1939 outerjoin, 

1940 full, 

1941 ): 

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

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

1944 our _from_obj list (or augment an existing one) 

1945 

1946 """ 

1947 

1948 if left is None: 

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

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

1951 # entities 

1952 assert prop is None 

1953 ( 

1954 left, 

1955 replace_from_obj_index, 

1956 use_entity_index, 

1957 ) = self._join_determine_implicit_left_side( 

1958 entities_collection, left, right, onclause 

1959 ) 

1960 else: 

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

1962 # Determine where in our 

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

1964 # existing entity it corresponds to. 

1965 ( 

1966 replace_from_obj_index, 

1967 use_entity_index, 

1968 ) = self._join_place_explicit_left_side(entities_collection, left) 

1969 

1970 if left is right: 

1971 raise sa_exc.InvalidRequestError( 

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

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

1974 ) 

1975 

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

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

1978 # get back the new effective "right" side 

1979 r_info, right, onclause = self._join_check_and_adapt_right_side( 

1980 left, right, onclause, prop 

1981 ) 

1982 

1983 if not r_info.is_selectable: 

1984 extra_criteria = self._get_extra_criteria(r_info) 

1985 else: 

1986 extra_criteria = () 

1987 

1988 if replace_from_obj_index is not None: 

1989 # splice into an existing element in the 

1990 # self._from_obj list 

1991 left_clause = self.from_clauses[replace_from_obj_index] 

1992 

1993 self.from_clauses = ( 

1994 self.from_clauses[:replace_from_obj_index] 

1995 + [ 

1996 _ORMJoin( 

1997 left_clause, 

1998 right, 

1999 onclause, 

2000 isouter=outerjoin, 

2001 full=full, 

2002 _extra_criteria=extra_criteria, 

2003 ) 

2004 ] 

2005 + self.from_clauses[replace_from_obj_index + 1 :] 

2006 ) 

2007 else: 

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

2009 if use_entity_index is not None: 

2010 # make use of _MapperEntity selectable, which is usually 

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

2012 # might be distinct 

2013 assert isinstance( 

2014 entities_collection[use_entity_index], _MapperEntity 

2015 ) 

2016 left_clause = entities_collection[use_entity_index].selectable 

2017 else: 

2018 left_clause = left 

2019 

2020 self.from_clauses = self.from_clauses + [ 

2021 _ORMJoin( 

2022 left_clause, 

2023 r_info, 

2024 onclause, 

2025 isouter=outerjoin, 

2026 full=full, 

2027 _extra_criteria=extra_criteria, 

2028 ) 

2029 ] 

2030 

2031 def _join_determine_implicit_left_side( 

2032 self, entities_collection, left, right, onclause 

2033 ): 

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

2035 determine if an existing FROM or entity in this query 

2036 can serve as the left hand side. 

2037 

2038 """ 

2039 

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

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

2042 # 

2043 # join(RightEntity) 

2044 # 

2045 # or 

2046 # 

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

2048 # 

2049 

2050 r_info = inspect(right) 

2051 

2052 replace_from_obj_index = use_entity_index = None 

2053 

2054 if self.from_clauses: 

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

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

2057 

2058 indexes = sql_util.find_left_clause_to_join_from( 

2059 self.from_clauses, r_info.selectable, onclause 

2060 ) 

2061 

2062 if len(indexes) == 1: 

2063 replace_from_obj_index = indexes[0] 

2064 left = self.from_clauses[replace_from_obj_index] 

2065 elif len(indexes) > 1: 

2066 raise sa_exc.InvalidRequestError( 

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

2068 "from, there are multiple FROMS which can " 

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

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

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

2072 "to help resolve the ambiguity." 

2073 ) 

2074 else: 

2075 raise sa_exc.InvalidRequestError( 

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

2077 "Please use the .select_from() " 

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

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

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

2081 ) 

2082 

2083 elif entities_collection: 

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

2085 # come from our list of entities. 

2086 

2087 potential = {} 

2088 for entity_index, ent in enumerate(entities_collection): 

2089 entity = ent.entity_zero_or_selectable 

2090 if entity is None: 

2091 continue 

2092 ent_info = inspect(entity) 

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

2094 continue 

2095 

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

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

2098 # against a series of columns from the same selectable 

2099 if isinstance(ent, _MapperEntity): 

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

2101 else: 

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

2103 

2104 all_clauses = list(potential.keys()) 

2105 indexes = sql_util.find_left_clause_to_join_from( 

2106 all_clauses, r_info.selectable, onclause 

2107 ) 

2108 

2109 if len(indexes) == 1: 

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

2111 elif len(indexes) > 1: 

2112 raise sa_exc.InvalidRequestError( 

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

2114 "from, there are multiple FROMS which can " 

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

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

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

2118 "to help resolve the ambiguity." 

2119 ) 

2120 else: 

2121 raise sa_exc.InvalidRequestError( 

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

2123 "Please use the .select_from() " 

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

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

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

2127 ) 

2128 else: 

2129 raise sa_exc.InvalidRequestError( 

2130 "No entities to join from; please use " 

2131 "select_from() to establish the left " 

2132 "entity/selectable of this join" 

2133 ) 

2134 

2135 return left, replace_from_obj_index, use_entity_index 

2136 

2137 def _join_place_explicit_left_side(self, entities_collection, left): 

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

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

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

2141 existing entities. 

2142 

2143 """ 

2144 

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

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

2147 # Relationship was given, e.g.: 

2148 # 

2149 # join(RightEntity, LeftEntity.right) 

2150 # 

2151 # or 

2152 # 

2153 # join(LeftEntity.right) 

2154 # 

2155 # as well as string forms: 

2156 # 

2157 # join(RightEntity, "right") 

2158 # 

2159 # etc. 

2160 # 

2161 

2162 replace_from_obj_index = use_entity_index = None 

2163 

2164 l_info = inspect(left) 

2165 if self.from_clauses: 

2166 indexes = sql_util.find_left_clause_that_matches_given( 

2167 self.from_clauses, l_info.selectable 

2168 ) 

2169 

2170 if len(indexes) > 1: 

2171 raise sa_exc.InvalidRequestError( 

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

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

2174 "ON clause." 

2175 ) 

2176 

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

2178 # an existing FROM in the self._from_obj tuple 

2179 if indexes: 

2180 replace_from_obj_index = indexes[0] 

2181 

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

2183 # self._from_obj tuple 

2184 

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

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

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

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

2189 if ( 

2190 replace_from_obj_index is None 

2191 and entities_collection 

2192 and hasattr(l_info, "mapper") 

2193 ): 

2194 for idx, ent in enumerate(entities_collection): 

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

2196 # matching? 

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

2198 use_entity_index = idx 

2199 break 

2200 

2201 return replace_from_obj_index, use_entity_index 

2202 

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

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

2205 according to polymorphic mapping translations, aliasing on the query 

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

2207 overlapping tables. 

2208 

2209 """ 

2210 

2211 l_info = inspect(left) 

2212 r_info = inspect(right) 

2213 

2214 overlap = False 

2215 

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

2217 # if the target is a joined inheritance mapping, 

2218 # be more liberal about auto-aliasing. 

2219 if right_mapper and ( 

2220 right_mapper.with_polymorphic 

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

2222 ): 

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

2224 if sql_util.selectables_overlap( 

2225 l_info.selectable, from_obj 

2226 ) and sql_util.selectables_overlap( 

2227 from_obj, r_info.selectable 

2228 ): 

2229 overlap = True 

2230 break 

2231 

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

2233 raise sa_exc.InvalidRequestError( 

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

2235 % l_info.selectable 

2236 ) 

2237 

2238 right_mapper, right_selectable, right_is_aliased = ( 

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

2240 r_info.selectable, 

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

2242 ) 

2243 

2244 if ( 

2245 right_mapper 

2246 and prop 

2247 and not right_mapper.common_parent(prop.mapper) 

2248 ): 

2249 raise sa_exc.InvalidRequestError( 

2250 "Join target %s does not correspond to " 

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

2252 ) 

2253 

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

2255 # purposes at the moment 

2256 if hasattr(r_info, "mapper"): 

2257 self._join_entities += (r_info,) 

2258 

2259 need_adapter = False 

2260 

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

2262 if r_info.is_clause_element: 

2263 if prop: 

2264 right_mapper = prop.mapper 

2265 

2266 if right_selectable._is_lateral: 

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

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

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

2270 current_adapter = self._get_current_adapter() 

2271 if current_adapter is not None: 

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

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

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

2275 right = current_adapter(right, True) 

2276 

2277 elif prop: 

2278 # joining to selectable with a mapper property given 

2279 # as the ON clause 

2280 

2281 if not right_selectable.is_derived_from( 

2282 right_mapper.persist_selectable 

2283 ): 

2284 raise sa_exc.InvalidRequestError( 

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

2286 % ( 

2287 right_selectable.description, 

2288 right_mapper.persist_selectable.description, 

2289 ) 

2290 ) 

2291 

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

2293 # turn it into an alias(). 

2294 if isinstance(right_selectable, expression.SelectBase): 

2295 right_selectable = coercions.expect( 

2296 roles.FromClauseRole, right_selectable 

2297 ) 

2298 need_adapter = True 

2299 

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

2301 right = AliasedClass(right_mapper, right_selectable) 

2302 

2303 util.warn_deprecated( 

2304 "An alias is being generated automatically against " 

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

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

2307 "Use the aliased() " 

2308 "construct explicitly, see the linked example." 

2309 % right_mapper, 

2310 "1.4", 

2311 code="xaj1", 

2312 ) 

2313 

2314 # test for overlap: 

2315 # orm/inheritance/relationships.py 

2316 # SelfReferentialM2MTest 

2317 aliased_entity = right_mapper and not right_is_aliased and overlap 

2318 

2319 if not need_adapter and aliased_entity: 

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

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

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

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

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

2325 # aliasing is desirable. 

2326 right = AliasedClass(right, flat=True) 

2327 need_adapter = True 

2328 

2329 util.warn( 

2330 "An alias is being generated automatically against " 

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

2332 "legacy pattern which may be " 

2333 "deprecated in a later release. Use the " 

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

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

2336 code="xaj2", 

2337 ) 

2338 

2339 if need_adapter: 

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

2341 # a warning has been emitted. 

2342 assert right_mapper 

2343 

2344 adapter = ORMAdapter( 

2345 _TraceAdaptRole.DEPRECATED_JOIN_ADAPT_RIGHT_SIDE, 

2346 inspect(right), 

2347 equivalents=right_mapper._equivalent_columns, 

2348 ) 

2349 

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

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

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

2353 # set are also adapted. 

2354 self._mapper_loads_polymorphically_with(right_mapper, adapter) 

2355 elif ( 

2356 not r_info.is_clause_element 

2357 and not right_is_aliased 

2358 and right_mapper._has_aliased_polymorphic_fromclause 

2359 ): 

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

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

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

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

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

2365 # strictly necessary. 

2366 # see test/orm/test_core_compilation.py 

2367 # ::RelNaturalAliasedJoinsTest::test_straight 

2368 # and similar 

2369 self._mapper_loads_polymorphically_with( 

2370 right_mapper, 

2371 ORMAdapter( 

2372 _TraceAdaptRole.WITH_POLYMORPHIC_ADAPTER_RIGHT_JOIN, 

2373 right_mapper, 

2374 selectable=right_mapper.selectable, 

2375 equivalents=right_mapper._equivalent_columns, 

2376 ), 

2377 ) 

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

2379 # adapters that are in place right now 

2380 if isinstance(onclause, expression.ClauseElement): 

2381 current_adapter = self._get_current_adapter() 

2382 if current_adapter: 

2383 onclause = current_adapter(onclause, True) 

2384 

2385 # if joining on a MapperProperty path, 

2386 # track the path to prevent redundant joins 

2387 if prop: 

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

2389 

2390 return inspect(right), right, onclause 

2391 

2392 @property 

2393 def _select_args(self): 

2394 return { 

2395 "limit_clause": self.select_statement._limit_clause, 

2396 "offset_clause": self.select_statement._offset_clause, 

2397 "distinct": self.distinct, 

2398 "distinct_on": self.distinct_on, 

2399 "prefixes": self.select_statement._prefixes, 

2400 "suffixes": self.select_statement._suffixes, 

2401 "group_by": self.group_by or None, 

2402 "fetch_clause": self.select_statement._fetch_clause, 

2403 "fetch_clause_options": ( 

2404 self.select_statement._fetch_clause_options 

2405 ), 

2406 "independent_ctes": self.select_statement._independent_ctes, 

2407 "independent_ctes_opts": ( 

2408 self.select_statement._independent_ctes_opts 

2409 ), 

2410 } 

2411 

2412 @property 

2413 def _should_nest_selectable(self): 

2414 kwargs = self._select_args 

2415 return ( 

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

2417 or kwargs.get("offset_clause") is not None 

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

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

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

2421 ) 

2422 

2423 def _get_extra_criteria(self, ext_info): 

2424 if ( 

2425 "additional_entity_criteria", 

2426 ext_info.mapper, 

2427 ) in self.global_attributes: 

2428 return tuple( 

2429 ae._resolve_where_criteria(ext_info) 

2430 for ae in self.global_attributes[ 

2431 ("additional_entity_criteria", ext_info.mapper) 

2432 ] 

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

2434 and ae._should_include(self) 

2435 ) 

2436 else: 

2437 return () 

2438 

2439 def _adjust_for_extra_criteria(self): 

2440 """Apply extra criteria filtering. 

2441 

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

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

2444 add criterion to the WHERE 

2445 clause of the given QueryContext such that only the appropriate 

2446 subtypes are selected from the total results. 

2447 

2448 Additionally, add WHERE criteria originating from LoaderCriteriaOptions 

2449 associated with the global context. 

2450 

2451 """ 

2452 

2453 for fromclause in self.from_clauses: 

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

2455 

2456 if ( 

2457 ext_info 

2458 and ( 

2459 ext_info.mapper._single_table_criterion is not None 

2460 or ("additional_entity_criteria", ext_info.mapper) 

2461 in self.global_attributes 

2462 ) 

2463 and ext_info not in self.extra_criteria_entities 

2464 ): 

2465 self.extra_criteria_entities[ext_info] = ( 

2466 ext_info, 

2467 ext_info._adapter if ext_info.is_aliased_class else None, 

2468 ) 

2469 

2470 search = set(self.extra_criteria_entities.values()) 

2471 

2472 for ext_info, adapter in search: 

2473 if ext_info in self._join_entities: 

2474 continue 

2475 

2476 single_crit = ext_info.mapper._single_table_criterion 

2477 

2478 if self.compile_options._for_refresh_state: 

2479 additional_entity_criteria = [] 

2480 else: 

2481 additional_entity_criteria = self._get_extra_criteria(ext_info) 

2482 

2483 if single_crit is not None: 

2484 additional_entity_criteria += (single_crit,) 

2485 

2486 current_adapter = self._get_current_adapter() 

2487 for crit in additional_entity_criteria: 

2488 if adapter: 

2489 crit = adapter.traverse(crit) 

2490 

2491 if current_adapter: 

2492 crit = sql_util._deep_annotate(crit, {"_orm_adapt": True}) 

2493 crit = current_adapter(crit, False) 

2494 self._where_criteria += (crit,) 

2495 

2496 

2497def _column_descriptions( 

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

2499 compile_state: Optional[ORMSelectCompileState] = None, 

2500 legacy: bool = False, 

2501) -> List[ORMColumnDescription]: 

2502 if compile_state is None: 

2503 compile_state = ORMSelectCompileState._create_entities_collection( 

2504 query_or_select_stmt, legacy=legacy 

2505 ) 

2506 ctx = compile_state 

2507 d = [ 

2508 { 

2509 "name": ent._label_name, 

2510 "type": ent.type, 

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

2512 "expr": ent.expr, 

2513 "entity": ( 

2514 getattr(insp_ent, "entity", None) 

2515 if ent.entity_zero is not None 

2516 and not insp_ent.is_clause_element 

2517 else None 

2518 ), 

2519 } 

2520 for ent, insp_ent in [ 

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

2522 ] 

2523 ] 

2524 return d 

2525 

2526 

2527def _legacy_filter_by_entity_zero( 

2528 query_or_augmented_select: Union[Query[Any], Select[Any]] 

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

2530 self = query_or_augmented_select 

2531 if self._setup_joins: 

2532 _last_joined_entity = self._last_joined_entity 

2533 if _last_joined_entity is not None: 

2534 return _last_joined_entity 

2535 

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

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

2538 

2539 return _entity_from_pre_ent_zero(self) 

2540 

2541 

2542def _entity_from_pre_ent_zero( 

2543 query_or_augmented_select: Union[Query[Any], Select[Any]] 

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

2545 self = query_or_augmented_select 

2546 if not self._raw_columns: 

2547 return None 

2548 

2549 ent = self._raw_columns[0] 

2550 

2551 if "parententity" in ent._annotations: 

2552 return ent._annotations["parententity"] 

2553 elif isinstance(ent, ORMColumnsClauseRole): 

2554 return ent.entity 

2555 elif "bundle" in ent._annotations: 

2556 return ent._annotations["bundle"] 

2557 else: 

2558 return ent 

2559 

2560 

2561def _determine_last_joined_entity( 

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

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

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

2565 if not setup_joins: 

2566 return None 

2567 

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

2569 

2570 if isinstance( 

2571 target, 

2572 attributes.QueryableAttribute, 

2573 ): 

2574 return target.entity 

2575 else: 

2576 return target 

2577 

2578 

2579class _QueryEntity: 

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

2581 

2582 __slots__ = () 

2583 

2584 supports_single_entity: bool 

2585 

2586 _non_hashable_value = False 

2587 _null_column_type = False 

2588 use_id_for_hash = False 

2589 

2590 _label_name: Optional[str] 

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

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

2593 entity_zero: Optional[_InternalEntityType] 

2594 

2595 def setup_compile_state(self, compile_state: ORMCompileState) -> None: 

2596 raise NotImplementedError() 

2597 

2598 def setup_dml_returning_compile_state( 

2599 self, 

2600 compile_state: ORMCompileState, 

2601 adapter: Optional[_DMLReturningColFilter], 

2602 ) -> None: 

2603 raise NotImplementedError() 

2604 

2605 def row_processor(self, context, result): 

2606 raise NotImplementedError() 

2607 

2608 @classmethod 

2609 def to_compile_state( 

2610 cls, compile_state, entities, entities_collection, is_current_entities 

2611 ): 

2612 for idx, entity in enumerate(entities): 

2613 if entity._is_lambda_element: 

2614 if entity._is_sequence: 

2615 cls.to_compile_state( 

2616 compile_state, 

2617 entity._resolved, 

2618 entities_collection, 

2619 is_current_entities, 

2620 ) 

2621 continue 

2622 else: 

2623 entity = entity._resolved 

2624 

2625 if entity.is_clause_element: 

2626 if entity.is_selectable: 

2627 if "parententity" in entity._annotations: 

2628 _MapperEntity( 

2629 compile_state, 

2630 entity, 

2631 entities_collection, 

2632 is_current_entities, 

2633 ) 

2634 else: 

2635 _ColumnEntity._for_columns( 

2636 compile_state, 

2637 entity._select_iterable, 

2638 entities_collection, 

2639 idx, 

2640 is_current_entities, 

2641 ) 

2642 else: 

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

2644 _BundleEntity( 

2645 compile_state, 

2646 entity, 

2647 entities_collection, 

2648 is_current_entities, 

2649 ) 

2650 elif entity._is_clause_list: 

2651 # this is legacy only - test_composites.py 

2652 # test_query_cols_legacy 

2653 _ColumnEntity._for_columns( 

2654 compile_state, 

2655 entity._select_iterable, 

2656 entities_collection, 

2657 idx, 

2658 is_current_entities, 

2659 ) 

2660 else: 

2661 _ColumnEntity._for_columns( 

2662 compile_state, 

2663 [entity], 

2664 entities_collection, 

2665 idx, 

2666 is_current_entities, 

2667 ) 

2668 elif entity.is_bundle: 

2669 _BundleEntity(compile_state, entity, entities_collection) 

2670 

2671 return entities_collection 

2672 

2673 

2674class _MapperEntity(_QueryEntity): 

2675 """mapper/class/AliasedClass entity""" 

2676 

2677 __slots__ = ( 

2678 "expr", 

2679 "mapper", 

2680 "entity_zero", 

2681 "is_aliased_class", 

2682 "path", 

2683 "_extra_entities", 

2684 "_label_name", 

2685 "_with_polymorphic_mappers", 

2686 "selectable", 

2687 "_polymorphic_discriminator", 

2688 ) 

2689 

2690 expr: _InternalEntityType 

2691 mapper: Mapper[Any] 

2692 entity_zero: _InternalEntityType 

2693 is_aliased_class: bool 

2694 path: PathRegistry 

2695 _label_name: str 

2696 

2697 def __init__( 

2698 self, compile_state, entity, entities_collection, is_current_entities 

2699 ): 

2700 entities_collection.append(self) 

2701 if is_current_entities: 

2702 if compile_state._primary_entity is None: 

2703 compile_state._primary_entity = self 

2704 compile_state._has_mapper_entities = True 

2705 compile_state._has_orm_entities = True 

2706 

2707 entity = entity._annotations["parententity"] 

2708 entity._post_inspect 

2709 ext_info = self.entity_zero = entity 

2710 entity = ext_info.entity 

2711 

2712 self.expr = entity 

2713 self.mapper = mapper = ext_info.mapper 

2714 

2715 self._extra_entities = (self.expr,) 

2716 

2717 if ext_info.is_aliased_class: 

2718 self._label_name = ext_info.name 

2719 else: 

2720 self._label_name = mapper.class_.__name__ 

2721 

2722 self.is_aliased_class = ext_info.is_aliased_class 

2723 self.path = ext_info._path_registry 

2724 

2725 self.selectable = ext_info.selectable 

2726 self._with_polymorphic_mappers = ext_info.with_polymorphic_mappers 

2727 self._polymorphic_discriminator = ext_info.polymorphic_on 

2728 

2729 if mapper._should_select_with_poly_adapter: 

2730 compile_state._create_with_polymorphic_adapter( 

2731 ext_info, self.selectable 

2732 ) 

2733 

2734 supports_single_entity = True 

2735 

2736 _non_hashable_value = True 

2737 use_id_for_hash = True 

2738 

2739 @property 

2740 def type(self): 

2741 return self.mapper.class_ 

2742 

2743 @property 

2744 def entity_zero_or_selectable(self): 

2745 return self.entity_zero 

2746 

2747 def corresponds_to(self, entity): 

2748 return _entity_corresponds_to(self.entity_zero, entity) 

2749 

2750 def _get_entity_clauses(self, compile_state): 

2751 adapter = None 

2752 

2753 if not self.is_aliased_class: 

2754 if compile_state._polymorphic_adapters: 

2755 adapter = compile_state._polymorphic_adapters.get( 

2756 self.mapper, None 

2757 ) 

2758 else: 

2759 adapter = self.entity_zero._adapter 

2760 

2761 if adapter: 

2762 if compile_state._from_obj_alias: 

2763 ret = adapter.wrap(compile_state._from_obj_alias) 

2764 else: 

2765 ret = adapter 

2766 else: 

2767 ret = compile_state._from_obj_alias 

2768 

2769 return ret 

2770 

2771 def row_processor(self, context, result): 

2772 compile_state = context.compile_state 

2773 adapter = self._get_entity_clauses(compile_state) 

2774 

2775 if compile_state.compound_eager_adapter and adapter: 

2776 adapter = adapter.wrap(compile_state.compound_eager_adapter) 

2777 elif not adapter: 

2778 adapter = compile_state.compound_eager_adapter 

2779 

2780 if compile_state._primary_entity is self: 

2781 only_load_props = compile_state.compile_options._only_load_props 

2782 refresh_state = context.refresh_state 

2783 else: 

2784 only_load_props = refresh_state = None 

2785 

2786 _instance = loading._instance_processor( 

2787 self, 

2788 self.mapper, 

2789 context, 

2790 result, 

2791 self.path, 

2792 adapter, 

2793 only_load_props=only_load_props, 

2794 refresh_state=refresh_state, 

2795 polymorphic_discriminator=self._polymorphic_discriminator, 

2796 ) 

2797 

2798 return _instance, self._label_name, self._extra_entities 

2799 

2800 def setup_dml_returning_compile_state( 

2801 self, 

2802 compile_state: ORMCompileState, 

2803 adapter: Optional[_DMLReturningColFilter], 

2804 ) -> None: 

2805 loading._setup_entity_query( 

2806 compile_state, 

2807 self.mapper, 

2808 self, 

2809 self.path, 

2810 adapter, 

2811 compile_state.primary_columns, 

2812 with_polymorphic=self._with_polymorphic_mappers, 

2813 only_load_props=compile_state.compile_options._only_load_props, 

2814 polymorphic_discriminator=self._polymorphic_discriminator, 

2815 ) 

2816 

2817 def setup_compile_state(self, compile_state): 

2818 adapter = self._get_entity_clauses(compile_state) 

2819 

2820 single_table_crit = self.mapper._single_table_criterion 

2821 if ( 

2822 single_table_crit is not None 

2823 or ("additional_entity_criteria", self.mapper) 

2824 in compile_state.global_attributes 

2825 ): 

2826 ext_info = self.entity_zero 

2827 compile_state.extra_criteria_entities[ext_info] = ( 

2828 ext_info, 

2829 ext_info._adapter if ext_info.is_aliased_class else None, 

2830 ) 

2831 

2832 loading._setup_entity_query( 

2833 compile_state, 

2834 self.mapper, 

2835 self, 

2836 self.path, 

2837 adapter, 

2838 compile_state.primary_columns, 

2839 with_polymorphic=self._with_polymorphic_mappers, 

2840 only_load_props=compile_state.compile_options._only_load_props, 

2841 polymorphic_discriminator=self._polymorphic_discriminator, 

2842 ) 

2843 compile_state._fallback_from_clauses.append(self.selectable) 

2844 

2845 

2846class _BundleEntity(_QueryEntity): 

2847 _extra_entities = () 

2848 

2849 __slots__ = ( 

2850 "bundle", 

2851 "expr", 

2852 "type", 

2853 "_label_name", 

2854 "_entities", 

2855 "supports_single_entity", 

2856 ) 

2857 

2858 _entities: List[_QueryEntity] 

2859 bundle: Bundle 

2860 type: Type[Any] 

2861 _label_name: str 

2862 supports_single_entity: bool 

2863 expr: Bundle 

2864 

2865 def __init__( 

2866 self, 

2867 compile_state, 

2868 expr, 

2869 entities_collection, 

2870 is_current_entities, 

2871 setup_entities=True, 

2872 parent_bundle=None, 

2873 ): 

2874 compile_state._has_orm_entities = True 

2875 

2876 expr = expr._annotations["bundle"] 

2877 if parent_bundle: 

2878 parent_bundle._entities.append(self) 

2879 else: 

2880 entities_collection.append(self) 

2881 

2882 if isinstance( 

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

2884 ): 

2885 bundle = expr.__clause_element__() 

2886 else: 

2887 bundle = expr 

2888 

2889 self.bundle = self.expr = bundle 

2890 self.type = type(bundle) 

2891 self._label_name = bundle.name 

2892 self._entities = [] 

2893 

2894 if setup_entities: 

2895 for expr in bundle.exprs: 

2896 if "bundle" in expr._annotations: 

2897 _BundleEntity( 

2898 compile_state, 

2899 expr, 

2900 entities_collection, 

2901 is_current_entities, 

2902 parent_bundle=self, 

2903 ) 

2904 elif isinstance(expr, Bundle): 

2905 _BundleEntity( 

2906 compile_state, 

2907 expr, 

2908 entities_collection, 

2909 is_current_entities, 

2910 parent_bundle=self, 

2911 ) 

2912 else: 

2913 _ORMColumnEntity._for_columns( 

2914 compile_state, 

2915 [expr], 

2916 entities_collection, 

2917 None, 

2918 is_current_entities, 

2919 parent_bundle=self, 

2920 ) 

2921 

2922 self.supports_single_entity = self.bundle.single_entity 

2923 

2924 @property 

2925 def mapper(self): 

2926 ezero = self.entity_zero 

2927 if ezero is not None: 

2928 return ezero.mapper 

2929 else: 

2930 return None 

2931 

2932 @property 

2933 def entity_zero(self): 

2934 for ent in self._entities: 

2935 ezero = ent.entity_zero 

2936 if ezero is not None: 

2937 return ezero 

2938 else: 

2939 return None 

2940 

2941 def corresponds_to(self, entity): 

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

2943 # we are working around it 

2944 return False 

2945 

2946 @property 

2947 def entity_zero_or_selectable(self): 

2948 for ent in self._entities: 

2949 ezero = ent.entity_zero_or_selectable 

2950 if ezero is not None: 

2951 return ezero 

2952 else: 

2953 return None 

2954 

2955 def setup_compile_state(self, compile_state): 

2956 for ent in self._entities: 

2957 ent.setup_compile_state(compile_state) 

2958 

2959 def setup_dml_returning_compile_state( 

2960 self, 

2961 compile_state: ORMCompileState, 

2962 adapter: Optional[_DMLReturningColFilter], 

2963 ) -> None: 

2964 return self.setup_compile_state(compile_state) 

2965 

2966 def row_processor(self, context, result): 

2967 procs, labels, extra = zip( 

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

2969 ) 

2970 

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

2972 

2973 return proc, self._label_name, self._extra_entities 

2974 

2975 

2976class _ColumnEntity(_QueryEntity): 

2977 __slots__ = ( 

2978 "_fetch_column", 

2979 "_row_processor", 

2980 "raw_column_index", 

2981 "translate_raw_column", 

2982 ) 

2983 

2984 @classmethod 

2985 def _for_columns( 

2986 cls, 

2987 compile_state, 

2988 columns, 

2989 entities_collection, 

2990 raw_column_index, 

2991 is_current_entities, 

2992 parent_bundle=None, 

2993 ): 

2994 for column in columns: 

2995 annotations = column._annotations 

2996 if "parententity" in annotations: 

2997 _entity = annotations["parententity"] 

2998 else: 

2999 _entity = sql_util.extract_first_column_annotation( 

3000 column, "parententity" 

3001 ) 

3002 

3003 if _entity: 

3004 if "identity_token" in column._annotations: 

3005 _IdentityTokenEntity( 

3006 compile_state, 

3007 column, 

3008 entities_collection, 

3009 _entity, 

3010 raw_column_index, 

3011 is_current_entities, 

3012 parent_bundle=parent_bundle, 

3013 ) 

3014 else: 

3015 _ORMColumnEntity( 

3016 compile_state, 

3017 column, 

3018 entities_collection, 

3019 _entity, 

3020 raw_column_index, 

3021 is_current_entities, 

3022 parent_bundle=parent_bundle, 

3023 ) 

3024 else: 

3025 _RawColumnEntity( 

3026 compile_state, 

3027 column, 

3028 entities_collection, 

3029 raw_column_index, 

3030 is_current_entities, 

3031 parent_bundle=parent_bundle, 

3032 ) 

3033 

3034 @property 

3035 def type(self): 

3036 return self.column.type 

3037 

3038 @property 

3039 def _non_hashable_value(self): 

3040 return not self.column.type.hashable 

3041 

3042 @property 

3043 def _null_column_type(self): 

3044 return self.column.type._isnull 

3045 

3046 def row_processor(self, context, result): 

3047 compile_state = context.compile_state 

3048 

3049 # the resulting callable is entirely cacheable so just return 

3050 # it if we already made one 

3051 if self._row_processor is not None: 

3052 getter, label_name, extra_entities = self._row_processor 

3053 if self.translate_raw_column: 

3054 extra_entities += ( 

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

3056 ) 

3057 

3058 return getter, label_name, extra_entities 

3059 

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

3061 # setup_compile_state, to avoid doing redundant work 

3062 if self._fetch_column is not None: 

3063 column = self._fetch_column 

3064 else: 

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

3066 # and setup_compile_state may not have been called. 

3067 column = self.column 

3068 

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

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

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

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

3073 if compile_state._from_obj_alias: 

3074 column = compile_state._from_obj_alias.columns[column] 

3075 

3076 if column._annotations: 

3077 # annotated columns perform more slowly in compiler and 

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

3079 column = column._deannotate() 

3080 

3081 if compile_state.compound_eager_adapter: 

3082 column = compile_state.compound_eager_adapter.columns[column] 

3083 

3084 getter = result._getter(column) 

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

3086 self._row_processor = ret 

3087 

3088 if self.translate_raw_column: 

3089 extra_entities = self._extra_entities + ( 

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

3091 ) 

3092 return getter, self._label_name, extra_entities 

3093 else: 

3094 return ret 

3095 

3096 

3097class _RawColumnEntity(_ColumnEntity): 

3098 entity_zero = None 

3099 mapper = None 

3100 supports_single_entity = False 

3101 

3102 __slots__ = ( 

3103 "expr", 

3104 "column", 

3105 "_label_name", 

3106 "entity_zero_or_selectable", 

3107 "_extra_entities", 

3108 ) 

3109 

3110 def __init__( 

3111 self, 

3112 compile_state, 

3113 column, 

3114 entities_collection, 

3115 raw_column_index, 

3116 is_current_entities, 

3117 parent_bundle=None, 

3118 ): 

3119 self.expr = column 

3120 self.raw_column_index = raw_column_index 

3121 self.translate_raw_column = raw_column_index is not None 

3122 

3123 if column._is_star: 

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

3125 

3126 if not is_current_entities or column._is_text_clause: 

3127 self._label_name = None 

3128 else: 

3129 if parent_bundle: 

3130 self._label_name = column._proxy_key 

3131 else: 

3132 self._label_name = compile_state._label_convention(column) 

3133 

3134 if parent_bundle: 

3135 parent_bundle._entities.append(self) 

3136 else: 

3137 entities_collection.append(self) 

3138 

3139 self.column = column 

3140 self.entity_zero_or_selectable = ( 

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

3142 ) 

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

3144 self._fetch_column = self._row_processor = None 

3145 

3146 def corresponds_to(self, entity): 

3147 return False 

3148 

3149 def setup_dml_returning_compile_state( 

3150 self, 

3151 compile_state: ORMCompileState, 

3152 adapter: Optional[_DMLReturningColFilter], 

3153 ) -> None: 

3154 return self.setup_compile_state(compile_state) 

3155 

3156 def setup_compile_state(self, compile_state): 

3157 current_adapter = compile_state._get_current_adapter() 

3158 if current_adapter: 

3159 column = current_adapter(self.column, False) 

3160 if column is None: 

3161 return 

3162 else: 

3163 column = self.column 

3164 

3165 if column._annotations: 

3166 # annotated columns perform more slowly in compiler and 

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

3168 column = column._deannotate() 

3169 

3170 compile_state.dedupe_columns.add(column) 

3171 compile_state.primary_columns.append(column) 

3172 self._fetch_column = column 

3173 

3174 

3175class _ORMColumnEntity(_ColumnEntity): 

3176 """Column/expression based entity.""" 

3177 

3178 supports_single_entity = False 

3179 

3180 __slots__ = ( 

3181 "expr", 

3182 "mapper", 

3183 "column", 

3184 "_label_name", 

3185 "entity_zero_or_selectable", 

3186 "entity_zero", 

3187 "_extra_entities", 

3188 ) 

3189 

3190 def __init__( 

3191 self, 

3192 compile_state, 

3193 column, 

3194 entities_collection, 

3195 parententity, 

3196 raw_column_index, 

3197 is_current_entities, 

3198 parent_bundle=None, 

3199 ): 

3200 annotations = column._annotations 

3201 

3202 _entity = parententity 

3203 

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

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

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

3207 # within internal loaders. 

3208 

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

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

3211 if orm_key: 

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

3213 self.translate_raw_column = False 

3214 else: 

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

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

3217 # include this column position from the invoked statement 

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

3219 # it can be targeted by identity after caching 

3220 self.expr = column 

3221 self.translate_raw_column = raw_column_index is not None 

3222 

3223 self.raw_column_index = raw_column_index 

3224 

3225 if is_current_entities: 

3226 if parent_bundle: 

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

3228 else: 

3229 self._label_name = compile_state._label_convention( 

3230 column, col_name=orm_key 

3231 ) 

3232 else: 

3233 self._label_name = None 

3234 

3235 _entity._post_inspect 

3236 self.entity_zero = self.entity_zero_or_selectable = ezero = _entity 

3237 self.mapper = mapper = _entity.mapper 

3238 

3239 if parent_bundle: 

3240 parent_bundle._entities.append(self) 

3241 else: 

3242 entities_collection.append(self) 

3243 

3244 compile_state._has_orm_entities = True 

3245 

3246 self.column = column 

3247 

3248 self._fetch_column = self._row_processor = None 

3249 

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

3251 

3252 if mapper._should_select_with_poly_adapter: 

3253 compile_state._create_with_polymorphic_adapter( 

3254 ezero, ezero.selectable 

3255 ) 

3256 

3257 def corresponds_to(self, entity): 

3258 if _is_aliased_class(entity): 

3259 # TODO: polymorphic subclasses ? 

3260 return entity is self.entity_zero 

3261 else: 

3262 return not _is_aliased_class( 

3263 self.entity_zero 

3264 ) and entity.common_parent(self.entity_zero) 

3265 

3266 def setup_dml_returning_compile_state( 

3267 self, 

3268 compile_state: ORMCompileState, 

3269 adapter: Optional[_DMLReturningColFilter], 

3270 ) -> None: 

3271 

3272 self._fetch_column = column = self.column 

3273 if adapter: 

3274 column = adapter(column, False) 

3275 

3276 if column is not None: 

3277 compile_state.dedupe_columns.add(column) 

3278 compile_state.primary_columns.append(column) 

3279 

3280 def setup_compile_state(self, compile_state): 

3281 current_adapter = compile_state._get_current_adapter() 

3282 if current_adapter: 

3283 column = current_adapter(self.column, False) 

3284 if column is None: 

3285 assert compile_state.is_dml_returning 

3286 self._fetch_column = self.column 

3287 return 

3288 else: 

3289 column = self.column 

3290 

3291 ezero = self.entity_zero 

3292 

3293 single_table_crit = self.mapper._single_table_criterion 

3294 if ( 

3295 single_table_crit is not None 

3296 or ("additional_entity_criteria", self.mapper) 

3297 in compile_state.global_attributes 

3298 ): 

3299 compile_state.extra_criteria_entities[ezero] = ( 

3300 ezero, 

3301 ezero._adapter if ezero.is_aliased_class else None, 

3302 ) 

3303 

3304 if column._annotations and not column._expression_label: 

3305 # annotated columns perform more slowly in compiler and 

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

3307 column = column._deannotate() 

3308 

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

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

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

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

3313 # a scalar subquery. 

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

3315 ezero.selectable._from_objects 

3316 ): 

3317 compile_state._fallback_from_clauses.append(ezero.selectable) 

3318 

3319 compile_state.dedupe_columns.add(column) 

3320 compile_state.primary_columns.append(column) 

3321 self._fetch_column = column 

3322 

3323 

3324class _IdentityTokenEntity(_ORMColumnEntity): 

3325 translate_raw_column = False 

3326 

3327 def setup_compile_state(self, compile_state): 

3328 pass 

3329 

3330 def row_processor(self, context, result): 

3331 def getter(row): 

3332 return context.load_options._identity_token 

3333 

3334 return getter, self._label_name, self._extra_entities