mirror of
https://github.com/django/django.git
synced 2025-11-18 11:00:24 +00:00
Fix duration-only expressions for SQLite and MySQL
Allow arithmetic with DurationField on SQLite/MySQL by converting timedelta expressions to microseconds for proper backend handling. Addresses errors with duration-only annotation queries.
This commit is contained in:
parent
ec5aa2161d
commit
9be35a85d0
2 changed files with 23 additions and 0 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.db import DatabaseError
|
||||||
from django.db.backends.base.operations import BaseDatabaseOperations
|
from django.db.backends.base.operations import BaseDatabaseOperations
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.duration import duration_microseconds
|
from django.utils.duration import duration_microseconds
|
||||||
|
|
@ -288,6 +289,24 @@ class DatabaseOperations(BaseDatabaseOperations):
|
||||||
return 'FLOOR(%(lhs)s / POW(2, %(rhs)s))' % {'lhs': lhs, 'rhs': rhs}
|
return 'FLOOR(%(lhs)s / POW(2, %(rhs)s))' % {'lhs': lhs, 'rhs': rhs}
|
||||||
return super().combine_expression(connector, sub_expressions)
|
return super().combine_expression(connector, sub_expressions)
|
||||||
|
|
||||||
|
def combine_duration_expression(self, connector, sub_expressions):
|
||||||
|
# MySQL doesn't support direct arithmetic on INTERVAL expressions.
|
||||||
|
# For duration-only operations, we need to extract the numeric values
|
||||||
|
# and perform integer arithmetic, then format the result.
|
||||||
|
if connector not in ['+', '-']:
|
||||||
|
raise DatabaseError('Invalid connector for timedelta: %s.' % connector)
|
||||||
|
# Remove INTERVAL ... MICROSECOND wrapping if present and do numeric arithmetic
|
||||||
|
unwrapped = []
|
||||||
|
for expr in sub_expressions:
|
||||||
|
if expr.startswith('INTERVAL ') and expr.endswith(' MICROSECOND'):
|
||||||
|
# Extract the numeric part
|
||||||
|
unwrapped.append(expr[9:-12]) # Remove 'INTERVAL ' and ' MICROSECOND'
|
||||||
|
else:
|
||||||
|
unwrapped.append(expr)
|
||||||
|
# Perform the arithmetic and return as microseconds (BIGINT)
|
||||||
|
conn = ' %s ' % connector
|
||||||
|
return conn.join(unwrapped)
|
||||||
|
|
||||||
def get_db_converters(self, expression):
|
def get_db_converters(self, expression):
|
||||||
converters = super().get_db_converters(expression)
|
converters = super().get_db_converters(expression)
|
||||||
internal_type = expression.output_field.get_internal_type()
|
internal_type = expression.output_field.get_internal_type()
|
||||||
|
|
|
||||||
|
|
@ -561,6 +561,10 @@ def _sqlite_format_dtdelta(conn, lhs, rhs):
|
||||||
return None
|
return None
|
||||||
# typecast_timestamp returns a date or a datetime without timezone.
|
# typecast_timestamp returns a date or a datetime without timezone.
|
||||||
# It will be formatted as "%Y-%m-%d" or "%Y-%m-%d %H:%M:%S[.%f]"
|
# It will be formatted as "%Y-%m-%d" or "%Y-%m-%d %H:%M:%S[.%f]"
|
||||||
|
# For timedelta, return the value as microseconds to match the expected
|
||||||
|
# format for DurationField in databases without native duration support.
|
||||||
|
if isinstance(out, datetime.timedelta):
|
||||||
|
return duration_microseconds(out)
|
||||||
return str(out)
|
return str(out)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue