diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2013-12-19 16:02:14 -0500 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2013-12-19 16:02:14 -0500 |
| commit | 5f76f29c15b7a23cfe29c5fbd22ad02452b6a2c0 (patch) | |
| tree | 022779ebba8df3b7d75d4c46fa7730f0f2d1fda5 | |
| parent | 47eb5682d1b8885c052e4bc50004af45b5f19174 (diff) | |
| download | sqlalchemy-5f76f29c15b7a23cfe29c5fbd22ad02452b6a2c0.tar.gz | |
- Fixed bug with :meth:`.Insert.from_select` method where the order
of the given names would not be taken into account when generating
the INSERT statement, thus producing a mismatch versus the column
names in the given SELECT statement. Also noted that
:meth:`.Insert.from_select` implies that Python-side insert defaults
cannot be used, since the statement has no VALUES clause. [ticket:2895]
| -rw-r--r-- | doc/build/changelog/changelog_08.rst | 12 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/compiler.py | 15 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/dml.py | 10 | ||||
| -rw-r--r-- | test/sql/test_insert.py | 29 |
4 files changed, 61 insertions, 5 deletions
diff --git a/doc/build/changelog/changelog_08.rst b/doc/build/changelog/changelog_08.rst index 3460e5c68..751cefdae 100644 --- a/doc/build/changelog/changelog_08.rst +++ b/doc/build/changelog/changelog_08.rst @@ -12,6 +12,18 @@ :version: 0.8.5 .. change:: + :tags: bug, sql + :versions: 0.9.0b2 + :tickets: 2895 + + Fixed bug with :meth:`.Insert.from_select` method where the order + of the given names would not be taken into account when generating + the INSERT statement, thus producing a mismatch versus the column + names in the given SELECT statement. Also noted that + :meth:`.Insert.from_select` implies that Python-side insert defaults + cannot be used, since the statement has no VALUES clause. + + .. change:: :tags: enhancement, sql :versions: 0.9.0b2 diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index 1d38c9ad3..bd886bd40 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -1965,10 +1965,17 @@ class SQLCompiler(Compiled): elif c.server_onupdate is not None: self.postfetch.append(c) - # iterating through columns at the top to maintain ordering. - # otherwise we might iterate through individual sets of - # "defaults", "primary key cols", etc. - for c in stmt.table.columns: + if self.isinsert and stmt.select_names: + # for an insert from select, we can only use names that + # are given, so only select for those names. + cols = (stmt.table.c[elements._column_as_key(name)] + for name in stmt.select_names) + else: + # iterate through all table columns to maintain + # ordering, even for those cols that aren't included + cols = stmt.table.columns + + for c in cols: if c.key in parameters and c.key not in check_columns: value = parameters.pop(c.key) if elements._is_literal(value): diff --git a/lib/sqlalchemy/sql/dml.py b/lib/sqlalchemy/sql/dml.py index 3501417a4..83f4365d7 100644 --- a/lib/sqlalchemy/sql/dml.py +++ b/lib/sqlalchemy/sql/dml.py @@ -426,7 +426,7 @@ class Insert(ValuesBase): """ ValuesBase.__init__(self, table, values, prefixes) self._bind = bind - self.select = None + self.select = self.select_names = None self.inline = inline self._returning = returning self.kwargs = kwargs @@ -470,6 +470,13 @@ class Insert(ValuesBase): sel = select([table1.c.a, table1.c.b]).where(table1.c.c > 5) ins = table2.insert(inline=True).from_select(['a', 'b'], sel) + .. note:: + + A SELECT..INSERT construct in SQL has no VALUES clause. Therefore + :class:`.Column` objects which utilize Python-side defaults + (e.g. as described at :ref:`metadata_defaults_toplevel`) + will **not** take effect when using :meth:`.Insert.from_select`. + .. versionadded:: 0.8.3 """ @@ -480,6 +487,7 @@ class Insert(ValuesBase): self.parameters, self._has_multi_parameters = \ self._process_colparams(dict((n, Null()) for n in names)) + self.select_names = names self.select = _interpret_as_select(select) def _copy_internals(self, clone=_clone, **kw): diff --git a/test/sql/test_insert.py b/test/sql/test_insert.py index e1171532d..5c3b9b6c9 100644 --- a/test/sql/test_insert.py +++ b/test/sql/test_insert.py @@ -133,6 +133,35 @@ class InsertTest(_InsertTestBase, fixtures.TablesTest, AssertsCompiledSQL): checkparams={"name_1": "foo"} ) + def test_insert_from_select_select_alt_ordering(self): + table1 = self.tables.mytable + sel = select([table1.c.name, table1.c.myid]).where(table1.c.name == 'foo') + ins = self.tables.myothertable.insert().\ + from_select(("othername", "otherid"), sel) + self.assert_compile( + ins, + "INSERT INTO myothertable (othername, otherid) " + "SELECT mytable.name, mytable.myid FROM mytable " + "WHERE mytable.name = :name_1", + checkparams={"name_1": "foo"} + ) + + def test_insert_from_select_select_no_defaults(self): + metadata = MetaData() + table = Table('sometable', metadata, + Column('id', Integer, primary_key=True), + Column('foo', Integer, default=func.foobar())) + table1 = self.tables.mytable + sel = select([table1.c.myid]).where(table1.c.name == 'foo') + ins = table.insert().\ + from_select(["id"], sel) + self.assert_compile( + ins, + "INSERT INTO sometable (id) SELECT mytable.myid " + "FROM mytable WHERE mytable.name = :name_1", + checkparams={"name_1": "foo"} + ) + def test_insert_mix_select_values_exception(self): table1 = self.tables.mytable sel = select([table1.c.myid, table1.c.name]).where(table1.c.name == 'foo') |
