.. 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
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):
**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
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
"""
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):
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')