Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed Oracle backend limit/offset SQL to again use extra_select, prop…

…erly this time. This cleans up a test case failure, and hopefully gets contrib.gis working again.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@8471 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 6e36ce142951b846e30fcac2e823162f2eeb1bd5 1 parent 1b8b968
Matt Boersma authored August 22, 2008

Showing 1 changed file with 38 additions and 29 deletions. Show diff stats Hide diff stats

  1. 67  django/db/backends/oracle/query.py
67  django/db/backends/oracle/query.py
@@ -82,55 +82,64 @@ def as_sql(self, with_limits=True, with_col_aliases=False):
82 82
             """
83 83
             Creates the SQL for this query. Returns the SQL string and list
84 84
             of parameters.  This is overriden from the original Query class
85  
-            to accommodate Oracle's limit/offset SQL.
  85
+            to handle the additional SQL Oracle requires to emulate LIMIT
  86
+            and OFFSET.
86 87
 
87 88
             If 'with_limits' is False, any limit/offset information is not
88 89
             included in the query.
89 90
             """
90 91
 
91 92
             # The `do_offset` flag indicates whether we need to construct
92  
-            # the SQL needed to use limit/offset w/Oracle.
  93
+            # the SQL needed to use limit/offset with Oracle.
93 94
             do_offset = with_limits and (self.high_mark is not None
94 95
                                          or self.low_mark)
95  
-            sql, params = super(OracleQuery, self).as_sql(with_limits=False,
96  
-                                with_col_aliases=with_col_aliases or do_offset)
97  
-            if do_offset:
98  
-                # Get the "ORDER BY" SQL for the ROW_NUMBER() result.
  96
+            if not do_offset:
  97
+                sql, params = super(OracleQuery, self).as_sql(with_limits=False,
  98
+                        with_col_aliases=with_col_aliases)
  99
+            else:
  100
+                # `get_columns` needs to be called before `get_ordering` to
  101
+                # populate `_select_alias`.
  102
+                self.pre_sql_setup()
  103
+                self.get_columns()
99 104
                 ordering = self.get_ordering()
  105
+
  106
+                # Oracle's ROW_NUMBER() function requires an ORDER BY clause.
100 107
                 if ordering:
101 108
                     rn_orderby = ', '.join(ordering)
102 109
                 else:
103  
-                    # Oracle's ROW_NUMBER() function always requires an
104  
-                    # order-by clause.  So we need to define a default
105  
-                    # order-by, since none was provided.
  110
+                    # Create a default ORDER BY since none was specified.
106 111
                     qn = self.quote_name_unless_alias
107 112
                     opts = self.model._meta
108 113
                     rn_orderby = '%s.%s' % (qn(opts.db_table),
109 114
                         qn(opts.fields[0].db_column or opts.fields[0].column))
110 115
 
111  
-                # Collect all the selected column names or aliases.
112  
-                outer_cols = []
113  
-                for col in self.get_columns(True):
114  
-                    if ' AS ' in col:
115  
-                        outer_cols.append(col.split(' AS ', 1)[1])
116  
-                    else:
117  
-                        outer_cols.append(col.rsplit('.', 1)[1])
118  
-
119  
-                # Rewrite the original SQL query to select ROW_NUMBER() and involve
120  
-                # it in the WHERE clause, then wrap everything in an outer SELECT
121  
-                # statement that omits the "rn" column.  This is the canonical way
122  
-                # to emulate LIMIT and OFFSET on Oracle.
123  
-                sql = 'SELECT ROW_NUMBER() OVER (ORDER BY %s) rn, %s' % (rn_orderby, sql[7:])
124  
-                result = ['SELECT %s FROM (%s)' % (', '.join(outer_cols), sql)]
125  
-
126  
-                # Place WHERE condition on `rn` for the desired range.
127  
-                result.append('WHERE rn > %d' % self.low_mark)
128  
-                if self.high_mark is not None:
129  
-                    result.append('AND rn <= %d' % self.high_mark)
  116
+                # Ensure the base query SELECTs our special "_RN" column
  117
+                self.extra_select['_RN'] = ('ROW_NUMBER() OVER (ORDER BY %s)'
  118
+                                            % rn_orderby, '')
  119
+                sql, params = super(OracleQuery, self).as_sql(with_limits=False,
  120
+                                                        with_col_aliases=True)
130 121
 
131  
-                sql = ' '.join(result)
  122
+                # Wrap the base query in an outer SELECT * with boundaries on
  123
+                # the "_RN" column.  This is the canonical way to emulate LIMIT
  124
+                # and OFFSET on Oracle.
  125
+                sql = 'SELECT * FROM (%s) WHERE "_RN" > %d' % (sql, self.low_mark)
  126
+                if self.high_mark is not None:
  127
+                    sql = '%s AND "_RN" <= %d' % (sql, self.high_mark)
132 128
 
133 129
             return sql, params
134 130
 
  131
+        def set_limits(self, low=None, high=None):
  132
+            super(OracleQuery, self).set_limits(low, high)
  133
+            # We need to select the row number for the LIMIT/OFFSET sql.
  134
+            # A placeholder is added to extra_select now, because as_sql is
  135
+            # too late to be modifying extra_select.  However, the actual sql
  136
+            # depends on the ordering, so that is generated in as_sql.
  137
+            self.extra_select['_RN'] = ('1', '')
  138
+
  139
+        def clear_limits(self):
  140
+            super(OracleQuery, self).clear_limits()
  141
+            if '_RN' in self.extra_select:
  142
+                del self.extra_select['_RN']
  143
+
135 144
     _classes[QueryClass] = OracleQuery
136 145
     return OracleQuery

0 notes on commit 6e36ce1

Please sign in to comment.
Something went wrong with that request. Please try again.