mirror of
https://github.com/django/django.git
synced 2025-11-17 10:43:25 +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
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import DatabaseError
|
||||
from django.db.backends.base.operations import BaseDatabaseOperations
|
||||
from django.utils import timezone
|
||||
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 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):
|
||||
converters = super().get_db_converters(expression)
|
||||
internal_type = expression.output_field.get_internal_type()
|
||||
|
|
|
|||
|
|
@ -561,6 +561,10 @@ def _sqlite_format_dtdelta(conn, lhs, rhs):
|
|||
return None
|
||||
# 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]"
|
||||
# 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)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue