| 102 | | def query_set_class(self, DefaultQuerySet): |
|---|
| 103 | | # Getting the base default `Query` object. |
|---|
| 104 | | DefaultQuery = DefaultQuerySet().query.__class__ |
|---|
| 105 | | |
|---|
| 106 | | class OracleQuery(DefaultQuery): |
|---|
| 107 | | def resolve_columns(self, row, fields=()): |
|---|
| 108 | | from django.db.models.fields import DateField, DateTimeField, \ |
|---|
| 109 | | TimeField, BooleanField, NullBooleanField, DecimalField, Field |
|---|
| 110 | | values = [] |
|---|
| 111 | | for value, field in map(None, row, fields): |
|---|
| 112 | | if isinstance(value, Database.LOB): |
|---|
| 113 | | value = value.read() |
|---|
| 114 | | # Oracle stores empty strings as null. We need to undo this in |
|---|
| 115 | | # order to adhere to the Django convention of using the empty |
|---|
| 116 | | # string instead of null, but only if the field accepts the |
|---|
| 117 | | # empty string. |
|---|
| 118 | | if value is None and isinstance(field, Field) and field.empty_strings_allowed: |
|---|
| 119 | | value = u'' |
|---|
| 120 | | # Convert 1 or 0 to True or False |
|---|
| 121 | | elif value in (1, 0) and isinstance(field, (BooleanField, NullBooleanField)): |
|---|
| 122 | | value = bool(value) |
|---|
| 123 | | # Convert floats to decimals |
|---|
| 124 | | elif value is not None and isinstance(field, DecimalField): |
|---|
| 125 | | value = util.typecast_decimal(field.format_number(value)) |
|---|
| 126 | | # cx_Oracle always returns datetime.datetime objects for |
|---|
| 127 | | # DATE and TIMESTAMP columns, but Django wants to see a |
|---|
| 128 | | # python datetime.date, .time, or .datetime. We use the type |
|---|
| 129 | | # of the Field to determine which to cast to, but it's not |
|---|
| 130 | | # always available. |
|---|
| 131 | | # As a workaround, we cast to date if all the time-related |
|---|
| 132 | | # values are 0, or to time if the date is 1/1/1900. |
|---|
| 133 | | # This could be cleaned a bit by adding a method to the Field |
|---|
| 134 | | # classes to normalize values from the database (the to_python |
|---|
| 135 | | # method is used for validation and isn't what we want here). |
|---|
| 136 | | elif isinstance(value, Database.Timestamp): |
|---|
| 137 | | # In Python 2.3, the cx_Oracle driver returns its own |
|---|
| 138 | | # Timestamp object that we must convert to a datetime class. |
|---|
| 139 | | if not isinstance(value, datetime.datetime): |
|---|
| 140 | | value = datetime.datetime(value.year, value.month, value.day, value.hour, |
|---|
| 141 | | value.minute, value.second, value.fsecond) |
|---|
| 142 | | if isinstance(field, DateTimeField): |
|---|
| 143 | | pass # DateTimeField subclasses DateField so must be checked first. |
|---|
| 144 | | elif isinstance(field, DateField): |
|---|
| 145 | | value = value.date() |
|---|
| 146 | | elif isinstance(field, TimeField) or (value.year == 1900 and value.month == value.day == 1): |
|---|
| 147 | | value = value.time() |
|---|
| 148 | | elif value.hour == value.minute == value.second == value.microsecond == 0: |
|---|
| 149 | | value = value.date() |
|---|
| 150 | | values.append(value) |
|---|
| 151 | | return values |
|---|
| 152 | | |
|---|
| 153 | | def as_sql(self, with_limits=True): |
|---|
| 154 | | """ |
|---|
| 155 | | Creates the SQL for this query. Returns the SQL string and list |
|---|
| 156 | | of parameters. This is overriden from the original Query class |
|---|
| 157 | | to accommodate Oracle's limit/offset SQL. |
|---|
| 158 | | |
|---|
| 159 | | If 'with_limits' is False, any limit/offset information is not |
|---|
| 160 | | included in the query. |
|---|
| 161 | | """ |
|---|
| 162 | | # The `do_offset` flag indicates whether we need to construct |
|---|
| 163 | | # the SQL needed to use limit/offset w/Oracle. |
|---|
| 164 | | do_offset = with_limits and (self.high_mark or self.low_mark) |
|---|
| 165 | | |
|---|
| 166 | | # If no offsets, just return the result of the base class |
|---|
| 167 | | # `as_sql`. |
|---|
| 168 | | if not do_offset: |
|---|
| 169 | | return super(OracleQuery, self).as_sql(with_limits=False) |
|---|
| 170 | | |
|---|
| 171 | | # `get_columns` needs to be called before `get_ordering` to |
|---|
| 172 | | # populate `_select_alias`. |
|---|
| 173 | | self.pre_sql_setup() |
|---|
| 174 | | out_cols = self.get_columns() |
|---|
| 175 | | ordering = self.get_ordering() |
|---|
| 176 | | |
|---|
| 177 | | # Getting the "ORDER BY" SQL for the ROW_NUMBER() result. |
|---|
| 178 | | if ordering: |
|---|
| 179 | | rn_orderby = ', '.join(ordering) |
|---|
| 180 | | else: |
|---|
| 181 | | # Oracle's ROW_NUMBER() function always requires an |
|---|
| 182 | | # order-by clause. So we need to define a default |
|---|
| 183 | | # order-by, since none was provided. |
|---|
| 184 | | qn = self.quote_name_unless_alias |
|---|
| 185 | | opts = self.model._meta |
|---|
| 186 | | rn_orderby = '%s.%s' % (qn(opts.db_table), qn(opts.fields[0].db_column or opts.fields[0].column)) |
|---|
| 187 | | |
|---|
| 188 | | # Getting the selection SQL and the params, which has the `rn` |
|---|
| 189 | | # extra selection SQL; we pop `rn` after this completes so we do |
|---|
| 190 | | # not get the attribute on the returned models. |
|---|
| 191 | | self.extra_select['rn'] = 'ROW_NUMBER() OVER (ORDER BY %s )' % rn_orderby |
|---|
| 192 | | sql, params= super(OracleQuery, self).as_sql(with_limits=False) |
|---|
| 193 | | self.extra_select.pop('rn') |
|---|
| 194 | | |
|---|
| 195 | | # Constructing the result SQL, using the initial select SQL |
|---|
| 196 | | # obtained above. |
|---|
| 197 | | result = ['SELECT * FROM (%s)' % sql] |
|---|
| 198 | | |
|---|
| 199 | | # Place WHERE condition on `rn` for the desired range. |
|---|
| 200 | | result.append('WHERE rn > %d' % self.low_mark) |
|---|
| 201 | | if self.high_mark: |
|---|
| 202 | | result.append('AND rn <= %d' % self.high_mark) |
|---|
| 203 | | |
|---|
| 204 | | # Returning the SQL w/params. |
|---|
| 205 | | return ' '.join(result), params |
|---|
| 206 | | |
|---|
| 207 | | from django.db import connection |
|---|
| 208 | | class OracleQuerySet(DefaultQuerySet): |
|---|
| 209 | | "The OracleQuerySet is overriden to use OracleQuery." |
|---|
| 210 | | def __init__(self, model=None, query=None): |
|---|
| 211 | | super(OracleQuerySet, self).__init__(model=model, query=query) |
|---|
| 212 | | self.query = query or OracleQuery(self.model, connection) |
|---|
| 213 | | return OracleQuerySet |
|---|
| | 102 | def query_class(self, DefaultQueryClass): |
|---|
| | 103 | return query.query_class(DefaultQueryClass, Database) |
|---|