]> 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:05:15 +0000 (16:05 -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/expression.py
test/sql/test_insert.py

index 4141585e0728f7f074ba72f0b08139c02cab22cf..5b6a99f3eb4eb01b0c30a63c1e3cf577b0603ae8 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 ad92396eef9bcb7ad85bd2047a6be95de478561b..608023f5172c89b7c90e1a6e33265ea63c2c54b9 100644 (file)
@@ -1671,10 +1671,17 @@ class SQLCompiler(engine.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[sql._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 sql._is_literal(value):
index aa655f34fe72edbd07e9dd2f0c26031a219fb011..21c0c73afd98c0d0b8f81de03d2b0a8bac5f163b 100644 (file)
@@ -6357,7 +6357,7 @@ class Insert(ValuesBase):
                 **kwargs):
         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
@@ -6400,6 +6400,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
 
         """
@@ -6410,6 +6417,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')