From e57bf796169282f69187f50665f5ea233c2c9ab7 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Wed, 9 Dec 2015 18:15:25 -0500 Subject: [PATCH] - Fixed issue within the :meth:`.Insert.from_select` construct whereby the :class:`.Select` construct would have its ``._raw_columns`` collection mutated in-place when compiling the :class:`.Insert` construct, when the target :class:`.Table` has Python-side defaults. The :class:`.Select` construct would compile standalone with the erroneous column present subsequent to compilation of the :class:`.Insert`, and the the :class:`.Insert` statement itself would fail on a second compile attempt due to duplicate bound parameters. fixes #3603 --- doc/build/changelog/changelog_10.rst | 14 ++++++++++++++ lib/sqlalchemy/sql/crud.py | 5 +++-- test/sql/test_insert.py | 26 ++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst index 21a8f3e6fd..a07f2db213 100644 --- a/doc/build/changelog/changelog_10.rst +++ b/doc/build/changelog/changelog_10.rst @@ -18,6 +18,20 @@ .. changelog:: :version: 1.0.10 + .. change:: + :tags: bug, sql + :tickets: 3603 + :versions: 1.1.0b1 + + Fixed issue within the :meth:`.Insert.from_select` construct whereby + the :class:`.Select` construct would have its ``._raw_columns`` + collection mutated in-place when compiling the :class:`.Insert` + construct, when the target :class:`.Table` has Python-side defaults. + The :class:`.Select` construct would compile standalone with the + erroneous column present subsequent to compilation of the + :class:`.Insert`, and the the :class:`.Insert` statement itself would + fail on a second compile attempt due to duplicate bound parameters. + .. change:: :tags: bug, mysql :tickets: 3602 diff --git a/lib/sqlalchemy/sql/crud.py b/lib/sqlalchemy/sql/crud.py index 67a8f09de6..18b96018d4 100644 --- a/lib/sqlalchemy/sql/crud.py +++ b/lib/sqlalchemy/sql/crud.py @@ -196,8 +196,9 @@ def _scan_insert_from_select_cols( if add_select_cols: values.extend(add_select_cols) compiler._insert_from_select = compiler._insert_from_select._generate() - compiler._insert_from_select._raw_columns += tuple( - expr for col, expr in add_select_cols) + compiler._insert_from_select._raw_columns = \ + tuple(compiler._insert_from_select._raw_columns) + tuple( + expr for col, expr in add_select_cols) def _scan_cols( diff --git a/test/sql/test_insert.py b/test/sql/test_insert.py index bdaf4f38c6..ea4de032c8 100644 --- a/test/sql/test_insert.py +++ b/test/sql/test_insert.py @@ -319,6 +319,32 @@ class InsertTest(_InsertTestBase, fixtures.TablesTest, AssertsCompiledSQL): checkparams={"name_1": "foo", "foo": None} ) + def test_insert_from_select_dont_mutate_raw_columns(self): + # test [ticket:3603] + from sqlalchemy import table + table_ = table( + 'mytable', + Column('foo', String), + Column('bar', String, default='baz'), + ) + + stmt = select([table_.c.foo]) + insert = table_.insert().from_select(['foo'], stmt) + + self.assert_compile(stmt, "SELECT mytable.foo FROM mytable") + self.assert_compile( + insert, + "INSERT INTO mytable (foo, bar) " + "SELECT mytable.foo, :bar AS anon_1 FROM mytable" + ) + self.assert_compile(stmt, "SELECT mytable.foo FROM mytable") + self.assert_compile( + insert, + "INSERT INTO mytable (foo, bar) " + "SELECT mytable.foo, :bar AS anon_1 FROM mytable" + ) + + def test_insert_mix_select_values_exception(self): table1 = self.tables.mytable sel = select([table1.c.myid, table1.c.name]).where( -- 2.47.2