--- /dev/null
+.. change::
+ :tags: bug, sql
+ :tickets: 4747
+
+ Fixed an unlikely issue where the "corresponding column" routine for unions
+ and other :class:`.CompoundSelect` objects could return the wrong column in
+ some overlapping column situtations, thus potentially impacting some ORM
+ operations when set operations are in use, if the underlying
+ :func:`.select` constructs were used previously in other similar kinds of
+ routines, due to a cached value not being cleared.
__visit_name__ = "column_element"
primary_key = False
foreign_keys = []
+ _proxies = ()
_label = None
"""The named label that can be used to target
@util.memoized_property
def base_columns(self):
- return util.column_set(
- c for c in self.proxy_set if not hasattr(c, "_proxies")
- )
+ return util.column_set(c for c in self.proxy_set if not c._proxies)
@util.memoized_property
def proxy_set(self):
s = util.column_set([self])
- if hasattr(self, "_proxies"):
- for c in self._proxies:
- s.update(c.proxy_set)
+ for c in self._proxies:
+ s.update(c.proxy_set)
return s
def shares_lineage(self, othercolumn):
def __init__(self, element, values):
Annotated.__init__(self, element, values)
ColumnElement.comparator._reset(self)
+ if self._proxies:
+ ColumnElement.proxy_set._reset(self)
for attr in ("name", "key", "table"):
if self.__dict__.get(attr, False) is None:
self.__dict__.pop(attr)
parenttable = self.parent.table
- # assertion, can be commented out.
+ # assertion
# basically Column._make_proxy() sends the actual
# target Column to the ForeignKey object, so the
# string resolution here is never called.
# to how low in the list of select()s the column occurs, so
# that the corresponding_column() operation can resolve
# conflicts
-
proxy._proxies = [
c._annotate({"weight": i + 1}) for (i, c) in enumerate(cols)
]
assert u1.corresponding_column(table1.c.colx) is u1.c.col2
assert u1.corresponding_column(table1.c.col3) is u1.c.col1
+ def test_proxy_set_pollution(self):
+ s1 = select([table1.c.col1, table1.c.col2])
+ s2 = select([table1.c.col2, table1.c.col1])
+
+ for c in s1.c:
+ c.proxy_set
+ for c in s2.c:
+ c.proxy_set
+
+ u1 = union(s1, s2)
+ assert u1.corresponding_column(table1.c.col2) is u1.c.col2
+
def test_singular_union(self):
u = union(
select([table1.c.col1, table1.c.col2, table1.c.col3]),
assert x_p.compare(x_p_a)
assert not x_p_a.compare(x_a)
+ def test_proxy_set_iteration_includes_annotated(self):
+ from sqlalchemy.schema import Column
+
+ c1 = Column("foo", Integer)
+
+ stmt = select([c1]).alias()
+ proxy = stmt.c.foo
+
+ proxy.proxy_set
+
+ # create an annotated of the column
+ p2 = proxy._annotate({"weight": 10})
+
+ # now see if our annotated version is in that column's
+ # proxy_set, as corresponding_column iterates through proxy_set
+ # in this way
+ d = {}
+ for col in p2.proxy_set:
+ d.update(col._annotations)
+ eq_(d, {"weight": 10})
+
def test_late_name_add(self):
from sqlalchemy.schema import Column