105 | | class OracleQuerySet(DefaultQuerySet): |
106 | | |
107 | | def iterator(self): |
108 | | "Performs the SELECT database lookup of this QuerySet." |
109 | | |
110 | | from django.db.models.query import get_cached_row |
111 | | |
112 | | # self._select is a dictionary, and dictionaries' key order is |
113 | | # undefined, so we convert it to a list of tuples. |
114 | | extra_select = self._select.items() |
115 | | |
116 | | full_query = None |
117 | | |
118 | | try: |
119 | | try: |
120 | | select, sql, params, full_query = self._get_sql_clause(get_full_query=True) |
121 | | except TypeError: |
122 | | select, sql, params = self._get_sql_clause() |
123 | | except EmptyResultSet: |
124 | | raise StopIteration |
125 | | if not full_query: |
126 | | full_query = "SELECT %s%s\n%s" % ((self._distinct and "DISTINCT " or ""), ', '.join(select), sql) |
127 | | |
128 | | cursor = connection.cursor() |
129 | | cursor.execute(full_query, params) |
130 | | |
131 | | fill_cache = self._select_related |
132 | | fields = self.model._meta.fields |
133 | | index_end = len(fields) |
134 | | |
135 | | # so here's the logic; |
136 | | # 1. retrieve each row in turn |
137 | | # 2. convert NCLOBs |
138 | | |
139 | | while 1: |
140 | | rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) |
141 | | if not rows: |
142 | | raise StopIteration |
143 | | for row in rows: |
144 | | row = self.resolve_columns(row, fields) |
145 | | if fill_cache: |
146 | | obj, index_end = get_cached_row(klass=self.model, row=row, |
147 | | index_start=0, max_depth=self._max_related_depth) |
148 | | else: |
149 | | obj = self.model(*row[:index_end]) |
150 | | for i, k in enumerate(extra_select): |
151 | | setattr(obj, k[0], row[index_end+i]) |
152 | | yield obj |
153 | | |
154 | | |
155 | | def _get_sql_clause(self, get_full_query=False): |
156 | | from django.db.models.query import fill_table_cache, \ |
157 | | handle_legacy_orderlist, orderfield2column |
158 | | |
159 | | opts = self.model._meta |
160 | | qn = connection.ops.quote_name |
161 | | |
162 | | # Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z. |
163 | | select = ["%s.%s" % (qn(opts.db_table), qn(f.column)) for f in opts.fields] |
164 | | tables = [quote_only_if_word(t) for t in self._tables] |
165 | | joins = SortedDict() |
166 | | where = self._where[:] |
167 | | params = self._params[:] |
168 | | |
169 | | # Convert self._filters into SQL. |
170 | | joins2, where2, params2 = self._filters.get_sql(opts) |
171 | | joins.update(joins2) |
172 | | where.extend(where2) |
173 | | params.extend(params2) |
174 | | |
175 | | # Add additional tables and WHERE clauses based on select_related. |
176 | | if self._select_related: |
177 | | fill_table_cache(opts, select, tables, where, opts.db_table, [opts.db_table]) |
178 | | |
179 | | # Add any additional SELECTs. |
180 | | if self._select: |
181 | | select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), qn(s[0])) for s in self._select.items()]) |
182 | | |
183 | | # Start composing the body of the SQL statement. |
184 | | sql = [" FROM", qn(opts.db_table)] |
185 | | |
186 | | # Compose the join dictionary into SQL describing the joins. |
187 | | if joins: |
188 | | sql.append(" ".join(["%s %s %s ON %s" % (join_type, table, alias, condition) |
189 | | for (alias, (table, join_type, condition)) in joins.items()])) |
190 | | |
191 | | # Compose the tables clause into SQL. |
192 | | if tables: |
193 | | sql.append(", " + ", ".join(tables)) |
194 | | |
195 | | # Compose the where clause into SQL. |
196 | | if where: |
197 | | sql.append(where and "WHERE " + " AND ".join(where)) |
198 | | |
199 | | # ORDER BY clause |
200 | | order_by = [] |
201 | | if self._order_by is not None: |
202 | | ordering_to_use = self._order_by |
203 | | else: |
204 | | ordering_to_use = opts.ordering |
205 | | for f in handle_legacy_orderlist(ordering_to_use): |
206 | | if f == '?': # Special case. |
207 | | order_by.append(DatabaseOperations().random_function_sql()) |
208 | | else: |
209 | | if f.startswith('-'): |
210 | | col_name = f[1:] |
211 | | order = "DESC" |
212 | | else: |
213 | | col_name = f |
214 | | order = "ASC" |
215 | | if "." in col_name: |
216 | | table_prefix, col_name = col_name.split('.', 1) |
217 | | table_prefix = qn(table_prefix) + '.' |
218 | | else: |
219 | | # Use the database table as a column prefix if it wasn't given, |
220 | | # and if the requested column isn't a custom SELECT. |
221 | | if "." not in col_name and col_name not in (self._select or ()): |
222 | | table_prefix = qn(opts.db_table) + '.' |
223 | | else: |
224 | | table_prefix = '' |
225 | | order_by.append('%s%s %s' % (table_prefix, qn(orderfield2column(col_name, opts)), order)) |
226 | | if order_by: |
227 | | sql.append("ORDER BY " + ", ".join(order_by)) |
228 | | |
229 | | # Look for column name collisions in the select elements |
230 | | # and fix them with an AS alias. This allows us to do a |
231 | | # SELECT * later in the paging query. |
232 | | cols = [clause.split('.')[-1] for clause in select] |
233 | | for index, col in enumerate(cols): |
234 | | if cols.count(col) > 1: |
235 | | col = '%s%d' % (col.replace('"', ''), index) |
236 | | cols[index] = col |
237 | | select[index] = '%s AS %s' % (select[index], col) |
238 | | |
239 | | # LIMIT and OFFSET clauses |
240 | | # To support limits and offsets, Oracle requires some funky rewriting of an otherwise normal looking query. |
241 | | select_clause = ",".join(select) |
242 | | distinct = (self._distinct and "DISTINCT " or "") |
243 | | |
244 | | if order_by: |
245 | | order_by_clause = " OVER (ORDER BY %s )" % (", ".join(order_by)) |
246 | | else: |
247 | | #Oracle's row_number() function always requires an order-by clause. |
248 | | #So we need to define a default order-by, since none was provided. |
249 | | order_by_clause = " OVER (ORDER BY %s.%s)" % \ |
250 | | (qn(opts.db_table), qn(opts.fields[0].db_column or opts.fields[0].column)) |
251 | | # limit_and_offset_clause |
252 | | if self._limit is None: |
253 | | assert self._offset is None, "'offset' is not allowed without 'limit'" |
254 | | |
255 | | if self._offset is not None: |
256 | | offset = int(self._offset) |
257 | | else: |
258 | | offset = 0 |
259 | | if self._limit is not None: |
260 | | limit = int(self._limit) |
261 | | else: |
262 | | limit = None |
263 | | |
264 | | limit_and_offset_clause = '' |
265 | | if limit is not None: |
266 | | limit_and_offset_clause = "WHERE rn > %s AND rn <= %s" % (offset, limit+offset) |
267 | | elif offset: |
268 | | limit_and_offset_clause = "WHERE rn > %s" % (offset) |
269 | | |
270 | | if len(limit_and_offset_clause) > 0: |
271 | | fmt = \ |
272 | | """SELECT * FROM |
273 | | (SELECT %s%s, |
274 | | ROW_NUMBER()%s AS rn |
275 | | %s) |
276 | | %s""" |
277 | | full_query = fmt % (distinct, select_clause, |
278 | | order_by_clause, ' '.join(sql).strip(), |
279 | | limit_and_offset_clause) |
280 | | else: |
281 | | full_query = None |
282 | | |
283 | | if get_full_query: |
284 | | return select, " ".join(sql), params, full_query |
285 | | else: |
286 | | return select, " ".join(sql), params |
287 | | |
| 100 | class OracleQuery(DefaultQuery): |
| 147 | def as_sql(self, with_limits=True): |
| 148 | """ |
| 149 | Creates the SQL for this query. Returns the SQL string and list of |
| 150 | parameters. This is overriden from the original Query class to |
| 151 | accommodate Oracle's limit/offset SQL. |
| 152 | |
| 153 | If 'with_limits' is False, any limit/offset information is not included |
| 154 | in the query. |
| 155 | """ |
| 156 | # The `do_offset` flag indicates whether we need to construct the |
| 157 | # SQL needed to use limit/offset w/Oracle. |
| 158 | do_offset = with_limits and (self.high_mark or self.low_mark) |
| 159 | |
| 160 | # If no offsets, just return the result of the base class `as_sql`. |
| 161 | if not do_offset: |
| 162 | return super(OracleQuery, self).as_sql(with_limits=False) |
| 163 | |
| 164 | # `get_columns` needs to be called before `get_ordering` to populate |
| 165 | # `_select_alias`. |
| 166 | self.pre_sql_setup() |
| 167 | out_cols = self.get_columns() |
| 168 | ordering = self.get_ordering() |
| 169 | |
| 170 | # Getting the "ORDER BY" SQL for the ROW_NUMBER() result. |
| 171 | if ordering: |
| 172 | rn_orderby = ', '.join(ordering) |
| 173 | else: |
| 174 | # Oracle's ROW_NUMBER() function always requires an order-by clause. |
| 175 | # So we need to define a default order-by, since none was provided. |
| 176 | qn = self.quote_name_unless_alias |
| 177 | opts = self.model._meta |
| 178 | rn_orderby = '%s.%s' % (qn(opts.db_table), qn(opts.fields[0].db_column or opts.fields[0].column)) |
| 179 | |
| 180 | # Getting the selection SQL and the params, which has the `rn` |
| 181 | # extra selection SQL; we pop `rn` after this completes so we do |
| 182 | # not get the attribute on the returned models. |
| 183 | self.extra_select['rn'] = 'ROW_NUMBER() OVER (ORDER BY %s )' % rn_orderby |
| 184 | sql, params= super(OracleQuery, self).as_sql(with_limits=False) |
| 185 | self.extra_select.pop('rn') |
| 186 | |
| 187 | # Constructing the result SQL, using the initial select SQL |
| 188 | # obtained above. |
| 189 | result = ['SELECT * FROM (%s)' % sql] |
| 190 | |
| 191 | # Place WHERE condition on `rn` for the desired range. |
| 192 | result.append('WHERE rn > %d' % self.low_mark) |
| 193 | if self.high_mark: |
| 194 | result.append('AND rn <= %d' % self.high_mark) |
| 195 | |
| 196 | # Returning the SQL w/params. |
| 197 | return ' '.join(result), params |
| 198 | |
| 199 | from django.db import connection |
| 200 | class OracleQuerySet(DefaultQuerySet): |
| 201 | "The OracleQuerySet is overriden to use OracleQuery." |
| 202 | def __init__(self, model=None, query=None): |
| 203 | super(OracleQuerySet, self).__init__(model=model, query=query) |
| 204 | self.query = query or OracleQuery(self.model, connection) |