summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2021-06-22 22:05:35 +0000
committerGerrit Code Review <gerrit@ci3.zzzcomputing.com>2021-06-22 22:05:35 +0000
commit7110a5fa8644b29d35c4c81a1e8f7ebddebc40dc (patch)
tree45ebf946b3414e45e982cbdf90e9d787de86cdf6 /lib/sqlalchemy
parenta84881e1b7505c3ffb9b9f0ac2b227cbd650aac2 (diff)
parent2cf8c5868cb83185001755d86aa0f79e0318b53f (diff)
downloadsqlalchemy-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.py4
-rw-r--r--lib/sqlalchemy/orm/context.py23
-rw-r--r--lib/sqlalchemy/orm/mapper.py1
-rw-r--r--lib/sqlalchemy/orm/properties.py6
-rw-r--r--lib/sqlalchemy/orm/strategies.py4
-rw-r--r--lib/sqlalchemy/sql/compiler.py15
-rw-r--r--lib/sqlalchemy/sql/dml.py13
-rw-r--r--lib/sqlalchemy/sql/selectable.py62
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