Browse Source

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.
Simon Charette 1 month ago
parent
commit
4a3ad9eebb

+ 5 - 1
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)

+ 0 - 1
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):

+ 11 - 0
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