From: Mike Bayer Date: Wed, 7 Sep 2022 21:13:23 +0000 (-0400) Subject: enable UPDATE..FROM for SQLite X-Git-Tag: rel_2_0_0b1~70^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=885c61cf226695cafa12e56ed047ba761c2f7f93;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git enable UPDATE..FROM for SQLite 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 --- diff --git a/doc/build/changelog/unreleased_20/7185.rst b/doc/build/changelog/unreleased_20/7185.rst new file mode 100644 index 0000000000..223bb90551 --- /dev/null +++ b/doc/build/changelog/unreleased_20/7185.rst @@ -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. diff --git a/lib/sqlalchemy/dialects/sqlite/base.py b/lib/sqlalchemy/dialects/sqlite/base.py index 222f3a1379..88c6dbe183 100644 --- a/lib/sqlalchemy/dialects/sqlite/base.py +++ b/lib/sqlalchemy/dialects/sqlite/base.py @@ -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 diff --git a/test/orm/test_update_delete.py b/test/orm/test_update_delete.py index adaed8f6f8..1e93f88de6 100644 --- a/test/orm/test_update_delete.py +++ b/test/orm/test_update_delete.py @@ -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 diff --git a/test/requirements.py b/test/requirements.py index 9001b52367..66d6b225e5 100644 --- a/test/requirements.py +++ b/test/requirements.py @@ -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", )