Don't assume key when matching cloned columns in _make_proxy

Fixed issue where internal cloning of SELECT constructs could lead to a key
error if the copy of the SELECT changed its state such that its list of
columns changed.  This was observed to be occurring in some ORM scenarios
which may be unique to 1.3 and above, so is partially a regression fix.

For 1.4, the _is_clone_of key will be removed entirely as it seems to
have no purpose.  This commit is the initial backport to 1.3 which
includes tests.

Fixes: #4780
Change-Id: I0c64962a2eba3763bea3107fc7c7d7aed8244430
This commit is contained in:
Mike Bayer
2019-08-01 11:45:34 -04:00
parent 71ca345a12
commit 896d47f318
3 changed files with 33 additions and 1 deletions
+10
View File
@@ -0,0 +1,10 @@
.. change::
:tags: bug, sql
:tickets: 4780
Fixed issue where internal cloning of SELECT constructs could lead to a key
error if the copy of the SELECT changed its state such that its list of
columns changed. This was observed to be occurring in some ORM scenarios
which may be unique to 1.3 and above, so is partially a regression fix.
+1 -1
View File
@@ -1608,7 +1608,7 @@ class Column(DialectKWArgs, SchemaItem, ColumnClause):
c.table = selectable
if selectable._is_clone_of is not None:
c._is_clone_of = selectable._is_clone_of.columns[c.key]
c._is_clone_of = selectable._is_clone_of.columns.get(c.key)
if self.primary_key:
selectable.primary_key.add(c)
if fk:
+22
View File
@@ -346,6 +346,28 @@ class SelectableTest(
cloned.append_column(func.foo())
eq_(list(cloned.selected_columns.keys()), ["a", "b", "foo()"])
def test_clone_col_list_changes_then_proxy(self):
t = table("t", column("q"), column("p"))
stmt = select([t.c.q]).subquery()
def add_column(stmt):
stmt.append_column(t.c.p)
stmt2 = visitors.cloned_traverse(stmt, {}, {"select": add_column})
eq_(list(stmt.c.keys()), ["q"])
eq_(list(stmt2.c.keys()), ["q", "p"])
def test_clone_col_list_changes_then_schema_proxy(self):
t = Table("t", MetaData(), Column("q", Integer), Column("p", Integer))
stmt = select([t.c.q]).subquery()
def add_column(stmt):
stmt.append_column(t.c.p)
stmt2 = visitors.cloned_traverse(stmt, {}, {"select": add_column})
eq_(list(stmt.c.keys()), ["q"])
eq_(list(stmt2.c.keys()), ["q", "p"])
def test_append_column_after_visitor_replace(self):
# test for a supported idiom that matches the deprecated / removed
# replace_selectable method