]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Fixed bug with :meth:`.Insert.from_select` method where the order
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 19 Dec 2013 21:02:14 +0000 (16:02 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 19 Dec 2013 21:02:14 +0000 (16:02 -0500)
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]

doc/build/changelog/changelog_08.rst
lib/sqlalchemy/sql/compiler.py
lib/sqlalchemy/sql/dml.py
test/sql/test_insert.py

index 3460e5c6851c99e41483d1251c1e8b6e2bf00616..751cefdae8e82aa3078aeb2f95770915c90a1d76 100644 (file)
 .. changelog::
     :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
index 1d38c9ad3c240219d63a6d114ae56b38eaa3a37e..bd886bd40feee98ee2382116deded0d138562b12 100644 (file)
@@ -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):
index 3501417a4ce15de540f896c9556f5c648f350e62..83f4365d7e956b21213ad09e8833affabca5a90f 100644 (file)
@@ -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):
index e1171532d9c8e4d4c13ebe70a5dd339bacc7d2de..5c3b9b6c98641029a63aaf9c544334bbba9753df 100644 (file)
@@ -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')