diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2021-04-16 11:06:56 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2021-04-17 00:40:52 -0400 |
| commit | 9f758d7fe328107cd3d047b8c999dce228a570d1 (patch) | |
| tree | 9867a6dbdf81d5111907e4a49c86d6e06488f0c9 /lib/sqlalchemy/sql/elements.py | |
| parent | b3d764fa768756cd7aef73253ccceac456c76c81 (diff) | |
| download | sqlalchemy-9f758d7fe328107cd3d047b8c999dce228a570d1.tar.gz | |
Don't stringify unnamed column elements when proxying
Repaired and solidified issues regarding custom functions and other
arbitrary expression constructs which within SQLAlchemy's column labeling
mechanics would seek to use ``str(obj)`` to get a string representation to
use as an anonymous column name in the ``.c`` collection of a subquery.
This is a very legacy behavior that performs poorly and leads to lots of
issues, so has been revised to no longer perform any compilation by
establishing specific methods on :class:`.FunctionElement` to handle this
case, as SQL functions are the only use case that it came into play. An
effect of this behavior is that an unlabeled column expression with no
derivable name will be given an arbitrary label starting with the prefix
``"_no_label"`` in the ``.c`` collection of a subquery; these were
previously being represented either as the generic stringification of that
expression, or as an internal symbol.
This change seeks to make the concept of "anon name" more private
and renames anon_label and anon_key_label to _anon_name_label
and _anon_key_label. There's no end-user utility to these accessors
and we need to be able to reorganize these as well.
Fixes: #6256
Change-Id: Ie63c86b20ca45873affea78500388da94cf8bf94
Diffstat (limited to 'lib/sqlalchemy/sql/elements.py')
| -rw-r--r-- | lib/sqlalchemy/sql/elements.py | 64 |
1 files changed, 43 insertions, 21 deletions
diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py index e97ed252e..698c836b0 100644 --- a/lib/sqlalchemy/sql/elements.py +++ b/lib/sqlalchemy/sql/elements.py @@ -903,23 +903,20 @@ class ColumnElement( elif self.key: return self.key else: - try: - return str(self) - except exc.UnsupportedCompilationError: - return self.anon_label + return getattr(self, "name", "_no_label") def _make_proxy( - self, selectable, name=None, name_is_truncatable=False, **kw + self, selectable, name=None, key=None, name_is_truncatable=False, **kw ): """Create a new :class:`_expression.ColumnElement` representing this - :class:`_expression.ColumnElement` - as it appears in the select list of a - descending selectable. + :class:`_expression.ColumnElement` as it appears in the select list of + a descending selectable. """ if name is None: - name = self.anon_label - key = self._proxy_key + name = self._anon_name_label + if key is None: + key = self._proxy_key else: key = name @@ -980,7 +977,7 @@ class ColumnElement( return _anonymous_label.safe_construct(hash(self), seed or "anon") @util.memoized_property - def anon_label(self): + def _anon_name_label(self): """Provides a constant 'anonymous label' for this ColumnElement. This is a label() expression which will be named at compile time. @@ -992,12 +989,16 @@ class ColumnElement( for expressions that are known to be 'unnamed' like binary expressions and function calls. + .. versionchanged:: 1.4.9 - this attribute was not intended to be + public and is renamed to _anon_name_label. anon_name exists + for backwards compat + """ name = getattr(self, "name", None) return self._anon_label(name) @util.memoized_property - def anon_key_label(self): + def _anon_key_label(self): """Provides a constant 'anonymous key label' for this ColumnElement. Compare to ``anon_label``, except that the "key" of the column, @@ -1006,9 +1007,30 @@ class ColumnElement( This is used when a deduplicating key is placed into the columns collection of a selectable. + .. versionchanged:: 1.4.9 - this attribute was not intended to be + public and is renamed to _anon_key_label. anon_key_label exists + for backwards compat + """ - name = getattr(self, "key", None) or getattr(self, "name", None) - return self._anon_label(name) + return self._anon_label(self._proxy_key) + + @property + @util.deprecated( + "1.4", + "The :attr:`_expression.ColumnElement.anon_label` attribute is now " + "private, and the public accessor is deprecated.", + ) + def anon_label(self): + return self._anon_name_label + + @property + @util.deprecated( + "1.4", + "The :attr:`_expression.ColumnElement.anon_key_label` attribute is " + "now private, and the public accessor is deprecated.", + ) + def anon_key_label(self): + return self._anon_key_label @util.memoized_property def _dedupe_anon_label(self): @@ -1056,14 +1078,14 @@ class WrapsColumnExpression(object): return None @property - def anon_label(self): + def _anon_name_label(self): wce = self.wrapped_column_expression if hasattr(wce, "name"): return wce.name - elif hasattr(wce, "anon_label"): - return wce.anon_label + elif hasattr(wce, "_anon_name_label"): + return wce._anon_name_label else: - return super(WrapsColumnExpression, self).anon_label + return super(WrapsColumnExpression, self)._anon_name_label class BindParameter(roles.InElementRole, ColumnElement): @@ -3788,7 +3810,7 @@ class Grouping(GroupedElement, ColumnElement): @property def _label(self): - return getattr(self.element, "_label", None) or self.anon_label + return getattr(self.element, "_label", None) or self._anon_name_label @property def _proxies(self): @@ -4962,8 +4984,8 @@ class AnnotatedColumnElement(Annotated): return self._Annotated__element.info @util.memoized_property - def anon_label(self): - return self._Annotated__element.anon_label + def _anon_name_label(self): + return self._Annotated__element._anon_name_label class _truncated_label(quoted_name): |
