]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Fixed issue where the columns from a SELECT embedded in an
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 11 Nov 2014 17:34:00 +0000 (12:34 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 11 Nov 2014 17:34:00 +0000 (12:34 -0500)
INSERT, either through the values clause or as a "from select",
would pollute the column types used in the result set produced by
the RETURNING clause when columns from both statements shared the
same name, leading to potential errors or mis-adaptation when
retrieving the returning rows.
fixes #3248

doc/build/changelog/changelog_09.rst
lib/sqlalchemy/sql/compiler.py
test/sql/test_compiler.py
test/sql/test_returning.py

index 8ed2ea77616c34541c1dc59fd3c5d976f4d26167..66a7da8dab30514e0c8872b76de912e30e75a054 100644 (file)
 .. changelog::
     :version: 0.9.9
 
+    .. change::
+        :tags: bug, sql
+        :versions: 1.0.0
+        :tickets: 3248
+
+        Fixed issue where the columns from a SELECT embedded in an
+        INSERT, either through the values clause or as a "from select",
+        would pollute the column types used in the result set produced by
+        the RETURNING clause when columns from both statements shared the
+        same name, leading to potential errors or mis-adaptation when
+        retrieving the returning rows.
+
     .. change::
         :tags: bug, orm, sqlite
         :versions: 1.0.0
index 5fa78ad0f4a50e9674f0273d0c8605372aac6857..8f3ede25f40aa5feb6d6b43f35a17b9e3780bb85 100644 (file)
@@ -1729,6 +1729,12 @@ class SQLCompiler(Compiled):
         )
 
     def visit_insert(self, insert_stmt, **kw):
+        self.stack.append(
+            {'correlate_froms': set(),
+             "iswrapper": False,
+             "asfrom_froms": set(),
+             "selectable": insert_stmt})
+
         self.isinsert = True
         crud_params = crud._get_crud_params(self, insert_stmt, **kw)
 
@@ -1812,6 +1818,8 @@ class SQLCompiler(Compiled):
         if self.returning and not self.returning_precedes_values:
             text += " " + returning_clause
 
+        self.stack.pop(-1)
+
         return text
 
     def update_limit_clause(self, update_stmt):
index 5d1afe6162d0fc0f947e60d2c93355cd3838d0ac..9e99a947b0ddc3b6bd3ade8e8e8ef6123c42c4ea 100644 (file)
@@ -3437,3 +3437,32 @@ class ResultMapTest(fixtures.TestBase):
         is_(
             comp.result_map['t1_a'][1][2], t1.c.a
         )
+
+    def test_insert_with_select_values(self):
+        astring = Column('a', String)
+        aint = Column('a', Integer)
+        m = MetaData()
+        Table('t1', m, astring)
+        t2 = Table('t2', m, aint)
+
+        stmt = t2.insert().values(a=select([astring])).returning(aint)
+        comp = stmt.compile(dialect=postgresql.dialect())
+        eq_(
+            comp.result_map,
+            {'a': ('a', (aint, 'a', 'a'), aint.type)}
+        )
+
+    def test_insert_from_select(self):
+        astring = Column('a', String)
+        aint = Column('a', Integer)
+        m = MetaData()
+        Table('t1', m, astring)
+        t2 = Table('t2', m, aint)
+
+        stmt = t2.insert().from_select(['a'], select([astring])).\
+            returning(aint)
+        comp = stmt.compile(dialect=postgresql.dialect())
+        eq_(
+            comp.result_map,
+            {'a': ('a', (aint, 'a', 'a'), aint.type)}
+        )
index 79a0b38a5dae80fb960ba6158649209bf6db733c..cd9f632b9be198dc794f3ade03634273d5e96bb4 100644 (file)
@@ -160,6 +160,39 @@ class ReturningTest(fixtures.TestBase, AssertsExecutionResults):
         eq_(result2.fetchall(), [(2, False), ])
 
 
+class CompositeStatementTest(fixtures.TestBase):
+    __requires__ = 'returning',
+    __backend__ = True
+
+    @testing.provide_metadata
+    def test_select_doesnt_pollute_result(self):
+        class MyType(TypeDecorator):
+            impl = Integer
+
+            def process_result_value(self, value, dialect):
+                raise Exception("I have not been selected")
+
+        t1 = Table(
+            't1', self.metadata,
+            Column('x', MyType())
+        )
+
+        t2 = Table(
+            't2', self.metadata,
+            Column('x', Integer)
+        )
+
+        self.metadata.create_all(testing.db)
+        with testing.db.connect() as conn:
+            conn.execute(t1.insert().values(x=5))
+
+            stmt = t2.insert().values(
+                x=select([t1.c.x]).as_scalar()).returning(t2.c.x)
+
+            result = conn.execute(stmt)
+            eq_(result.scalar(), 5)
+
+
 class SequenceReturningTest(fixtures.TestBase):
     __requires__ = 'returning', 'sequences'
     __backend__ = True