From 4a3ad9eebbc16ce80b348644b557c84ecc741be7 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Tue, 28 Jan 2025 15:04:14 -0500 Subject: [PATCH] Fixed #36148 -- Enabled native tuple comparison lookups on SQLite 3.37+ and Oracle 23.4+. VALUES must be explicitly specified when declaring a sequence of tuples on SQLite < 3.37 but it's not required on >= 3.37. See sqlite/sqlite@9289f51 which addressed the last remaining issue with IN. --- django/db/backends/oracle/features.py | 6 +++++- django/db/backends/sqlite3/features.py | 1 - django/db/models/fields/tuple_lookups.py | 11 +++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/django/db/backends/oracle/features.py b/django/db/backends/oracle/features.py index b6980ad1a5..559a36bfed 100644 --- a/django/db/backends/oracle/features.py +++ b/django/db/backends/oracle/features.py @@ -80,7 +80,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): allows_multiple_constraints_on_same_fields = False supports_json_field_contains = False supports_collation_on_textfield = False - supports_tuple_lookups = False test_now_utc_template = "CURRENT_TIMESTAMP AT TIME ZONE 'UTC'" django_test_expected_failures = { # A bug in Django/oracledb with respect to string handling (#23843). @@ -217,3 +216,8 @@ class DatabaseFeatures(BaseDatabaseFeatures): @cached_property def bare_select_suffix(self): return "" if self.connection.oracle_version >= (23,) else " FROM DUAL" + + @cached_property + def supports_tuple_lookups(self): + # Support is known to be missing on 23.2 but available on 23.4. + return self.connection.oracle_version >= (23, 4) diff --git a/django/db/backends/sqlite3/features.py b/django/db/backends/sqlite3/features.py index 6ab6308ece..60893561df 100644 --- a/django/db/backends/sqlite3/features.py +++ b/django/db/backends/sqlite3/features.py @@ -61,7 +61,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): insert_test_table_with_defaults = 'INSERT INTO {} ("null") VALUES (1)' supports_default_keyword_in_insert = False supports_unlimited_charfield = True - supports_tuple_lookups = False @cached_property def django_test_skips(self): diff --git a/django/db/models/fields/tuple_lookups.py b/django/db/models/fields/tuple_lookups.py index 1e77f095c8..b45bcaf2cd 100644 --- a/django/db/models/fields/tuple_lookups.py +++ b/django/db/models/fields/tuple_lookups.py @@ -27,6 +27,17 @@ class Tuple(Func): def __iter__(self): return iter(self.source_expressions) + def as_sqlite(self, compiler, connection): + if connection.get_database_version() < (3, 37) and isinstance( + first_expr := self.source_expressions[0], Tuple + ): + first_expr = first_expr.copy() + first_expr.function = "VALUES" + return Tuple(first_expr, *self.source_expressions[1:]).as_sql( + compiler, connection + ) + return self.as_sql(compiler, connection) + class TupleLookupMixin: allows_composite_expressions = True