summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql/elements.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/sql/elements.py')
-rw-r--r--lib/sqlalchemy/sql/elements.py70
1 files changed, 47 insertions, 23 deletions
diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py
index c1bc9edbc..287e53724 100644
--- a/lib/sqlalchemy/sql/elements.py
+++ b/lib/sqlalchemy/sql/elements.py
@@ -191,7 +191,12 @@ class ClauseElement(
__visit_name__ = "clause"
- _annotations = {}
+ _propagate_attrs = util.immutabledict()
+ """like annotations, however these propagate outwards liberally
+ as SQL constructs are built, and are set up at construction time.
+
+ """
+
supports_execution = False
_from_objects = []
bind = None
@@ -215,6 +220,16 @@ class ClauseElement(
_cache_key_traversal = None
+ def _set_propagate_attrs(self, values):
+ # usually, self._propagate_attrs is empty here. one case where it's
+ # not is a subquery against ORM select, that is then pulled as a
+ # property of an aliased class. should all be good
+
+ # assert not self._propagate_attrs
+
+ self._propagate_attrs = util.immutabledict(values)
+ return self
+
def _clone(self):
"""Create a shallow copy of this ClauseElement.
@@ -870,6 +885,7 @@ class ColumnElement(
type_=getattr(self, "type", None),
_selectable=selectable,
)
+ co._propagate_attrs = selectable._propagate_attrs
co._proxies = [self]
if selectable._is_clone_of is not None:
co._is_clone_of = selectable._is_clone_of.columns.get(key)
@@ -1495,6 +1511,8 @@ class TextClause(
_render_label_in_columns_clause = False
+ _hide_froms = ()
+
def __and__(self, other):
# support use in select.where(), query.filter()
return and_(self, other)
@@ -1509,10 +1527,6 @@ class TextClause(
_allow_label_resolve = False
- @property
- def _hide_froms(self):
- return []
-
def __init__(self, text, bind=None):
self._bind = bind
self._bindparams = {}
@@ -2093,14 +2107,16 @@ class ClauseList(
)
if self.group_contents:
self.clauses = [
- coercions.expect(text_converter_role, clause).self_group(
- against=self.operator
- )
+ coercions.expect(
+ text_converter_role, clause, apply_propagate_attrs=self
+ ).self_group(against=self.operator)
for clause in clauses
]
else:
self.clauses = [
- coercions.expect(text_converter_role, clause)
+ coercions.expect(
+ text_converter_role, clause, apply_propagate_attrs=self
+ )
for clause in clauses
]
self._is_implicitly_boolean = operators.is_boolean(self.operator)
@@ -2641,7 +2657,9 @@ class Case(ColumnElement):
whenlist = [
(
coercions.expect(
- roles.ExpressionElementRole, c
+ roles.ExpressionElementRole,
+ c,
+ apply_propagate_attrs=self,
).self_group(),
coercions.expect(roles.ExpressionElementRole, r),
)
@@ -2650,7 +2668,9 @@ class Case(ColumnElement):
else:
whenlist = [
(
- coercions.expect(roles.ColumnArgumentRole, c).self_group(),
+ coercions.expect(
+ roles.ColumnArgumentRole, c, apply_propagate_attrs=self
+ ).self_group(),
coercions.expect(roles.ExpressionElementRole, r),
)
for (c, r) in whens
@@ -2805,7 +2825,10 @@ class Cast(WrapsColumnExpression, ColumnElement):
"""
self.type = type_api.to_instance(type_)
self.clause = coercions.expect(
- roles.ExpressionElementRole, expression, type_=self.type
+ roles.ExpressionElementRole,
+ expression,
+ type_=self.type,
+ apply_propagate_attrs=self,
)
self.typeclause = TypeClause(self.type)
@@ -2906,7 +2929,10 @@ class TypeCoerce(WrapsColumnExpression, ColumnElement):
"""
self.type = type_api.to_instance(type_)
self.clause = coercions.expect(
- roles.ExpressionElementRole, expression, type_=self.type
+ roles.ExpressionElementRole,
+ expression,
+ type_=self.type,
+ apply_propagate_attrs=self,
)
@property
@@ -3031,6 +3057,7 @@ class UnaryExpression(ColumnElement):
):
self.operator = operator
self.modifier = modifier
+ self._propagate_attrs = element._propagate_attrs
self.element = element.self_group(
against=self.operator or self.modifier
)
@@ -3474,6 +3501,7 @@ class BinaryExpression(ColumnElement):
if isinstance(operator, util.string_types):
operator = operators.custom_op(operator)
self._orig = (left.__hash__(), right.__hash__())
+ self._propagate_attrs = left._propagate_attrs or right._propagate_attrs
self.left = left.self_group(against=operator)
self.right = right.self_group(against=operator)
self.operator = operator
@@ -4159,6 +4187,7 @@ class Label(roles.LabeledColumnExprRole, ColumnElement):
name=name if name else self.name,
disallow_is_literal=True,
)
+ e._propagate_attrs = selectable._propagate_attrs
e._proxies.append(self)
if self._type is not None:
e.type = self._type
@@ -4340,16 +4369,10 @@ class ColumnClause(
return other.proxy_set.intersection(self.proxy_set)
def get_children(self, column_tables=False, **kw):
- if column_tables and self.table is not None:
- # TODO: this is only used by ORM query deep_entity_zero.
- # this is being removed in a later release so remove
- # column_tables also at that time.
- return [self.table]
- else:
- # override base get_children() to not return the Table
- # or selectable that is parent to this column. Traversals
- # expect the columns of tables and subqueries to be leaf nodes.
- return []
+ # override base get_children() to not return the Table
+ # or selectable that is parent to this column. Traversals
+ # expect the columns of tables and subqueries to be leaf nodes.
+ return []
@HasMemoized.memoized_attribute
def _from_objects(self):
@@ -4474,6 +4497,7 @@ class ColumnClause(
_selectable=selectable,
is_literal=is_literal,
)
+ c._propagate_attrs = selectable._propagate_attrs
if name is None:
c.key = self.key
c._proxies = [self]