diff options
| author | Marc Tamlyn <marc.tamlyn@gmail.com> | 2014-12-21 14:36:37 +0000 |
|---|---|---|
| committer | Marc Tamlyn <marc.tamlyn@gmail.com> | 2014-12-23 10:38:32 +0000 |
| commit | 5ca82e710e2f92b8c5114492205c8764918407d3 (patch) | |
| tree | 47fe9f6501c2b93c164ff866b2945a0b4d3f8153 /django | |
| parent | 803947161b2296a1c71b9cb7822c469a5fffef04 (diff) | |
Fixed #24033 -- Use interval type on Oracle.
Use INTERVAL DAY(9) TO SECOND(6) for Durationfield on Oracle rather than
storing as a NUMBER(19) of microseconds.
There are issues with cx_Oracle which require some extra data
manipulation in the database backend when constructing queries, but it
handles the conversion back to timedelta objects cleanly.
Thanks to Shai for the review.
Diffstat (limited to 'django')
| -rw-r--r-- | django/db/backends/__init__.py | 6 | ||||
| -rw-r--r-- | django/db/backends/oracle/base.py | 10 | ||||
| -rw-r--r-- | django/db/backends/oracle/creation.py | 2 | ||||
| -rw-r--r-- | django/db/backends/postgresql_psycopg2/base.py | 1 | ||||
| -rw-r--r-- | django/db/models/expressions.py | 3 | ||||
| -rw-r--r-- | django/db/models/fields/__init__.py | 3 |
6 files changed, 19 insertions, 6 deletions
diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 104744b220..eb70ad9861 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -580,6 +580,12 @@ class BaseDatabaseFeatures(object): # Is there a true datatype for timedeltas? has_native_duration_field = False + # Does the database driver support timedeltas as arguments? + # This is only relevant when there is a native duration field. + # Specifically, there is a bug with cx_Oracle: + # https://bitbucket.org/anthony_tuininga/cx_oracle/issue/7/ + driver_supports_timedelta_args = False + # Do time/datetime fields have microsecond precision? supports_microsecond_precision = True diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index 9f275b785d..aadc7c9682 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -62,6 +62,7 @@ from django.db.backends.oracle.introspection import DatabaseIntrospection from django.db.backends.oracle.schema import DatabaseSchemaEditor from django.db.utils import InterfaceError from django.utils import six, timezone +from django.utils.duration import duration_string from django.utils.encoding import force_bytes, force_text from django.utils.functional import cached_property @@ -106,6 +107,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): supports_timezones = False has_zoneinfo_database = pytz is not None supports_bitwise_or = False + has_native_duration_field = True can_defer_constraint_checks = True supports_partially_nullable_unique_constraints = False truncates_names = True @@ -212,9 +214,6 @@ WHEN (new.%(col_name)s IS NULL) return fmt % (days, hours, minutes, seconds, timedelta.microseconds, day_precision), [] - def format_for_duration_arithmetic(self, sql): - return "NUMTODSINTERVAL(%s / 1000000, 'SECOND')" % sql - def date_trunc_sql(self, lookup_type, field_name): # http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions230.htm#i1002084 if lookup_type in ('year', 'month'): @@ -796,6 +795,11 @@ class OracleParam(object): param = timezone.make_aware(param, default_timezone) param = Oracle_datetime.from_datetime(param.astimezone(timezone.utc)) + if isinstance(param, datetime.timedelta): + param = duration_string(param) + if ' ' not in param: + param = '0 ' + param + string_size = 0 # Oracle doesn't recognize True and False correctly in Python 3. # The conversion done below works both in 2 and 3. diff --git a/django/db/backends/oracle/creation.py b/django/db/backends/oracle/creation.py index ae4f5f42ec..27f0e5333b 100644 --- a/django/db/backends/oracle/creation.py +++ b/django/db/backends/oracle/creation.py @@ -29,7 +29,7 @@ class DatabaseCreation(BaseDatabaseCreation): 'DateField': 'DATE', 'DateTimeField': 'TIMESTAMP', 'DecimalField': 'NUMBER(%(max_digits)s, %(decimal_places)s)', - 'DurationField': 'NUMBER(19)', + 'DurationField': 'INTERVAL DAY(9) TO SECOND(6)', 'FileField': 'NVARCHAR2(%(max_length)s)', 'FilePathField': 'NVARCHAR2(%(max_length)s)', 'FloatField': 'DOUBLE PRECISION', diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py index 5fb5d68b5b..c1dfa54c95 100644 --- a/django/db/backends/postgresql_psycopg2/base.py +++ b/django/db/backends/postgresql_psycopg2/base.py @@ -48,6 +48,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): can_return_id_from_insert = True has_real_datatype = True has_native_duration_field = True + driver_supports_timedelta_args = True can_defer_constraint_checks = True has_select_for_update = True has_select_for_update_nowait = True diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index de0d339af7..ac69307edc 100644 --- a/django/db/models/expressions.py +++ b/django/db/models/expressions.py @@ -488,7 +488,8 @@ class Value(ExpressionNode): class DurationValue(Value): def as_sql(self, compiler, connection): - if connection.features.has_native_duration_field: + if (connection.features.has_native_duration_field and + connection.features.driver_supports_timedelta_args): return super(DurationValue, self).as_sql(compiler, connection) return connection.ops.date_interval_sql(self.value) diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 92ad769e4c..e8e6750f1e 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -1577,7 +1577,8 @@ class DecimalField(Field): class DurationField(Field): """Stores timedelta objects. - Uses interval on postgres, bigint of microseconds on other databases. + Uses interval on postgres, INVERAL DAY TO SECOND on Oracle, and bigint of + microseconds on other databases. """ empty_strings_allowed = False default_error_messages = { |
