]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
enable UPDATE..FROM for SQLite
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 7 Sep 2022 21:13:23 +0000 (17:13 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 7 Sep 2022 23:10:53 +0000 (19:10 -0400)
The SQLite dialect now supports UPDATE..FROM syntax, for UPDATE statements
that may refer to additional tables within the WHERE criteria of the
statement without the need to use subqueries. This syntax is invoked
automatically when using the :class:`_dml.Update` construct when more than
one table or other entity or selectable is used.

Fixes: #7185
Change-Id: I27e94ace9ff761cc45e652fa1abff8cd1f42fec5

doc/build/changelog/unreleased_20/7185.rst [new file with mode: 0644]
lib/sqlalchemy/dialects/sqlite/base.py
test/orm/test_update_delete.py
test/requirements.py

diff --git a/doc/build/changelog/unreleased_20/7185.rst b/doc/build/changelog/unreleased_20/7185.rst
new file mode 100644 (file)
index 0000000..223bb90
--- /dev/null
@@ -0,0 +1,9 @@
+.. change::
+    :tags: usecase, sqlite
+    :tickets: 7185
+
+    The SQLite dialect now supports UPDATE..FROM syntax, for UPDATE statements
+    that may refer to additional tables within the WHERE criteria of the
+    statement without the need to use subqueries. This syntax is invoked
+    automatically when using the :class:`_dml.Update` construct when more than
+    one table or other entity or selectable is used.
index 222f3a13793d8f9ac18c4c635fffd0867469ffa2..88c6dbe1834778836a849396521350f82dc8d77f 100644 (file)
@@ -1381,6 +1381,15 @@ class SQLiteCompiler(compiler.SQLCompiler):
         # sqlite has no "FOR UPDATE" AFAICT
         return ""
 
+    def update_from_clause(
+        self, update_stmt, from_table, extra_froms, from_hints, **kw
+    ):
+        kw["asfrom"] = True
+        return "FROM " + ", ".join(
+            t._compiler_dispatch(self, fromhints=from_hints, **kw)
+            for t in extra_froms
+        )
+
     def visit_is_distinct_from_binary(self, binary, operator, **kw):
         return "%s IS NOT %s" % (
             self.process(binary.left),
@@ -1933,6 +1942,7 @@ class SQLiteDialect(default.DefaultDialect):
     insert_returning = True
     update_returning = True
     delete_returning = True
+    update_returning_multifrom = True
 
     default_paramstyle = "qmark"
     execution_ctx_cls = SQLiteExecutionContext
index adaed8f6f8b58834e6099cc9d120780d9b7a6588..1e93f88de608c25970324f486638a514d271837c 100644 (file)
@@ -2038,11 +2038,12 @@ class InheritTest(fixtures.DeclarativeMappedTest):
     def test_update_from(self, synchronize_session):
         """test an UPDATE that uses multiple tables.
 
-        The limitation that MariaDB has with DELETE does not apply here
-        at the moment as MariaDB doesn't support UPDATE..RETURNING at all.
-        However, the logic from DELETE is still implemented in
-        persistence.py. If MariaDB adds UPDATE...RETURNING, or SQLite adds
-        UPDATE..FROM, etc., then it will be useful.
+        The limitation that MariaDB has with DELETE does not apply here at the
+        moment as MariaDB doesn't support UPDATE..RETURNING at all. However,
+        the logic from DELETE is still implemented in persistence.py. If
+        MariaDB adds UPDATE...RETURNING, then it may be useful. SQLite,
+        PostgreSQL, MSSQL all support UPDATE..FROM however RETURNING seems to
+        function correctly for all three.
 
         """
         Engineer = self.classes.Engineer
index 9001b52367ff52d50cc8ce519be2574953279f23..66d6b225e5da733a9ce100e409d0f96aeafcd63d 100644 (file)
@@ -461,8 +461,8 @@ class DefaultRequirements(SuiteRequirements):
     def update_from(self):
         """Target must support UPDATE..FROM syntax"""
 
-        return only_on(
-            ["postgresql", "mssql", "mysql", "mariadb"],
+        return skip_if(
+            "oracle",
             "Backend does not support UPDATE..FROM",
         )