summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/db/backends/oracle/base.py76
-rw-r--r--django/db/models/query.py39
2 files changed, 72 insertions, 43 deletions
diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
index 0426b9d16b..771cd09e82 100644
--- a/django/db/backends/oracle/base.py
+++ b/django/db/backends/oracle/base.py
@@ -296,46 +296,12 @@ def get_query_set_class(DefaultQuerySet):
# 1. retrieve each row in turn
# 2. convert NCLOBs
- def resolve_cols(row):
- for field in row:
- value = field
- if isinstance(field, Database.LOB):
- value = field.read()
- # cx_Oracle always returns datetime.datetime objects for
- # DATE and TIMESTAMP columns, but Django wants to see a
- # python datetime.date, .time, or .datetime.
- # As a workaround, we cast to date if all the time-related
- # fields are 0, or to time if the date is 1/1/1900.
- # A better fix would involve either patching cx_Oracle
- # or checking the Model here, neither of which is good.
- elif isinstance(field, datetime.datetime):
- if field.hour == field.minute == field.second == field.microsecond == 0:
- value = field.date()
- elif field.year == 1900 and field.month == field.day == 1:
- value = field.time()
- # In Python 2.3, the cx_Oracle driver returns its own
- # Timestamp object that we must convert to a datetime class.
- elif isinstance(field, Database.Timestamp):
- if field.hour == field.minute == field.second == field.fsecond == 0:
- value = datetime.date(field.year, field.month, field.day)
- elif field.year == 1900 and field.month == field.day == 1:
- value = datetime.time(field.hour, field.minute, field.second, field.fsecond)
- else:
- value = datetime.datetime(field.year, field.month, field.day, field.hour,
- field.minute, field.second, field.fsecond)
- # Since Oracle won't distinguish between NULL and an empty
- # string (''), we store empty strings as a space. Here is
- # where we undo that treachery.
- if value == ' ':
- value = ''
- yield value
-
while 1:
rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
if not rows:
raise StopIteration
for row in rows:
- row = list(resolve_cols(row))
+ row = self.resolve_columns(row)
if fill_cache:
obj, index_end = get_cached_row(klass=self.model, row=row,
index_start=0, max_depth=self._max_related_depth)
@@ -479,6 +445,46 @@ def get_query_set_class(DefaultQuerySet):
else:
return select, " ".join(sql), params
+ def resolve_columns(self, row, fields=()):
+ from django.db.models.fields import DateField, DateTimeField, TimeField
+ values = []
+ for value, field in map(None, row, fields):
+ if isinstance(value, Database.LOB):
+ value = value.read()
+ # Since Oracle won't distinguish between NULL and an empty
+ # string (''), we store empty strings as a space. Here is
+ # where we undo that treachery.
+ if value == ' ':
+ value = ''
+ # cx_Oracle always returns datetime.datetime objects for
+ # DATE and TIMESTAMP columns, but Django wants to see a
+ # python datetime.date, .time, or .datetime. We use the type
+ # of the Field to determine which to cast to, but it's not
+ # always available.
+ # As a workaround, we cast to date if all the time-related
+ # values are 0, or to time if the date is 1/1/1900.
+ # This could be cleaned a bit by adding a method to the Field
+ # classes to normalize values from the database (the to_python
+ # method is used for validation and isn't what we want here).
+ elif isinstance(value, Database.Timestamp):
+ # In Python 2.3, the cx_Oracle driver returns its own
+ # Timestamp object that we must convert to a datetime class.
+ if not isinstance(value, datetime.datetime):
+ value = datetime.datetime(value.year, value.month, value.day, value.hour,
+ value.minute, value.second, value.fsecond)
+ if isinstance(field, DateTimeField):
+ pass # DateTimeField subclasses DateField so must be checked first.
+ elif isinstance(field, DateField):
+ value = value.date()
+ elif isinstance(field, TimeField):
+ value = value.time()
+ elif value.hour == value.minute == value.second == value.microsecond == 0:
+ value = value.date()
+ elif value.year == 1900 and value.month == value.day == 1:
+ value = value.time()
+ values.append(value)
+ return values
+
return OracleQuerySet
diff --git a/django/db/models/query.py b/django/db/models/query.py
index fcbe299399..44edb1cd22 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -184,11 +184,14 @@ class _QuerySet(object):
fill_cache = self._select_related
index_end = len(self.model._meta.fields)
+ has_resolve_columns = hasattr(self, 'resolve_columns')
while 1:
rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
if not rows:
raise StopIteration
for row in rows:
+ if has_resolve_columns:
+ row = self.resolve_columns(row)
if fill_cache:
obj, index_end = get_cached_row(klass=self.model, row=row,
index_start=0, max_depth=self._max_related_depth)
@@ -573,20 +576,24 @@ class ValuesQuerySet(QuerySet):
# self._fields is a list of field names to fetch.
if self._fields:
- columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields]
- field_names = self._fields
+ fields = [self.model._meta.get_field(f, many_to_many=False) for f in self._fields]
else: # Default to all fields.
- columns = [f.column for f in self.model._meta.fields]
- field_names = [f.attname for f in self.model._meta.fields]
+ fields = self.model._meta.fields
+ columns = [f.column for f in fields]
+ field_names = [f.attname for f in fields]
select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns]
cursor = connection.cursor()
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
+
+ has_resolve_columns = hasattr(self, 'resolve_columns')
while 1:
rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
if not rows:
raise StopIteration
for row in rows:
+ if has_resolve_columns:
+ row = self.resolve_columns(row, fields)
yield dict(zip(field_names, row))
def _clone(self, klass=None, **kwargs):
@@ -597,6 +604,7 @@ class ValuesQuerySet(QuerySet):
class DateQuerySet(QuerySet):
def iterator(self):
from django.db.backends.util import typecast_timestamp
+ from django.db.models.fields import DateTimeField
self._order_by = () # Clear this because it'll mess things up otherwise.
if self._field.null:
self._where.append('%s.%s IS NOT NULL' % \
@@ -620,10 +628,25 @@ class DateQuerySet(QuerySet):
backend.quote_name(self._field.column))), sql, group_by, self._order)
cursor = connection.cursor()
cursor.execute(sql, params)
- if backend.needs_datetime_string_cast:
- return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()]
- else:
- return [row[0] for row in cursor.fetchall()]
+
+ has_resolve_columns = hasattr(self, 'resolve_columns')
+ needs_datetime_string_cast = backend.needs_datetime_string_cast
+ dates = []
+ # It would be better to use self._field here instead of DateTimeField(),
+ # but in Oracle that will result in a list of datetime.date instead of
+ # datetime.datetime.
+ fields = [DateTimeField()]
+ while 1:
+ rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
+ if not rows:
+ return dates
+ for row in rows:
+ date = row[0]
+ if has_resolve_columns:
+ date = self.resolve_columns([date], fields)[0]
+ elif needs_datetime_string_cast:
+ date = typecast_timestamp(str(date))
+ dates.append(date)
def _clone(self, klass=None, **kwargs):
c = super(DateQuerySet, self)._clone(klass, **kwargs)