summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2021-06-04 19:45:45 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2021-06-04 19:45:45 -0400
commitb2cd9aca54d1625dde31abebf7f11149c9b7c636 (patch)
tree1ee7b31a3c549d8410875113932c1f1cc85cd94c
parent3874d2576dc536b54af9f5525aff1fe59f4f00d3 (diff)
downloadsqlalchemy-b2cd9aca54d1625dde31abebf7f11149c9b7c636.tar.gz
don't enable "fast insert executemany" for ON CONFLICT etc
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
-rw-r--r--doc/build/changelog/unreleased_14/6581.rst11
-rw-r--r--lib/sqlalchemy/sql/compiler.py9
-rw-r--r--test/dialect/postgresql/test_on_conflict.py29
3 files changed, 48 insertions, 1 deletions
diff --git a/doc/build/changelog/unreleased_14/6581.rst b/doc/build/changelog/unreleased_14/6581.rst
new file mode 100644
index 000000000..643892b6a
--- /dev/null
+++ b/doc/build/changelog/unreleased_14/6581.rst
@@ -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.
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
index 734e65492..220a0fa99 100644
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -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:
diff --git a/test/dialect/postgresql/test_on_conflict.py b/test/dialect/postgresql/test_on_conflict.py
index dcf112de6..508f691c5 100644
--- a/test/dialect/postgresql/test_on_conflict.py
+++ b/test/dialect/postgresql/test_on_conflict.py
@@ -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)