mirror of
https://github.com/django/django.git
synced 2025-03-12 10:22:37 +00:00
[5.2.x] Refs #36148 -- Relied on a feature switch to define tuple lookups support.
This should allow backends more easily opt-in or out of native support and rely on the fallback if unavailable. Backport of a0a765ddeb5056c85e084773d3f6432e2a426638 from main.
This commit is contained in:
parent
16c7dc543c
commit
f8fce8d4dc
@ -368,6 +368,9 @@ class BaseDatabaseFeatures:
|
|||||||
# Does the backend support unlimited character columns?
|
# Does the backend support unlimited character columns?
|
||||||
supports_unlimited_charfield = False
|
supports_unlimited_charfield = False
|
||||||
|
|
||||||
|
# Does the backend support native tuple lookups (=, >, <, IN)?
|
||||||
|
supports_tuple_lookups = True
|
||||||
|
|
||||||
# Collation names for use by the Django test suite.
|
# Collation names for use by the Django test suite.
|
||||||
test_collations = {
|
test_collations = {
|
||||||
"ci": None, # Case-insensitive.
|
"ci": None, # Case-insensitive.
|
||||||
|
@ -81,6 +81,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
|||||||
allows_multiple_constraints_on_same_fields = False
|
allows_multiple_constraints_on_same_fields = False
|
||||||
supports_json_field_contains = False
|
supports_json_field_contains = False
|
||||||
supports_collation_on_textfield = False
|
supports_collation_on_textfield = False
|
||||||
|
supports_tuple_lookups = False
|
||||||
test_now_utc_template = "CURRENT_TIMESTAMP AT TIME ZONE 'UTC'"
|
test_now_utc_template = "CURRENT_TIMESTAMP AT TIME ZONE 'UTC'"
|
||||||
django_test_expected_failures = {
|
django_test_expected_failures = {
|
||||||
# A bug in Django/oracledb with respect to string handling (#23843).
|
# A bug in Django/oracledb with respect to string handling (#23843).
|
||||||
|
@ -61,6 +61,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
|||||||
insert_test_table_with_defaults = 'INSERT INTO {} ("null") VALUES (1)'
|
insert_test_table_with_defaults = 'INSERT INTO {} ("null") VALUES (1)'
|
||||||
supports_default_keyword_in_insert = False
|
supports_default_keyword_in_insert = False
|
||||||
supports_unlimited_charfield = True
|
supports_unlimited_charfield = True
|
||||||
|
supports_tuple_lookups = False
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def django_test_skips(self):
|
def django_test_skips(self):
|
||||||
|
@ -96,9 +96,20 @@ class TupleLookupMixin:
|
|||||||
)
|
)
|
||||||
return "(%s)" % sql, params
|
return "(%s)" % sql, params
|
||||||
|
|
||||||
|
def get_fallback_sql(self, compiler, connection):
|
||||||
|
raise NotImplementedError(
|
||||||
|
f"{self.__class__.__name__}.get_fallback_sql() must be implemented "
|
||||||
|
f"for backends that don't have the supports_tuple_lookups feature enabled."
|
||||||
|
)
|
||||||
|
|
||||||
|
def as_sql(self, compiler, connection):
|
||||||
|
if not connection.features.supports_tuple_lookups:
|
||||||
|
return self.get_fallback_sql(compiler, connection)
|
||||||
|
return super().as_sql(compiler, connection)
|
||||||
|
|
||||||
|
|
||||||
class TupleExact(TupleLookupMixin, Exact):
|
class TupleExact(TupleLookupMixin, Exact):
|
||||||
def as_oracle(self, compiler, connection):
|
def get_fallback_sql(self, compiler, connection):
|
||||||
# Process right-hand-side to trigger sanitization.
|
# Process right-hand-side to trigger sanitization.
|
||||||
self.process_rhs(compiler, connection)
|
self.process_rhs(compiler, connection)
|
||||||
# e.g.: (a, b, c) == (x, y, z) as SQL:
|
# e.g.: (a, b, c) == (x, y, z) as SQL:
|
||||||
@ -132,7 +143,7 @@ class TupleIsNull(TupleLookupMixin, IsNull):
|
|||||||
|
|
||||||
|
|
||||||
class TupleGreaterThan(TupleLookupMixin, GreaterThan):
|
class TupleGreaterThan(TupleLookupMixin, GreaterThan):
|
||||||
def as_oracle(self, compiler, connection):
|
def get_fallback_sql(self, compiler, connection):
|
||||||
# Process right-hand-side to trigger sanitization.
|
# Process right-hand-side to trigger sanitization.
|
||||||
self.process_rhs(compiler, connection)
|
self.process_rhs(compiler, connection)
|
||||||
# e.g.: (a, b, c) > (x, y, z) as SQL:
|
# e.g.: (a, b, c) > (x, y, z) as SQL:
|
||||||
@ -160,7 +171,7 @@ class TupleGreaterThan(TupleLookupMixin, GreaterThan):
|
|||||||
|
|
||||||
|
|
||||||
class TupleGreaterThanOrEqual(TupleLookupMixin, GreaterThanOrEqual):
|
class TupleGreaterThanOrEqual(TupleLookupMixin, GreaterThanOrEqual):
|
||||||
def as_oracle(self, compiler, connection):
|
def get_fallback_sql(self, compiler, connection):
|
||||||
# Process right-hand-side to trigger sanitization.
|
# Process right-hand-side to trigger sanitization.
|
||||||
self.process_rhs(compiler, connection)
|
self.process_rhs(compiler, connection)
|
||||||
# e.g.: (a, b, c) >= (x, y, z) as SQL:
|
# e.g.: (a, b, c) >= (x, y, z) as SQL:
|
||||||
@ -188,7 +199,7 @@ class TupleGreaterThanOrEqual(TupleLookupMixin, GreaterThanOrEqual):
|
|||||||
|
|
||||||
|
|
||||||
class TupleLessThan(TupleLookupMixin, LessThan):
|
class TupleLessThan(TupleLookupMixin, LessThan):
|
||||||
def as_oracle(self, compiler, connection):
|
def get_fallback_sql(self, compiler, connection):
|
||||||
# Process right-hand-side to trigger sanitization.
|
# Process right-hand-side to trigger sanitization.
|
||||||
self.process_rhs(compiler, connection)
|
self.process_rhs(compiler, connection)
|
||||||
# e.g.: (a, b, c) < (x, y, z) as SQL:
|
# e.g.: (a, b, c) < (x, y, z) as SQL:
|
||||||
@ -216,7 +227,7 @@ class TupleLessThan(TupleLookupMixin, LessThan):
|
|||||||
|
|
||||||
|
|
||||||
class TupleLessThanOrEqual(TupleLookupMixin, LessThanOrEqual):
|
class TupleLessThanOrEqual(TupleLookupMixin, LessThanOrEqual):
|
||||||
def as_oracle(self, compiler, connection):
|
def get_fallback_sql(self, compiler, connection):
|
||||||
# Process right-hand-side to trigger sanitization.
|
# Process right-hand-side to trigger sanitization.
|
||||||
self.process_rhs(compiler, connection)
|
self.process_rhs(compiler, connection)
|
||||||
# e.g.: (a, b, c) <= (x, y, z) as SQL:
|
# e.g.: (a, b, c) <= (x, y, z) as SQL:
|
||||||
@ -315,17 +326,19 @@ class TupleIn(TupleLookupMixin, In):
|
|||||||
|
|
||||||
return compiler.compile(Tuple(*result))
|
return compiler.compile(Tuple(*result))
|
||||||
|
|
||||||
def as_sql(self, compiler, connection):
|
def as_subquery_sql(self, compiler, connection):
|
||||||
if not self.rhs_is_direct_value():
|
lhs = self.lhs
|
||||||
return self.as_subquery(compiler, connection)
|
rhs = self.rhs
|
||||||
return super().as_sql(compiler, connection)
|
if isinstance(lhs, ColPairs):
|
||||||
|
rhs = rhs.clone()
|
||||||
|
rhs.set_values([source.name for source in lhs.sources])
|
||||||
|
lhs = Tuple(lhs)
|
||||||
|
return compiler.compile(In(lhs, rhs))
|
||||||
|
|
||||||
def as_sqlite(self, compiler, connection):
|
def get_fallback_sql(self, compiler, connection):
|
||||||
rhs = self.rhs
|
rhs = self.rhs
|
||||||
if not rhs:
|
if not rhs:
|
||||||
raise EmptyResultSet
|
raise EmptyResultSet
|
||||||
if not self.rhs_is_direct_value():
|
|
||||||
return self.as_subquery(compiler, connection)
|
|
||||||
|
|
||||||
# e.g.: (a, b, c) in [(x1, y1, z1), (x2, y2, z2)] as SQL:
|
# e.g.: (a, b, c) in [(x1, y1, z1), (x2, y2, z2)] as SQL:
|
||||||
# WHERE (a = x1 AND b = y1 AND c = z1) OR (a = x2 AND b = y2 AND c = z2)
|
# WHERE (a = x1 AND b = y1 AND c = z1) OR (a = x2 AND b = y2 AND c = z2)
|
||||||
@ -338,14 +351,10 @@ class TupleIn(TupleLookupMixin, In):
|
|||||||
|
|
||||||
return root.as_sql(compiler, connection)
|
return root.as_sql(compiler, connection)
|
||||||
|
|
||||||
def as_subquery(self, compiler, connection):
|
def as_sql(self, compiler, connection):
|
||||||
lhs = self.lhs
|
if not self.rhs_is_direct_value():
|
||||||
rhs = self.rhs
|
return self.as_subquery_sql(compiler, connection)
|
||||||
if isinstance(lhs, ColPairs):
|
return super().as_sql(compiler, connection)
|
||||||
rhs = rhs.clone()
|
|
||||||
rhs.set_values([source.name for source in lhs.sources])
|
|
||||||
lhs = Tuple(lhs)
|
|
||||||
return compiler.compile(In(lhs, rhs))
|
|
||||||
|
|
||||||
|
|
||||||
tuple_lookups = {
|
tuple_lookups = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user