]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
don't enable "fast insert executemany" for ON CONFLICT etc
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 4 Jun 2021 23:45:45 +0000 (19:45 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 4 Jun 2021 23:45:45 +0000 (19:45 -0400)
Fixed issue where using the PostgreSQL "INSERT..ON CONFLICT" structure
would fail to work with the psycopg2 driver if it were used in an
"executemany" context along with bound parameters in the "SET" clause, due
to the implicit use of the psycopg2 fast execution helpers which are not
appropriate for this style of INSERT statement. Additional checks to
exclude this kind of statement from that particular extension have been
added.

Fixes: #6581
Change-Id: I3d6169e7e188dc087d1d1bfba9a42162db183265

doc/build/changelog/unreleased_14/6581.rst [new file with mode: 0644]
lib/sqlalchemy/sql/compiler.py
test/dialect/postgresql/test_on_conflict.py

diff --git a/doc/build/changelog/unreleased_14/6581.rst b/doc/build/changelog/unreleased_14/6581.rst
new file mode 100644 (file)
index 0000000..643892b
--- /dev/null
@@ -0,0 +1,11 @@
+.. change::
+    :tags: bug, postgresql
+    :tickets: 6581
+
+    Fixed issue where using the PostgreSQL "INSERT..ON CONFLICT" structure
+    would fail to work with the psycopg2 driver if it were used in an
+    "executemany" context along with bound parameters in the "SET" clause, due
+    to the implicit use of the psycopg2 fast execution helpers which are not
+    appropriate for this style of INSERT statement. Additional checks to
+    exclude this kind of statement from that particular extension have been
+    added.
index 734e6549219e5fc207aaed39cff0f6a9c1614d14..220a0fa9985bb2ddd6ee9a2da34a47c1fba88385 100644 (file)
@@ -3588,7 +3588,14 @@ class SQLCompiler(Compiled):
                 [value for c, expr, value in crud_params]
             )
             text += " VALUES (%s)" % insert_single_values_expr
-            if toplevel:
+            if toplevel and insert_stmt._post_values_clause is None:
+                # don't assign insert_single_values_expr if _post_values_clause
+                # is present.  what this means concretely is that the
+                # "fast insert executemany helper" won't be used, in other
+                # words we won't convert "executemany()" of many parameter
+                # sets into a single INSERT with many elements in VALUES.
+                # We can't apply that optimization safely if for example the
+                # statement includes a clause like "ON CONFLICT DO UPDATE"
                 self.insert_single_values_expr = insert_single_values_expr
 
         if insert_stmt._post_values_clause is not None:
index dcf112de6623d5c14f3e10dbee541cceee0a529e..508f691c51413308bc5565bca1084a1df9b8bf2f 100644 (file)
@@ -206,6 +206,35 @@ class OnConflictTest(fixtures.TablesTest):
             [(1, "name1")],
         )
 
+    def test_on_conflict_do_update_set_executemany(self, connection):
+        """test #6581"""
+
+        users = self.tables.users
+
+        connection.execute(
+            users.insert(),
+            [dict(id=1, name="name1"), dict(id=2, name="name2")],
+        )
+
+        i = insert(users)
+        i = i.on_conflict_do_update(
+            index_elements=[users.c.id],
+            set_={"id": i.excluded.id, "name": i.excluded.name + ".5"},
+        )
+        connection.execute(
+            i,
+            [
+                dict(id=1, name="name1"),
+                dict(id=2, name="name2"),
+                dict(id=3, name="name3"),
+            ],
+        )
+
+        eq_(
+            connection.execute(users.select().order_by(users.c.id)).fetchall(),
+            [(1, "name1.5"), (2, "name2.5"), (3, "name3")],
+        )
+
     def test_on_conflict_do_update_schema(self, connection):
         users = self.tables.get("%s.users_schema" % config.test_schema)