diff options
| author | mike bayer <mike_mp@zzzcomputing.com> | 2021-06-22 22:05:35 +0000 |
|---|---|---|
| committer | Gerrit Code Review <gerrit@ci3.zzzcomputing.com> | 2021-06-22 22:05:35 +0000 |
| commit | 7110a5fa8644b29d35c4c81a1e8f7ebddebc40dc (patch) | |
| tree | 45ebf946b3414e45e982cbdf90e9d787de86cdf6 /lib/sqlalchemy | |
| parent | a84881e1b7505c3ffb9b9f0ac2b227cbd650aac2 (diff) | |
| parent | 2cf8c5868cb83185001755d86aa0f79e0318b53f (diff) | |
| download | sqlalchemy-7110a5fa8644b29d35c4c81a1e8f7ebddebc40dc.tar.gz | |
Merge "Export deferred columns but not col props; fix CTE labeling"
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/engine/cursor.py | 4 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/context.py | 23 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/mapper.py | 1 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/properties.py | 6 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/strategies.py | 4 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/compiler.py | 15 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/dml.py | 13 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/selectable.py | 62 |
8 files changed, 58 insertions, 70 deletions
diff --git a/lib/sqlalchemy/engine/cursor.py b/lib/sqlalchemy/engine/cursor.py index cf77d0835..965959846 100644 --- a/lib/sqlalchemy/engine/cursor.py +++ b/lib/sqlalchemy/engine/cursor.py @@ -132,9 +132,7 @@ class CursorResultMetaData(ResultMetaData): keymap_by_position = self._keymap_by_result_column_idx - for idx, new in enumerate( - invoked_statement._exported_columns_iterator() - ): + for idx, new in enumerate(invoked_statement._all_selected_columns): try: rec = keymap_by_position[idx] except KeyError: diff --git a/lib/sqlalchemy/orm/context.py b/lib/sqlalchemy/orm/context.py index 321eeada0..78026efb1 100644 --- a/lib/sqlalchemy/orm/context.py +++ b/lib/sqlalchemy/orm/context.py @@ -153,6 +153,7 @@ class ORMCompileState(CompileState): ("_only_load_props", InternalTraversal.dp_plain_obj), ("_set_base_alias", InternalTraversal.dp_boolean), ("_for_refresh_state", InternalTraversal.dp_boolean), + ("_render_for_subquery", InternalTraversal.dp_boolean), ] # set to True by default from Query._statement_20(), to indicate @@ -176,6 +177,7 @@ class ORMCompileState(CompileState): _only_load_props = None _set_base_alias = False _for_refresh_state = False + _render_for_subquery = False current_path = _path_registry @@ -530,9 +532,7 @@ class ORMSelectCompileState(ORMCompileState, SelectState): self.select_statement = select_statement # indicates this select() came from Query.statement - self.for_statement = ( - for_statement - ) = select_statement._compile_options._for_statement + self.for_statement = select_statement._compile_options._for_statement # generally if we are from Query or directly from a select() self.use_legacy_query_style = ( @@ -554,13 +554,12 @@ class ORMSelectCompileState(ORMCompileState, SelectState): self.compile_options = select_statement._compile_options - if not for_statement and not toplevel: - # for subqueries, turn off eagerloads. - # if "for_statement" mode is set, Query.subquery() - # would have set this flag to False already if that's what's - # desired + if not toplevel: + # for subqueries, turn off eagerloads and set + # "render_for_subquery". self.compile_options += { "_enable_eagerloads": False, + "_render_for_subquery": True, } # determine label style. we can make different decisions here. @@ -855,14 +854,6 @@ class ORMSelectCompileState(ORMCompileState, SelectState): return target @classmethod - def exported_columns_iterator(cls, statement): - return ( - elem - for elem in cls.all_selected_columns(statement) - if not elem._is_text_clause - ) - - @classmethod def all_selected_columns(cls, statement): for element in statement._raw_columns: if ( diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 6054029aa..378c65278 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -2311,6 +2311,7 @@ class Mapper( adapter.columns[prop.columns[0]] if adapter else prop.columns[0] for prop in poly_properties if isinstance(prop, properties.ColumnProperty) + and prop._renders_in_subqueries ] def _columns_plus_keys(self, polymorphic_mappers=()): diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index 7823aca20..18121bb04 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -65,6 +65,7 @@ class ColumnProperty(StrategizedProperty): "_mapped_by_synonym", "_deferred_column_loader", "_raise_column_loader", + "_renders_in_subqueries", "raiseload", ) @@ -202,6 +203,11 @@ class ColumnProperty(StrategizedProperty): if self.raiseload: self.strategy_key += (("raiseload", True),) + def _memoized_attr__renders_in_subqueries(self): + return ("deferred", True) not in self.strategy_key or ( + self not in self.parent._readonly_props + ) + @util.preload_module("sqlalchemy.orm.state", "sqlalchemy.orm.strategies") def _memoized_attr__deferred_column_loader(self): state = util.preloaded.orm_state diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index 4a8ebaabb..2a254f8de 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -421,6 +421,10 @@ class DeferredColumnLoader(LoaderStrategy): if ( ( + compile_state.compile_options._render_for_subquery + and self.parent_property._renders_in_subqueries + ) + or ( loadopt and "undefer_pks" in loadopt.local_opts and set(self.columns).intersection( diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index 220a0fa99..f47ea8f33 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -2562,18 +2562,20 @@ class SQLCompiler(Compiled): elif isinstance(cte.element, selectable.CompoundSelect): col_source = cte.element.selects[0] else: - assert False + assert False, "cte should only be against SelectBase" recur_cols = [ c for c in util.unique_list( - col_source._exported_columns_iterator() + col_source._all_selected_columns ) if c is not None ] text += "(%s)" % ( ", ".join( - self.preparer.format_column(ident) + self.preparer.format_column( + ident, anon_map=self.anon_map + ) for ident in recur_cols ) ) @@ -5012,11 +5014,18 @@ class IdentifierPreparer(object): name=None, table_name=None, use_schema=False, + anon_map=None, ): """Prepare a quoted column name.""" if name is None: name = column.name + + if anon_map is not None and isinstance( + name, elements._truncated_label + ): + name = name.apply_map(anon_map) + if not getattr(column, "is_literal", False): if use_table: return ( diff --git a/lib/sqlalchemy/sql/dml.py b/lib/sqlalchemy/sql/dml.py index ea10bfc27..a6ef62619 100644 --- a/lib/sqlalchemy/sql/dml.py +++ b/lib/sqlalchemy/sql/dml.py @@ -27,6 +27,7 @@ from .elements import ClauseElement from .elements import Null from .selectable import HasCTE from .selectable import HasPrefixes +from .selectable import ReturnsRows from .visitors import InternalTraversal from .. import exc from .. import util @@ -199,6 +200,7 @@ class UpdateBase( HasCompileState, DialectKWArgs, HasPrefixes, + ReturnsRows, Executable, ClauseElement, ): @@ -415,13 +417,8 @@ class UpdateBase( coercions.expect(roles.ColumnsClauseRole, c) for c in cols ) - def _exported_columns_iterator(self): - """Return the RETURNING columns as a sequence for this statement. - - .. versionadded:: 1.4 - - """ - + @property + def _all_selected_columns(self): return self._returning @property @@ -434,7 +431,7 @@ class UpdateBase( """ # TODO: no coverage here return ColumnCollection( - (c.key, c) for c in self._exported_columns_iterator() + (c.key, c) for c in self._all_selected_columns ).as_immutable() @_generative diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py index e1dee091b..557c443bf 100644 --- a/lib/sqlalchemy/sql/selectable.py +++ b/lib/sqlalchemy/sql/selectable.py @@ -109,23 +109,19 @@ class ReturnsRows(roles.ReturnsRowsRole, ClauseElement): @property def selectable(self): - raise NotImplementedError() - - def _exported_columns_iterator(self): - """An iterator of column objects that represents the "exported" - columns of this :class:`_expression.ReturnsRows`. + return self - This is the same set of columns as are returned by - :meth:`_expression.ReturnsRows.exported_columns` - except they are returned - as a simple iterator or sequence, rather than as a - :class:`_expression.ColumnCollection` namespace. + @property + def _all_selected_columns(self): + """A sequence of column expression objects that represents the + "selected" columns of this :class:`_expression.ReturnsRows`. - Subclasses should re-implement this method to bypass the interim - creation of the :class:`_expression.ColumnCollection` if appropriate. + This is typically equivalent to .exported_columns except it is + delivered in the form of a straight sequence and not keyed + :class:`_expression.ColumnCollection`. """ - return iter(self.exported_columns) + raise NotImplementedError() @property def exported_columns(self): @@ -161,10 +157,6 @@ class Selectable(ReturnsRows): is_selectable = True - @property - def selectable(self): - return self - def _refresh_for_new_column(self, column): raise NotImplementedError() @@ -3113,9 +3105,6 @@ class SelectStatementGrouping(GroupedElement, SelectBase): def _generate_proxy_for_new_column(self, column, subquery): return self.element._generate_proxy_for_new_column(subquery) - def _exported_columns_iterator(self): - return self.element._exported_columns_iterator() - @property def _all_selected_columns(self): return self.element._all_selected_columns @@ -3935,9 +3924,6 @@ class CompoundSelect(HasCompileState, GenerativeSelect): for select in self.selects: select._refresh_for_new_column(column) - def _exported_columns_iterator(self): - return self.selects[0]._exported_columns_iterator() - @property def _all_selected_columns(self): return self.selects[0]._all_selected_columns @@ -4335,7 +4321,7 @@ class SelectState(util.MemoizedSlots, CompileState): def _memoized_attr__label_resolve_dict(self): with_cols = dict( (c._resolve_label or c._label or c.key, c) - for c in self.statement._exported_columns_iterator() + for c in self.statement._all_selected_columns if c._allow_label_resolve ) only_froms = dict( @@ -4357,14 +4343,6 @@ class SelectState(util.MemoizedSlots, CompileState): return None @classmethod - def exported_columns_iterator(cls, statement): - return [ - c - for c in _select_iterables(statement._raw_columns) - if not c._is_text_clause - ] - - @classmethod def all_selected_columns(cls, statement): return [c for c in _select_iterables(statement._raw_columns)] @@ -5318,7 +5296,7 @@ class Select( """ - return self._exported_columns_iterator() + return iter(self._all_selected_columns) def is_derived_from(self, fromclause): if self in fromclause._cloned_set: @@ -5470,7 +5448,7 @@ class Select( """ return self.with_only_columns( *util.preloaded.sql_util.reduce_columns( - self._exported_columns_iterator(), + self._all_selected_columns, only_synonyms=only_synonyms, *(self._where_criteria + self._from_obj) ) @@ -5779,7 +5757,11 @@ class Select( conv = SelectState._column_naming_convention(self._label_style) return ColumnCollection( - [(conv(c), c) for c in self._exported_columns_iterator()] + [ + (conv(c), c) + for c in self._all_selected_columns + if not c._is_text_clause + ] ).as_immutable() @HasMemoized.memoized_attribute @@ -5787,10 +5769,6 @@ class Select( meth = SelectState.get_plugin_class(self).all_selected_columns return list(meth(self)) - def _exported_columns_iterator(self): - meth = SelectState.get_plugin_class(self).exported_columns_iterator - return meth(self) - def _ensure_disambiguated_names(self): if self._label_style is LABEL_STYLE_NONE: self = self.set_label_style(LABEL_STYLE_DISAMBIGUATE_ONLY) @@ -5912,7 +5890,7 @@ class Select( disambiguate_only = self._label_style is LABEL_STYLE_DISAMBIGUATE_ONLY for name, c, repeated in self._generate_columns_plus_names(False): - if not hasattr(c, "_make_proxy"): + if c._is_text_clause: continue elif tablename_plus_col: key = c._key_label @@ -6405,6 +6383,10 @@ class TextualSelect(SelectBase): (c.key, c) for c in self.column_args ).as_immutable() + @property + def _all_selected_columns(self): + return self.column_args + def _set_label_style(self, style): return self |
