Changeset 4078
- Timestamp:
- 11/16/06 10:25:59 (2 years ago)
- Files:
-
- django/branches/boulder-oracle-sprint/django/db/backends/oracle/base.py (modified) (3 diffs)
- django/branches/boulder-oracle-sprint/django/db/backends/oracle/creation.py (modified) (4 diffs)
- django/branches/boulder-oracle-sprint/django/db/backends/oracle/query.py (modified) (9 diffs)
- django/branches/boulder-oracle-sprint/django/db/models/query.py (modified) (7 diffs)
- django/branches/boulder-oracle-sprint/tests/modeltests/many_to_one/models.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/boulder-oracle-sprint/django/db/backends/oracle/base.py
r4066 r4078 70 70 This fixes it -- but note that if you want to use a literal "%s" in a query, 71 71 you'll need to use "%%s". 72 """ 72 """ 73 73 def _rewrite_args(self, query, params=None): 74 if params is None: 74 if params is None: 75 75 params = [] 76 76 args = [(':arg%d' % i) for i in range(len(params))] 77 77 query = query % tuple(args) 78 # cx_Oracle cannot execute a query with the closing ';' 78 # cx_Oracle cannot execute a query with the closing ';' 79 79 if query.endswith(';'): 80 80 query = query[:-1] 81 81 return query, params 82 82 83 83 def execute(self, query, params=None): 84 84 query, params = self._rewrite_args(query, params) … … 91 91 return Database.Cursor.executemany(self, query, params) 92 92 93 93 94 94 def quote_name(name): 95 # Oracle requires that quoted names be uppercase. 95 # Oracle requires that quoted names be uppercase. 96 96 name = name.upper() 97 97 if not name.startswith('"') and not name.endswith('"'): … … 144 144 OPERATOR_MAPPING = { 145 145 'exact': '= %s', 146 'iexact': 'LIKE %s',147 'contains': 'LIKE %s',148 'icontains': 'LIKE %s',146 'iexact': "LIKE %s ESCAPE '\\'", 147 'contains': "LIKE %s ESCAPE '\\'", 148 'icontains': "LIKE %s ESCAPE '\\'", 149 149 'gt': '> %s', 150 150 'gte': '>= %s', 151 151 'lt': '< %s', 152 152 'lte': '<= %s', 153 'startswith': 'LIKE %s',154 'endswith': 'LIKE %s',155 'istartswith': 'LIKE %s',156 'iendswith': 'LIKE %s',153 'startswith': "LIKE %s ESCAPE '\\'", 154 'endswith': "LIKE %s ESCAPE '\\'", 155 'istartswith': "LIKE %s ESCAPE '\\'", 156 'iendswith': "LIKE %s ESCAPE '\\'", 157 157 } django/branches/boulder-oracle-sprint/django/db/backends/oracle/creation.py
r4064 r4078 46 46 try: 47 47 _create_test_db(cursor, TEST_DATABASE_NAME, verbosity) 48 except Exception, e: 48 except Exception, e: 49 49 sys.stderr.write("Got an error creating the test database: %s\n" % e) 50 50 if not autoclobber: … … 64 64 print "Tests cancelled." 65 65 sys.exit(1) 66 66 67 67 connection.close() 68 68 settings.DATABASE_USER = TEST_DATABASE_NAME … … 72 72 # the side effect of initializing the test database. 73 73 cursor = connection.cursor() 74 74 75 75 def destroy_test_db(settings, connection, backend, old_database_name, verbosity=1): 76 76 if verbosity >= 1: … … 106 106 ] 107 107 _execute_statements(cursor, statements, dbname, verbosity) 108 108 109 109 def _destroy_test_db(cursor, dbname, verbosity): 110 110 if verbosity >= 2: django/branches/boulder-oracle-sprint/django/db/backends/oracle/query.py
r4064 r4078 23 23 place) for select_related queries. 24 24 """ 25 from django.db.models.fields import AutoField 25 from django.db.models.fields import AutoField 26 26 qn = backend.quote_name 27 27 for f in opts.fields: … … 37 37 where.append('%s.%s = %s.%s' % \ 38 38 (qn(old_prefix), qn(f.column), qn(db_table), qn(f.rel.get_related_field().column))) 39 select.extend(['%s.%s' % (backend.quote_name(db_table), backend.quote_name(f2.column)) for f2 in f.rel.to._meta.fields if not isinstance(f2, AutoField)]) 39 select.extend(['%s.%s' % (backend.quote_name(db_table), backend.quote_name(f2.column)) for f2 in f.rel.to._meta.fields if not isinstance(f2, AutoField)]) 40 40 fill_table_cache(f.rel.to._meta, select, tables, where, db_table, cache_tables_seen) 41 41 … … 43 43 def get_query_set_class(DefaultQuerySet): 44 44 "Create a custom QuerySet class for Oracle." 45 45 46 46 class OracleQuerySet(DefaultQuerySet): 47 47 48 48 def iterator(self): 49 49 "Performs the SELECT database lookup of this QuerySet." … … 54 54 55 55 full_query = None 56 56 57 select, sql, params, full_query = self._get_sql_clause() 57 58 if not full_query: … … 59 60 ((self._distinct and "DISTINCT " or ""), 60 61 ', '.join(select), sql) 61 62 62 63 cursor = connection.cursor() 63 cursor.execute(full_query, params) 64 cursor.execute(full_query, params) 64 65 65 66 fill_cache = self._select_related … … 89 90 def _get_sql_clause(self): 90 91 opts = self.model._meta 91 92 92 93 # Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z. 93 94 select = ["%s.%s" % (backend.quote_name(opts.db_table), backend.quote_name(f.column)) for f in opts.fields] … … 96 97 where = self._where[:] 97 98 params = self._params[:] 98 99 99 100 # Convert self._filters into SQL. 100 101 joins2, where2, params2 = self._filters.get_sql(opts) … … 102 103 where.extend(where2) 103 104 params.extend(params2) 104 105 105 106 # Add additional tables and WHERE clauses based on select_related. 106 107 if self._select_related: 107 108 fill_table_cache(opts, select, tables, where, opts.db_table, [opts.db_table]) 108 109 109 110 # Add any additional SELECTs. 110 111 if self._select: 111 112 select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()]) 112 113 113 114 # Start composing the body of the SQL statement. 114 115 sql = [" FROM", backend.quote_name(opts.db_table)] 115 116 116 117 # Compose the join dictionary into SQL describing the joins. 117 118 if joins: 118 119 sql.append(" ".join(["%s %s %s ON %s" % (join_type, table, alias, condition) 119 120 for (alias, (table, join_type, condition)) in joins.items()])) 120 121 121 122 # Compose the tables clause into SQL. 122 123 if tables: 123 124 sql.append(", " + ", ".join(tables)) 124 125 125 126 # Compose the where clause into SQL. 126 127 if where: 127 128 sql.append(where and "WHERE " + " AND ".join(where)) 128 129 129 130 # ORDER BY clause 130 131 order_by = [] … … 156 157 if order_by: 157 158 sql.append("ORDER BY " + ", ".join(order_by)) 158 159 159 160 # LIMIT and OFFSET clauses 160 # To support limits and offsets, Oracle requires some funky rewriting of an otherwise normal looking query. 161 select_clause = ",".join(select) 162 distinct = (self._distinct and "DISTINCT " or "") 163 164 if order_by: 165 order_by_clause = " OVER (ORDER BY %s )" % (", ".join(order_by)) 166 else: 167 #Oracle's row_number() function always requires an order-by clause. 168 #So we need to define a default order-by, since none was provided. 161 # To support limits and offsets, Oracle requires some funky rewriting of an otherwise normal looking query. 162 select_clause = ",".join(select) 163 distinct = (self._distinct and "DISTINCT " or "") 164 165 if order_by: 166 order_by_clause = " OVER (ORDER BY %s )" % (", ".join(order_by)) 167 else: 168 #Oracle's row_number() function always requires an order-by clause. 169 #So we need to define a default order-by, since none was provided. 169 170 order_by_clause = " OVER (ORDER BY %s.%s)" % \ 170 (backend.quote_name(opts.db_table), 171 backend.quote_name(opts.fields[0].db_column or opts.fields[0].column)) 172 # limit_and_offset_clause 173 offset = self._offset and int(self._offset) or 0 174 limit = self._limit and int(self._limit) or None 175 limit_and_offset_clause = '' 176 if limit: 177 limit_and_offset_clause = "WHERE rn > %s AND rn <= %s" % (offset, limit+offset) 171 (backend.quote_name(opts.db_table), 172 backend.quote_name(opts.fields[0].db_column or opts.fields[0].column)) 173 # limit_and_offset_clause 174 if self._limit is None: 175 assert self._offset is None, "'offset' is not allowed without 'limit'" 176 177 if self._offset is not None: 178 offset = int(self._offset) 179 else: 180 offset = 0 181 if self._limit is not None: 182 limit = int(self._limit) 183 else: 184 limit = None 185 186 limit_and_offset_clause = '' 187 if limit is not None: 188 limit_and_offset_clause = "WHERE rn > %s AND rn <= %s" % (offset, limit+offset) 178 189 elif offset: 179 limit_and_offset_clause = "WHERE rn > %s" % (offset) 180 181 if len(limit_and_offset_clause) > 0: 182 full_query = """SELECT * FROM 183 (SELECT %s 184 %s, 185 ROW_NUMBER() %s AS rn 186 %s 187 ) 188 %s 190 limit_and_offset_clause = "WHERE rn > %s" % (offset) 191 192 if len(limit_and_offset_clause) > 0: 193 full_query = """SELECT * FROM 194 (SELECT %s 195 %s, 196 ROW_NUMBER() %s AS rn 197 %s 198 ) 199 %s 189 200 """ % (distinct, select_clause, order_by_clause, " ".join(sql), limit_and_offset_clause) 190 201 else: 191 full_query = None 192 202 full_query = None 203 193 204 return select, " ".join(sql), params, full_query 194 205 195 206 return OracleQuerySet django/branches/boulder-oracle-sprint/django/db/models/query.py
r4066 r4078 170 170 171 171 cursor = connection.cursor() 172 173 select, sql, params, full_query = self._get_sql_clause() 174 cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 172 173 select, sql, params, full_query = self._get_sql_clause() 174 cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 175 175 176 176 fill_cache = self._select_related … … 519 519 else: 520 520 QuerySet = _QuerySet 521 521 522 522 class ValuesQuerySet(QuerySet): 523 523 def iterator(self): … … 568 568 cursor = connection.cursor() 569 569 cursor.execute(sql, params) 570 return [row[0] for row in cursor.fetchall()] 570 return [row[0] for row in cursor.fetchall()] 571 571 else: 572 572 sql = fmt % (date_trunc_sql, sql, 1, self._order_by) … … 661 661 if settings.DATABASE_ENGINE == 'oracle': 662 662 if lookup_type == 'icontains': 663 return 'lower(%s%s) %s' % (table_prefix, field_name, (backend.OPERATOR_MAPPING[lookup_type] % '%s')) 663 return 'lower(%s%s) %s' % (table_prefix, field_name, (backend.OPERATOR_MAPPING[lookup_type] % '%s')) 664 664 elif type(value) == datetime.datetime: 665 665 return "%s%s %s" % (table_prefix, field_name, 666 (backend.OPERATOR_MAPPING[lookup_type] % "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS')")) 666 (backend.OPERATOR_MAPPING[lookup_type] % "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS')")) 667 667 try: 668 668 return '%s%s %s' % (table_prefix, field_name, (backend.OPERATOR_MAPPING[lookup_type] % '%s')) … … 696 696 place) for select_related queries. 697 697 """ 698 from django.db.models.fields import AutoField 698 from django.db.models.fields import AutoField 699 699 qn = backend.quote_name 700 700 for f in opts.fields: … … 710 710 where.append('%s.%s = %s.%s' % \ 711 711 (qn(old_prefix), qn(f.column), qn(db_table), qn(f.rel.get_related_field().column))) 712 select.extend(['%s.%s' % (backend.quote_name(db_table), backend.quote_name(f2.column)) for f2 in f.rel.to._meta.fields]) 712 select.extend(['%s.%s' % (backend.quote_name(db_table), backend.quote_name(f2.column)) for f2 in f.rel.to._meta.fields]) 713 713 fill_table_cache(f.rel.to._meta, select, tables, where, db_table, cache_tables_seen) 714 714 … … 755 755 if len(path) < 1: 756 756 raise TypeError, "Cannot parse keyword query %r" % kwarg 757 757 758 758 if value is None: 759 759 # Interpret '__exact=None' as the sql '= NULL'; otherwise, reject django/branches/boulder-oracle-sprint/tests/modeltests/many_to_one/models.py
r3661 r4078 147 147 # The underlying query only makes one join when a related table is referenced twice. 148 148 >>> query = Article.objects.filter(reporter__first_name__exact='John', reporter__last_name__exact='Smith') 149 >>> null, sql, null = query._get_sql_clause()149 >>> null, sql, null, null = query._get_sql_clause() 150 150 >>> sql.count('INNER JOIN') 151 151 1 … … 156 156 157 157 # Find all Articles for the Reporter whose ID is 1. 158 # Use direct ID check, pk check, and object comparison 158 # Use direct ID check, pk check, and object comparison 159 159 >>> Article.objects.filter(reporter__id__exact=1) 160 160 [<Article: John's second story>, <Article: This is a test>]
