diff -r b758351d191f -r cc9b7e14412b web/lib/django/db/backends/oracle/query.py --- a/web/lib/django/db/backends/oracle/query.py Wed May 19 17:43:59 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,150 +0,0 @@ -""" -Custom Query class for Oracle. -Derives from: django.db.models.sql.query.Query -""" - -import datetime - -from django.db.backends import util -from django.utils.encoding import force_unicode - -# Cache. Maps default query class to new Oracle query class. -_classes = {} - -def query_class(QueryClass, Database): - """ - Returns a custom django.db.models.sql.query.Query subclass that is - appropriate for Oracle. - - The 'Database' module (cx_Oracle) is passed in here so that all the setup - required to import it only needs to be done by the calling module. - """ - global _classes - try: - return _classes[QueryClass] - except KeyError: - pass - - class OracleQuery(QueryClass): - def __reduce__(self): - """ - Enable pickling for this class (normal pickling handling doesn't - work as Python can only pickle module-level classes by default). - """ - if hasattr(QueryClass, '__getstate__'): - assert hasattr(QueryClass, '__setstate__') - data = self.__getstate__() - else: - data = self.__dict__ - return (unpickle_query_class, (QueryClass,), data) - - def resolve_columns(self, row, fields=()): - # If this query has limit/offset information, then we expect the - # first column to be an extra "_RN" column that we need to throw - # away. - if self.high_mark is not None or self.low_mark: - rn_offset = 1 - else: - rn_offset = 0 - index_start = rn_offset + len(self.extra_select.keys()) - values = [self.convert_values(v, None) - for v in row[rn_offset:index_start]] - for value, field in map(None, row[index_start:], fields): - values.append(self.convert_values(value, field)) - return tuple(values) - - def convert_values(self, value, field): - if isinstance(value, Database.LOB): - value = value.read() - if field and field.get_internal_type() == 'TextField': - value = force_unicode(value) - - # Oracle stores empty strings as null. We need to undo this in - # order to adhere to the Django convention of using the empty - # string instead of null, but only if the field accepts the - # empty string. - if value is None and field and field.empty_strings_allowed: - value = u'' - # Convert 1 or 0 to True or False - elif value in (1, 0) and field and field.get_internal_type() in ('BooleanField', 'NullBooleanField'): - value = bool(value) - # Force floats to the correct type - elif value is not None and field and field.get_internal_type() == 'FloatField': - value = float(value) - # Convert floats to decimals - elif value is not None and field and field.get_internal_type() == 'DecimalField': - value = util.typecast_decimal(field.format_number(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 field and field.get_internal_type() == 'DateTimeField': - pass - elif field and field.get_internal_type() == 'DateField': - value = value.date() - elif field and field.get_internal_type() == 'TimeField' or (value.year == 1900 and value.month == value.day == 1): - value = value.time() - elif value.hour == value.minute == value.second == value.microsecond == 0: - value = value.date() - return value - - def as_sql(self, with_limits=True, with_col_aliases=False): - """ - Creates the SQL for this query. Returns the SQL string and list - of parameters. This is overriden from the original Query class - to handle the additional SQL Oracle requires to emulate LIMIT - and OFFSET. - - If 'with_limits' is False, any limit/offset information is not - included in the query. - """ - - # The `do_offset` flag indicates whether we need to construct - # the SQL needed to use limit/offset with Oracle. - do_offset = with_limits and (self.high_mark is not None - or self.low_mark) - if not do_offset: - sql, params = super(OracleQuery, self).as_sql(with_limits=False, - with_col_aliases=with_col_aliases) - else: - sql, params = super(OracleQuery, self).as_sql(with_limits=False, - with_col_aliases=True) - - # Wrap the base query in an outer SELECT * with boundaries on - # the "_RN" column. This is the canonical way to emulate LIMIT - # and OFFSET on Oracle. - high_where = '' - if self.high_mark is not None: - high_where = 'WHERE ROWNUM <= %d' % (self.high_mark,) - sql = 'SELECT * FROM (SELECT ROWNUM AS "_RN", "_SUB".* FROM (%s) "_SUB" %s) WHERE "_RN" > %d' % (sql, high_where, self.low_mark) - - return sql, params - - _classes[QueryClass] = OracleQuery - return OracleQuery - -def unpickle_query_class(QueryClass): - """ - Utility function, called by Python's unpickling machinery, that handles - unpickling of Oracle Query subclasses. - """ - # XXX: Would be nice to not have any dependency on cx_Oracle here. Since - # modules can't be pickled, we need a way to know to load the right module. - import cx_Oracle - - klass = query_class(QueryClass, cx_Oracle) - return klass.__new__(klass) -unpickle_query_class.__safe_for_unpickling__ = True