summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorontowhee <82607723+ontowhee@users.noreply.github.com>2025-03-15 19:23:28 -0700
committerSarah Boyce <42296566+sarahboyce@users.noreply.github.com>2025-05-20 10:01:42 +0200
commitddb85294159185c5bd5cae34c9ef735ff8409bfe (patch)
treeaf6266521ed95fffa1e497e0c6420b867a7df839 /django
parentf603ece016352834829c84c157cc6dbf93c8d8d5 (diff)
Fixed #34262 -- Added support for AnyValue for SQLite, MySQL, Oracle, and Postgresql 16+.
Thanks Simon Charette for the guidance and review. Thanks Tim Schilling for the documentation review. Thanks David Wobrock for investigation and solution proposals.
Diffstat (limited to 'django')
-rw-r--r--django/db/backends/base/features.py3
-rw-r--r--django/db/backends/mysql/features.py14
-rw-r--r--django/db/backends/oracle/features.py1
-rw-r--r--django/db/backends/postgresql/features.py2
-rw-r--r--django/db/backends/sqlite3/_functions.py6
-rw-r--r--django/db/backends/sqlite3/features.py1
-rw-r--r--django/db/models/aggregates.py15
7 files changed, 32 insertions, 10 deletions
diff --git a/django/db/backends/base/features.py b/django/db/backends/base/features.py
index d3b148dc3d..87fc7204ee 100644
--- a/django/db/backends/base/features.py
+++ b/django/db/backends/base/features.py
@@ -266,6 +266,9 @@ class BaseDatabaseFeatures:
# delimiter along with DISTINCT.
supports_aggregate_distinct_multiple_argument = True
+ # Does the database support SQL 2023 ANY_VALUE in GROUP BY?
+ supports_any_value = False
+
# Does the backend support indexing a TextField?
supports_index_on_text_field = True
diff --git a/django/db/backends/mysql/features.py b/django/db/backends/mysql/features.py
index d0749eb5e6..6ae4c56af1 100644
--- a/django/db/backends/mysql/features.py
+++ b/django/db/backends/mysql/features.py
@@ -111,16 +111,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
},
}
)
- if "ONLY_FULL_GROUP_BY" in self.connection.sql_mode:
- skips.update(
- {
- "GROUP BY cannot contain nonaggregated column when "
- "ONLY_FULL_GROUP_BY mode is enabled on MySQL, see #34262.": {
- "aggregation.tests.AggregateTestCase."
- "test_group_by_nested_expression_with_params",
- },
- }
- )
if self.connection.mysql_version < (8, 0, 31):
skips.update(
{
@@ -297,3 +287,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
if self.connection.mysql_is_mariadb:
return "ONLY_FULL_GROUP_BY" not in self.connection.sql_mode
return True
+
+ @cached_property
+ def supports_any_value(self):
+ return not self.connection.mysql_is_mariadb
diff --git a/django/db/backends/oracle/features.py b/django/db/backends/oracle/features.py
index b355e7136c..fd327fabcc 100644
--- a/django/db/backends/oracle/features.py
+++ b/django/db/backends/oracle/features.py
@@ -61,6 +61,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
END;
"""
supports_callproc_kwargs = True
+ supports_any_value = True
supports_over_clause = True
supports_frame_range_fixed_distance = True
supports_ignore_conflicts = False
diff --git a/django/db/backends/postgresql/features.py b/django/db/backends/postgresql/features.py
index 74a65fdbd9..83e6b5cf7f 100644
--- a/django/db/backends/postgresql/features.py
+++ b/django/db/backends/postgresql/features.py
@@ -162,3 +162,5 @@ class DatabaseFeatures(BaseDatabaseFeatures):
supports_nulls_distinct_unique_constraints = property(
operator.attrgetter("is_postgresql_15")
)
+
+ supports_any_value = property(operator.attrgetter("is_postgresql_16"))
diff --git a/django/db/backends/sqlite3/_functions.py b/django/db/backends/sqlite3/_functions.py
index 6d07d3d78b..b38e06eec2 100644
--- a/django/db/backends/sqlite3/_functions.py
+++ b/django/db/backends/sqlite3/_functions.py
@@ -80,6 +80,7 @@ def register(connection):
connection.create_aggregate("STDDEV_SAMP", 1, StdDevSamp)
connection.create_aggregate("VAR_POP", 1, VarPop)
connection.create_aggregate("VAR_SAMP", 1, VarSamp)
+ connection.create_aggregate("ANY_VALUE", 1, AnyValue)
# Some math functions are enabled by default in SQLite 3.35+.
sql = "select sqlite_compileoption_used('ENABLE_MATH_FUNCTIONS')"
if not connection.execute(sql).fetchone()[0]:
@@ -513,3 +514,8 @@ class VarPop(ListAggregate):
class VarSamp(ListAggregate):
finalize = statistics.variance
+
+
+class AnyValue(ListAggregate):
+ def finalize(self):
+ return self[0]
diff --git a/django/db/backends/sqlite3/features.py b/django/db/backends/sqlite3/features.py
index 1e995a87c0..8604adf40a 100644
--- a/django/db/backends/sqlite3/features.py
+++ b/django/db/backends/sqlite3/features.py
@@ -36,6 +36,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
supports_aggregate_filter_clause = True
supports_aggregate_order_by_clause = Database.sqlite_version_info >= (3, 44, 0)
supports_aggregate_distinct_multiple_argument = False
+ supports_any_value = True
order_by_nulls_first = True
supports_json_field_contains = False
supports_update_conflicts = True
diff --git a/django/db/models/aggregates.py b/django/db/models/aggregates.py
index 4174904f71..2a7ed23bc3 100644
--- a/django/db/models/aggregates.py
+++ b/django/db/models/aggregates.py
@@ -22,6 +22,7 @@ from django.db.models.functions.mixins import (
__all__ = [
"Aggregate",
+ "AnyValue",
"Avg",
"Count",
"Max",
@@ -229,6 +230,20 @@ class Aggregate(Func):
return options
+class AnyValue(Aggregate):
+ function = "ANY_VALUE"
+ name = "AnyValue"
+ arity = 1
+ window_compatible = False
+
+ def as_sql(self, compiler, connection, **extra_context):
+ if not connection.features.supports_any_value:
+ raise NotSupportedError(
+ "ANY_VALUE is not supported on this database backend."
+ )
+ return super().as_sql(compiler, connection, **extra_context)
+
+
class Avg(FixDurationInputMixin, NumericOutputFieldMixin, Aggregate):
function = "AVG"
name = "Avg"