From: Mike Bayer Date: Thu, 1 Aug 2019 15:45:34 +0000 (-0400) Subject: Don't assume key when matching cloned columns in _make_proxy X-Git-Tag: rel_1_4_0b1~779 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=896d47f318c5c27620fd6da805f98811941b88c5;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git 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 --- diff --git a/doc/build/changelog/unreleased_13/4780.rst b/doc/build/changelog/unreleased_13/4780.rst new file mode 100644 index 0000000000..55886f3c56 --- /dev/null +++ b/doc/build/changelog/unreleased_13/4780.rst @@ -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. + + diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py index c63a3160f5..beebc5b1fc 100644 --- a/lib/sqlalchemy/sql/schema.py +++ b/lib/sqlalchemy/sql/schema.py @@ -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: diff --git a/test/sql/test_selectable.py b/test/sql/test_selectable.py index dfd35908bd..189436192d 100644 --- a/test/sql/test_selectable.py +++ b/test/sql/test_selectable.py @@ -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