]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Fix and test sequences w/ executemany in pre-exec scenarios
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 31 Aug 2021 17:34:43 +0000 (13:34 -0400)
committermike bayer <mike_mp@zzzcomputing.com>
Thu, 2 Sep 2021 14:01:56 +0000 (14:01 +0000)
Fixed issue where an engine that had ``implicit_returning`` set to False
would fail to function when PostgreSQL's "fast insertmany" feature were
used in conjunction with a ``Sequence``, as well as if any kind of
"executemany" with "return_defaults()" were used in conjunction with a
``Sequence``. Note that PostgreSQL "fast insertmany" uses "RETURNING" by
definition, when the SQL statement is passed to the driver; overall, the
``implicit_returning`` flag is legacy and has no real use in modern
SQLAlchemy, and will be deprecated in a separate change.

Fixes: #6963
Change-Id: Id8e3dd50a21b9124f338067b0fdb57b8f608dca8

doc/build/changelog/unreleased_14/6963.rst [new file with mode: 0644]
lib/sqlalchemy/engine/default.py
test/sql/test_sequences.py

diff --git a/doc/build/changelog/unreleased_14/6963.rst b/doc/build/changelog/unreleased_14/6963.rst
new file mode 100644 (file)
index 0000000..8b0473b
--- /dev/null
@@ -0,0 +1,12 @@
+.. change::
+    :tags: bug, engine, postgresql
+    :tickets: 6963
+
+    Fixed issue where an engine that had ``implicit_returning`` set to False
+    would fail to function when PostgreSQL's "fast insertmany" feature were
+    used in conjunction with a ``Sequence``, as well as if any kind of
+    "executemany" with "return_defaults()" were used in conjunction with a
+    ``Sequence``. Note that PostgreSQL "fast insertmany" uses "RETURNING" by
+    definition, when the SQL statement is passed to the driver; overall, the
+    ``implicit_returning`` flag is legacy and has no real use in modern
+    SQLAlchemy, and will be deprecated in a separate change.
\ No newline at end of file
index 8d6f40ff66da9269e1f2a52035173c352e59b8df..8bd8a121b30187854958f2bd75aec0d279ac4828 100644 (file)
@@ -1835,8 +1835,9 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
         # to avoid many calls of get_insert_default()/
         # get_update_default()
         for c in insert_prefetch:
-            if c.default and c.default.is_scalar:
+            if c.default and not c.default.is_sequence and c.default.is_scalar:
                 scalar_defaults[c] = c.default.arg
+
         for c in update_prefetch:
             if c.onupdate and c.onupdate.is_scalar:
                 scalar_defaults[c] = c.onupdate.arg
index d1d46afa3295192f3af534d90550516713c9a131..a0fef99be31af8f56a8525e301d0862d6beb7d6f 100644 (file)
@@ -208,6 +208,8 @@ class SequenceExecTest(fixtures.TestBase):
         ("implicit_returning",),
         ("no_implicit_returning",),
         ("explicit_returning", testing.requires.returning),
+        ("return_defaults_no_implicit_returning", testing.requires.returning),
+        ("return_defaults_implicit_returning", testing.requires.returning),
         argnames="returning",
     )
     @testing.requires.multivalues_inserts
@@ -221,7 +223,7 @@ class SequenceExecTest(fixtures.TestBase):
 
         e = engines.testing_engine(
             options={
-                "implicit_returning": returning != "no_implicit_returning"
+                "implicit_returning": "no_implicit_returning" not in returning
             }
         )
         metadata.create_all(e)
@@ -232,10 +234,79 @@ class SequenceExecTest(fixtures.TestBase):
             )
             if returning == "explicit_returning":
                 stmt = stmt.returning(t1.c.x)
+            elif "return_defaults" in returning:
+                stmt = stmt.return_defaults()
 
             r = conn.execute(stmt)
             if returning == "explicit_returning":
                 eq_(r.all(), [(1,), (2,), (3,)])
+            elif "return_defaults" in returning:
+                eq_(r.returned_defaults_rows, None)
+
+                # TODO: not sure what this is
+                eq_(r.inserted_primary_key_rows, [(None,)])
+
+            eq_(
+                conn.execute(t1.select().order_by(t1.c.x)).all(),
+                [(1, "d1"), (2, "d2"), (3, "d3")],
+            )
+
+    @testing.combinations(
+        ("implicit_returning",),
+        ("no_implicit_returning",),
+        (
+            "explicit_returning",
+            testing.requires.returning
+            + testing.requires.insert_executemany_returning,
+        ),
+        (
+            "return_defaults_no_implicit_returning",
+            testing.requires.returning
+            + testing.requires.insert_executemany_returning,
+        ),
+        (
+            "return_defaults_implicit_returning",
+            testing.requires.returning
+            + testing.requires.insert_executemany_returning,
+        ),
+        argnames="returning",
+    )
+    def test_seq_multivalues_executemany(
+        self, metadata, testing_engine, returning
+    ):
+        t1 = Table(
+            "t",
+            metadata,
+            Column("x", Integer, Sequence("my_seq"), primary_key=True),
+            Column("data", String(50)),
+        )
+
+        e = engines.testing_engine(
+            options={
+                "implicit_returning": "no_implicit_returning" not in returning
+            }
+        )
+        metadata.create_all(e)
+        with e.begin() as conn:
+
+            stmt = t1.insert()
+            if returning == "explicit_returning":
+                stmt = stmt.returning(t1.c.x)
+            elif "return_defaults" in returning:
+                stmt = stmt.return_defaults()
+
+            r = conn.execute(
+                stmt, [{"data": "d1"}, {"data": "d2"}, {"data": "d3"}]
+            )
+            if returning == "explicit_returning":
+                eq_(r.all(), [(1,), (2,), (3,)])
+            elif "return_defaults" in returning:
+                if "no_implicit_returning" in returning:
+                    eq_(r.returned_defaults_rows, None)
+                    eq_(r.inserted_primary_key_rows, [(1,), (2,), (3,)])
+                else:
+                    eq_(r.returned_defaults_rows, [(1,), (2,), (3,)])
+                    eq_(r.inserted_primary_key_rows, [(1,), (2,), (3,)])
 
             eq_(
                 conn.execute(t1.select().order_by(t1.c.x)).all(),