Changeset 5519
- Timestamp:
- 06/23/07 09:16:00 (1 year ago)
- Files:
-
- django/trunk/django/contrib/admin/models.py (modified) (1 diff)
- django/trunk/django/core/management.py (modified) (26 diffs)
- django/trunk/django/db/backends/ado_mssql/base.py (modified) (3 diffs)
- django/trunk/django/db/backends/dummy/base.py (modified) (1 diff)
- django/trunk/django/db/backends/mysql/base.py (modified) (4 diffs)
- django/trunk/django/db/backends/mysql_old/base.py (modified) (4 diffs)
- django/trunk/django/db/backends/oracle/base.py (modified) (9 diffs)
- django/trunk/django/db/backends/oracle/client.py (modified) (1 diff)
- django/trunk/django/db/backends/oracle/creation.py (modified) (1 diff)
- django/trunk/django/db/backends/oracle/introspection.py (modified) (4 diffs)
- django/trunk/django/db/backends/postgresql/base.py (modified) (8 diffs)
- django/trunk/django/db/backends/postgresql_psycopg2/base.py (modified) (6 diffs)
- django/trunk/django/db/backends/sqlite3/base.py (modified) (4 diffs)
- django/trunk/django/db/backends/util.py (modified) (2 diffs)
- django/trunk/django/db/models/base.py (modified) (5 diffs)
- django/trunk/django/db/models/fields/__init__.py (modified) (7 diffs)
- django/trunk/django/db/models/fields/related.py (modified) (1 diff)
- django/trunk/django/db/models/options.py (modified) (5 diffs)
- django/trunk/django/db/models/query.py (modified) (12 diffs)
- django/trunk/django/newforms/widgets.py (modified) (1 diff)
- django/trunk/django/test/utils.py (modified) (3 diffs)
- django/trunk/docs/faq.txt (modified) (2 diffs)
- django/trunk/docs/install.txt (modified) (2 diffs)
- django/trunk/docs/model-api.txt (modified) (3 diffs)
- django/trunk/docs/settings.txt (modified) (1 diff)
- django/trunk/tests/regressiontests/datatypes (copied) (copied from django/branches/boulder-oracle-sprint/tests/regressiontests/datatypes)
- django/trunk/tests/regressiontests/datatypes/__init__.py (copied) (copied from django/branches/boulder-oracle-sprint/tests/regressiontests/datatypes/__init__.py)
- django/trunk/tests/regressiontests/datatypes/models.py (copied) (copied from django/branches/boulder-oracle-sprint/tests/regressiontests/datatypes/models.py) (3 diffs)
- django/trunk/tests/regressiontests/serializers_regress/tests.py (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/trunk/django/contrib/admin/models.py
r4265 r5519 10 10 class LogEntryManager(models.Manager): 11 11 def log_action(self, user_id, content_type_id, object_id, object_repr, action_flag, change_message=''): 12 e = self.model(None, None, user_id, content_type_id, object_id, object_repr[:200], action_flag, change_message)12 e = self.model(None, None, user_id, content_type_id, str(object_id), object_repr[:200], action_flag, change_message) 13 13 e.save() 14 14 django/trunk/django/core/management.py
r5514 r5519 60 60 def _get_installed_models(table_list): 61 61 "Gets a set of all models that are installed, given a list of existing tables" 62 from django.db import models62 from django.db import backend, models 63 63 all_models = [] 64 64 for app in models.get_apps(): 65 65 for model in models.get_models(app): 66 66 all_models.append(model) 67 return set([m for m in all_models if m._meta.db_table in table_list]) 67 if backend.uses_case_insensitive_names: 68 converter = str.upper 69 else: 70 converter = lambda x: x 71 return set([m for m in all_models if converter(m._meta.db_table) in map(converter, table_list)]) 68 72 69 73 def _get_table_list(): … … 101 105 "Returns a list of the CREATE TABLE SQL statements for the given app." 102 106 from django.db import get_creation_module, models 107 103 108 data_types = get_creation_module().DATA_TYPES 104 109 … … 172 177 data_type = f.get_internal_type() 173 178 col_type = data_types[data_type] 179 tablespace = f.db_tablespace or opts.db_tablespace 174 180 if col_type is not None: 175 181 # Make the definition (e.g. 'foo VARCHAR(30)') for this field. … … 177 183 style.SQL_COLTYPE(col_type % rel_field.__dict__)] 178 184 field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))) 179 if f.unique :185 if f.unique and (not f.primary_key or backend.allows_unique_and_pk): 180 186 field_output.append(style.SQL_KEYWORD('UNIQUE')) 181 187 if f.primary_key: 182 188 field_output.append(style.SQL_KEYWORD('PRIMARY KEY')) 189 if tablespace and backend.supports_tablespaces and (f.unique or f.primary_key) and backend.autoindexes_primary_keys: 190 # We must specify the index tablespace inline, because we 191 # won't be generating a CREATE INDEX statement for this field. 192 field_output.append(backend.get_tablespace_sql(tablespace, inline=True)) 183 193 if f.rel: 184 194 if f.rel.to in known_models: … … 204 214 for i, line in enumerate(table_output): # Combine and add commas. 205 215 full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or '')) 206 full_statement.append(');') 216 full_statement.append(')') 217 if opts.db_tablespace and backend.supports_tablespaces: 218 full_statement.append(backend.get_tablespace_sql(opts.db_tablespace)) 219 full_statement.append(';') 207 220 final_output.append('\n'.join(full_statement)) 221 222 if opts.has_auto_field and hasattr(backend, 'get_autoinc_sql'): 223 # Add any extra SQL needed to support auto-incrementing primary keys 224 autoinc_sql = backend.get_autoinc_sql(opts.db_table) 225 if autoinc_sql: 226 for stmt in autoinc_sql: 227 final_output.append(stmt) 208 228 209 229 return final_output, pending_references … … 214 234 """ 215 235 from django.db import backend, get_creation_module 236 from django.db.backends.util import truncate_name 216 237 data_types = get_creation_module().DATA_TYPES 217 238 … … 230 251 r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table)))) 231 252 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \ 232 (backend.quote_name(r_table), r_name,253 (backend.quote_name(r_table), truncate_name(r_name, backend.get_max_name_length()), 233 254 backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col), 234 255 backend.get_deferrable_sql())) … … 246 267 for f in opts.many_to_many: 247 268 if not isinstance(f.rel, generic.GenericRel): 269 tablespace = f.db_tablespace or opts.db_tablespace 270 if tablespace and backend.supports_tablespaces and backend.autoindexes_primary_keys: 271 tablespace_sql = ' ' + backend.get_tablespace_sql(tablespace, inline=True) 272 else: 273 tablespace_sql = '' 248 274 table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \ 249 275 style.SQL_TABLE(backend.quote_name(f.m2m_db_table())) + ' ('] 250 table_output.append(' %s %s %s ,' % \276 table_output.append(' %s %s %s%s,' % \ 251 277 (style.SQL_FIELD(backend.quote_name('id')), 252 278 style.SQL_COLTYPE(data_types['AutoField']), 253 style.SQL_KEYWORD('NOT NULL PRIMARY KEY'))) 279 style.SQL_KEYWORD('NOT NULL PRIMARY KEY'), 280 tablespace_sql)) 254 281 table_output.append(' %s %s %s %s (%s)%s,' % \ 255 282 (style.SQL_FIELD(backend.quote_name(f.m2m_column_name())), … … 266 293 style.SQL_FIELD(backend.quote_name(f.rel.to._meta.pk.column)), 267 294 backend.get_deferrable_sql())) 268 table_output.append(' %s (%s, %s) ' % \295 table_output.append(' %s (%s, %s)%s' % \ 269 296 (style.SQL_KEYWORD('UNIQUE'), 270 297 style.SQL_FIELD(backend.quote_name(f.m2m_column_name())), 271 style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())))) 272 table_output.append(');') 298 style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())), 299 tablespace_sql)) 300 table_output.append(')') 301 if opts.db_tablespace and backend.supports_tablespaces: 302 # f.db_tablespace is only for indices, so ignore its value here. 303 table_output.append(backend.get_tablespace_sql(opts.db_tablespace)) 304 table_output.append(';') 273 305 final_output.append('\n'.join(table_output)) 306 307 # Add any extra SQL needed to support auto-incrementing PKs 308 autoinc_sql = backend.get_autoinc_sql(f.m2m_db_table()) 309 if autoinc_sql: 310 for stmt in autoinc_sql: 311 final_output.append(stmt) 312 274 313 return final_output 275 314 … … 277 316 "Returns a list of the DROP TABLE SQL statements for the given app." 278 317 from django.db import backend, connection, models, get_introspection_module 318 from django.db.backends.util import truncate_name 279 319 introspection = get_introspection_module() 280 320 … … 290 330 else: 291 331 table_names = [] 332 if backend.uses_case_insensitive_names: 333 table_name_converter = str.upper 334 else: 335 table_name_converter = lambda x: x 292 336 293 337 output = [] … … 299 343 app_models = models.get_models(app) 300 344 for model in app_models: 301 if cursor and model._meta.db_tablein table_names:345 if cursor and table_name_converter(model._meta.db_table) in table_names: 302 346 # The table exists, so it needs to be dropped 303 347 opts = model._meta … … 309 353 310 354 for model in app_models: 311 if cursor and model._meta.db_tablein table_names:355 if cursor and table_name_converter(model._meta.db_table) in table_names: 312 356 # Drop the table now 313 357 output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'), … … 319 363 r_table = model._meta.db_table 320 364 r_col = model._meta.get_field(f.rel.field_name).column 365 r_name = '%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table)))) 321 366 output.append('%s %s %s %s;' % \ 322 367 (style.SQL_KEYWORD('ALTER TABLE'), 323 368 style.SQL_TABLE(backend.quote_name(table)), 324 369 style.SQL_KEYWORD(backend.get_drop_foreignkey_sql()), 325 style.SQL_FIELD( backend.quote_name('%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table))))))))370 style.SQL_FIELD(truncate_name(r_name, backend.get_max_name_length())))) 326 371 del references_to_delete[model] 372 if model._meta.has_auto_field and hasattr(backend, 'get_drop_sequence'): 373 output.append(backend.get_drop_sequence(model._meta.db_table)) 327 374 328 375 # Output DROP TABLE statements for many-to-many tables. … … 330 377 opts = model._meta 331 378 for f in opts.many_to_many: 332 if cursor and f.m2m_db_table() in table_names:379 if cursor and table_name_converter(f.m2m_db_table()) in table_names: 333 380 output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'), 334 381 style.SQL_TABLE(backend.quote_name(f.m2m_db_table())))) 382 if hasattr(backend, 'get_drop_sequence'): 383 output.append(backend.get_drop_sequence("%s_%s" % (model._meta.db_table, f.column))) 384 335 385 336 386 app_label = app_models[0]._meta.app_label … … 431 481 432 482 for f in model._meta.fields: 433 if f.db_index :483 if f.db_index and not ((f.primary_key or f.unique) and backend.autoindexes_primary_keys): 434 484 unique = f.unique and 'UNIQUE ' or '' 485 tablespace = f.db_tablespace or model._meta.db_tablespace 486 if tablespace and backend.supports_tablespaces: 487 tablespace_sql = ' ' + backend.get_tablespace_sql(tablespace) 488 else: 489 tablespace_sql = '' 435 490 output.append( 436 491 style.SQL_KEYWORD('CREATE %sINDEX' % unique) + ' ' + \ … … 438 493 style.SQL_KEYWORD('ON') + ' ' + \ 439 494 style.SQL_TABLE(backend.quote_name(model._meta.db_table)) + ' ' + \ 440 "(%s);" % style.SQL_FIELD(backend.quote_name(f.column)) 495 "(%s)" % style.SQL_FIELD(backend.quote_name(f.column)) + \ 496 "%s;" % tablespace_sql 441 497 ) 442 498 return output … … 462 518 def syncdb(verbosity=1, interactive=True): 463 519 "Creates the database tables for all apps in INSTALLED_APPS whose tables haven't already been created." 464 from django.db import connection, transaction, models, get_creation_module520 from django.db import backend, connection, transaction, models, get_creation_module 465 521 from django.conf import settings 466 522 … … 485 541 # so we know what needs to be added. 486 542 table_list = _get_table_list() 543 if backend.uses_case_insensitive_names: 544 table_name_converter = str.upper 545 else: 546 table_name_converter = lambda x: x 487 547 488 548 # Get a list of already installed *models* so that references work right. … … 499 559 if verbosity >= 2: 500 560 print "Processing %s.%s model" % (app_name, model._meta.object_name) 501 if model._meta.db_tablein table_list:561 if table_name_converter(model._meta.db_table) in table_list: 502 562 continue 503 563 sql, references = _get_sql_model_create(model, seen_models) … … 511 571 for statement in sql: 512 572 cursor.execute(statement) 513 table_list.append( model._meta.db_table)573 table_list.append(table_name_converter(model._meta.db_table)) 514 574 515 575 # Create the m2m tables. This must be done after all tables have been created … … 830 890 indexes = {} 831 891 for i, row in enumerate(introspection_module.get_table_description(cursor, table_name)): 832 att_name = row[0] 892 att_name = row[0].lower() 833 893 comment_notes = [] # Holds Field notes, to be displayed in a Python comment. 834 894 extra_params = {} # Holds Field parameters such as 'db_column'. … … 1323 1383 count = [0,0] 1324 1384 models = set() 1325 1385 1326 1386 humanize = lambda dirname: dirname and "'%s'" % dirname or 'absolute path' 1327 1387 … … 1401 1461 print "No %s fixture '%s' in %s." % \ 1402 1462 (format, fixture_name, humanize(fixture_dir)) 1403 1463 1404 1464 if count[0] > 0: 1405 1465 sequence_sql = backend.get_sql_sequence_reset(style, models) … … 1409 1469 for line in sequence_sql: 1410 1470 cursor.execute(line) 1411 1471 1412 1472 transaction.commit() 1413 1473 transaction.leave_transaction_management() 1414 1474 1415 1475 if count[0] == 0: 1416 1476 if verbosity > 0: … … 1627 1687 parser.print_usage_and_exit() 1628 1688 if action not in NO_SQL_TRANSACTION: 1629 print style.SQL_KEYWORD("BEGIN;") 1689 from django.db import backend 1690 if backend.get_start_transaction_sql(): 1691 print style.SQL_KEYWORD(backend.get_start_transaction_sql()) 1630 1692 for mod in mod_list: 1631 1693 if action == 'reset': django/trunk/django/db/backends/ado_mssql/base.py
r5076 r5519 90 90 self.connection = None 91 91 92 allows_group_by_ordinal = True 93 allows_unique_and_pk = True 94 autoindexes_primary_keys = True 95 needs_datetime_string_cast = True 96 needs_upper_for_iops = False 92 97 supports_constraints = True 98 supports_tablespaces = True 99 uses_case_insensitive_names = False 93 100 94 101 def quote_name(name): … … 118 125 return "Convert(datetime, Convert(varchar(12), %s))" % field_name 119 126 127 def get_datetime_cast_sql(): 128 return None 129 120 130 def get_limit_offset_sql(limit, offset=None): 121 131 # TODO: This is a guess. Make sure this is correct. … … 139 149 def get_pk_default_value(): 140 150 return "DEFAULT" 151 152 def get_max_name_length(): 153 return None 154 155 def get_start_transaction_sql(): 156 return "BEGIN;" 157 158 def get_tablespace_sql(tablespace, inline=False): 159 return "ON %s" % quote_name(tablespace) 160 161 def get_autoinc_sql(table): 162 return None 141 163 142 164 def get_sql_flush(style, tables, sequences): django/trunk/django/db/backends/dummy/base.py
r5410 r5519 34 34 35 35 supports_constraints = False 36 supports_tablespaces = False 36 37 quote_name = complain 37 38 dictfetchone = complain django/trunk/django/db/backends/mysql/base.py
r5302 r5519 135 135 return self.server_version 136 136 137 allows_group_by_ordinal = True 138 allows_unique_and_pk = True 139 autoindexes_primary_keys = False 140 needs_datetime_string_cast = True # MySQLdb requires a typecast for dates 141 needs_upper_for_iops = False 137 142 supports_constraints = True 143 supports_tablespaces = False 144 uses_case_insensitive_names = False 138 145 139 146 def quote_name(name): … … 168 175 return sql 169 176 177 def get_datetime_cast_sql(): 178 return None 179 170 180 def get_limit_offset_sql(limit, offset=None): 171 181 sql = "LIMIT " … … 189 199 return "DEFAULT" 190 200 201 def get_max_name_length(): 202 return None; 203 204 def get_start_transaction_sql(): 205 return "BEGIN;" 206 207 def get_autoinc_sql(table): 208 return None 209 191 210 def get_sql_flush(style, tables, sequences): 192 211 """Return a list of SQL statements required to remove all data from 193 212 all tables in the database (without actually removing the tables 194 213 themselves) and put the database in an empty 'initial' state 195 214 196 215 """ 197 216 # NB: The generated SQL below is specific to MySQL … … 205 224 ) for table in tables] + \ 206 225 ['SET FOREIGN_KEY_CHECKS = 1;'] 207 226 208 227 # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements 209 228 # to reset sequence indices django/trunk/django/db/backends/mysql_old/base.py
r5313 r5519 136 136 return self.server_version 137 137 138 allows_group_by_ordinal = True 139 allows_unique_and_pk = True 140 autoindexes_primary_keys = False 141 needs_datetime_string_cast = True # MySQLdb requires a typecast for dates 142 needs_upper_for_iops = False 138 143 supports_constraints = True 144 supports_tablespaces = False 145 uses_case_insensitive_names = False 139 146 140 147 def quote_name(name): … … 169 176 return sql 170 177 178 def get_datetime_cast_sql(): 179 return None 180 171 181 def get_limit_offset_sql(limit, offset=None): 172 182 sql = "LIMIT " … … 190 200 return "DEFAULT" 191 201 202 def get_max_name_length(): 203 return None; 204 205 def get_start_transaction_sql(): 206 return "BEGIN;" 207 208 def get_autoinc_sql(table): 209 return None 210 192 211 def get_sql_flush(style, tables, sequences): 193 212 """Return a list of SQL statements required to remove all data from 194 213 all tables in the database (without actually removing the tables 195 214 themselves) and put the database in an empty 'initial' state 196 215 197 216 """ 198 217 # NB: The generated SQL below is specific to MySQL … … 206 225 ) for table in tables] + \ 207 226 ['SET FOREIGN_KEY_CHECKS = 1;'] 208 227 209 228 # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements 210 229 # to reset sequence indices django/trunk/django/db/backends/oracle/base.py
r5076 r5519 5 5 """ 6 6 7 from django.conf import settings 7 8 from django.db.backends import util 8 9 try: … … 11 12 from django.core.exceptions import ImproperlyConfigured 12 13 raise ImproperlyConfigured, "Error loading cx_Oracle module: %s" % e 14 import datetime 15 from django.utils.datastructures import SortedDict 16 13 17 14 18 DatabaseError = Database.Error … … 32 36 33 37 def cursor(self): 34 from django.conf import settings35 38 if not self._valid_connection(): 36 39 if len(settings.DATABASE_HOST.strip()) == 0: … … 42 45 conn_string = "%s/%s@%s" % (settings.DATABASE_USER, settings.DATABASE_PASSWORD, settings.DATABASE_NAME) 43 46 self.connection = Database.connect(conn_string, **self.options) 44 return FormatStylePlaceholderCursor(self.connection) 47 cursor = FormatStylePlaceholderCursor(self.connection) 48 # default arraysize of 1 is highly sub-optimal 49 cursor.arraysize = 100 50 # set oracle date to ansi date format 51 cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD'") 52 cursor.execute("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'") 53 if settings.DEBUG: 54 return util.CursorDebugWrapper(cursor, self) 55 return cursor 45 56 46 57 def _commit(self): 47 58 if self.connection is not None: 48 self.connection.commit()59 return self.connection.commit() 49 60 50 61 def _rollback(self): 51 62 if self.connection is not None: 52 try: 53 self.connection.rollback() 54 except Database.NotSupportedError: 55 pass 63 return self.connection.rollback() 56 64 57 65 def close(self): … … 60 68 self.connection = None 61 69 70 allows_group_by_ordinal = False 71 allows_unique_and_pk = False # Suppress UNIQUE/PK for Oracle (ORA-02259) 72 autoindexes_primary_keys = True 73 needs_datetime_string_cast = False 74 needs_upper_for_iops = True 62 75 supports_constraints = True 76 supports_tablespaces = True 77 uses_case_insensitive_names = True 63 78 64 79 class FormatStylePlaceholderCursor(Database.Cursor): … … 68 83 you'll need to use "%%s". 69 84 """ 85 def _rewrite_args(self, query, params=None): 86 if params is None: 87 params = [] 88 else: 89 # cx_Oracle can't handle unicode parameters, so cast to str for now 90 for i, param in enumerate(params): 91 if type(param) == unicode: 92 try: 93 params[i] = param.encode('utf-8') 94 except UnicodeError: 95 params[i] = str(param) 96 args = [(':arg%d' % i) for i in range(len(params))] 97 query = query % tuple(args) 98 # cx_Oracle wants no trailing ';' for SQL statements. For PL/SQL, it 99 # it does want a trailing ';' but not a trailing '/'. However, these 100 # characters must be included in the original query in case the query 101 # is being passed to SQL*Plus. 102 if query.endswith(';') or query.endswith('/'): 103 query = query[:-1] 104 return query, params 105 70 106 def execute(self, query, params=None): 71 if params is None: params = [] 72 query = self.convert_arguments(query, len(params)) 107 query, params = self._rewrite_args(query, params) 73 108 return Database.Cursor.execute(self, query, params) 74 109 75 110 def executemany(self, query, params=None): 76 if params is None: params = [] 77 query = self.convert_arguments(query, len(params[0])) 111 query, params = self._rewrite_args(query, params) 78 112 return Database.Cursor.executemany(self, query, params) 79 113 80 def convert_arguments(self, query, num_params):81 # replace occurances of "%s" with ":arg" - Oracle requires colons for parameter placeholders.82 args = [':arg' for i in range(num_params)]83 return query % tuple(args)84 85 114 def quote_name(name): 86 return name 115 # SQL92 requires delimited (quoted) names to be case-sensitive. When 116 # not quoted, Oracle has case-insensitive behavior for identifiers, but 117 # always defaults to uppercase. 118 # We simplify things by making Oracle identifiers always uppercase. 119 if not name.startswith('"') and not name.endswith('"'): 120 name = '"%s"' % util.truncate_name(name.upper(), get_max_name_length()) 121 return name.upper() 87 122 88 123 dictfetchone = util.dictfetchone … … 91 126 92 127 def get_last_insert_id(cursor, table_name, pk_name): 93 query = "SELECT %s_sq.currval from dual" % table_name94 cursor.execute( query)128 sq_name = util.truncate_name(table_name, get_max_name_length()-3) 129 cursor.execute('SELECT %s_sq.currval FROM dual' % sq_name) 95 130 return cursor.fetchone()[0] 96 131 97 132 def get_date_extract_sql(lookup_type, table_name): 98 133 # lookup_type is 'year', 'month', 'day' 99 # http:// www.psoug.org/reference/date_func.html134 # http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions42a.htm#1017163 100 135 return "EXTRACT(%s FROM %s)" % (lookup_type, table_name) 101 136 102 137 def get_date_trunc_sql(lookup_type, field_name): 103 return "EXTRACT(%s FROM TRUNC(%s))" % (lookup_type, field_name) 138 # lookup_type is 'year', 'month', 'day' 139 # Oracle uses TRUNC() for both dates and numbers. 140 # http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions155a.htm#SQLRF06151 141 if lookup_type == 'day': 142 sql = 'TRUNC(%s)' % (field_name,) 143 else: 144 sql = "TRUNC(%s, '%s')" % (field_name, lookup_type) 145 return sql 146 147 def get_datetime_cast_sql(): 148 return "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS.FF')" 104 149 105 150 def get_limit_offset_sql(limit, offset=None): 106 151 # Limits and offset are too complicated to be handled here. 107 # Instead, they are handled in django/db/ query.py.108 pass152 # Instead, they are handled in django/db/backends/oracle/query.py. 153 return "" 109 154 110 155 def get_random_function_sql(): … … 118 163 119 164 def get_drop_foreignkey_sql(): 120 return "DROP FOREIGN KEY"165 return "DROP CONSTRAINT" 121 166 122 167 def get_pk_default_value(): 123 168 return "DEFAULT" 169 170 def get_max_name_length(): 171 return 30 172 173 def get_start_transaction_sql(): 174 return None 175 176 def get_tablespace_sql(tablespace, inline=False): 177 return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), quote_name(tablespace)) 178 179 def get_autoinc_sql(table): 180 # To simulate auto-incrementing primary keys in Oracle, we have to 181 # create a sequence and a trigger. 182 sq_name = get_sequence_name(table) 183 tr_name = get_trigger_name(table) 184 sequence_sql = 'CREATE SEQUENCE %s;' % sq_name 185 trigger_sql = """CREATE OR REPLACE TRIGGER %s 186 BEFORE INSERT ON %s 187 FOR EACH ROW 188 WHEN (new.id IS NULL) 189 BEGIN 190 SELECT %s.nextval INTO :new.id FROM dual; 191 END; 192 /""" % (tr_name, quote_name(table), sq_name) 193 return sequence_sql, trigger_sql 194 195 def get_drop_sequence(table): 196 return "DROP SEQUENCE %s;" % quote_name(get_sequence_name(table)) 197 198 def _get_sequence_reset_sql(): 199 # TODO: colorize this SQL code with style.SQL_KEYWORD(), etc. 200 return """ 201 DECLARE 202 startvalue integer; 203 cval integer; 204 BEGIN 205 LOCK TABLE %(table)s IN SHARE MODE; 206 SELECT NVL(MAX(id), 0) INTO startvalue FROM %(table)s; 207 SELECT %(sequence)s.nextval INTO cval FROM dual; 208 cval := startvalue - cval; 209 IF cval != 0 THEN 210 EXECUTE IMMEDIATE 'ALTER SEQUENCE %(sequence)s MINVALUE 0 INCREMENT BY '||cval; 211 SELECT %(sequence)s.nextval INTO cval FROM dual; 212 EXECUTE IMMEDIATE 'ALTER SEQUENCE %(sequence)s INCREMENT BY 1'; 213 END IF; 214 COMMIT; 215 END; 216 /""" 124 217 125 218 def get_sql_flush(style, tables, sequences): … … 128 221 themselves) and put the database in an empty 'initial' state 129 222 """ 130 # Return a list of 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements 131 # TODO - SQL not actually tested against Oracle yet! 132 # TODO - autoincrement indices reset required? See other get_sql_flush() implementations 133 sql = ['%s %s;' % \ 134 (style.SQL_KEYWORD('TRUNCATE'), 135 style.SQL_FIELD(quote_name(table)) 136 ) for table in tables] 223 # Return a list of 'TRUNCATE x;', 'TRUNCATE y;', 224 # 'TRUNCATE z;'... style SQL statements 225 if tables: 226 # Oracle does support TRUNCATE, but it seems to get us into 227 # FK referential trouble, whereas DELETE FROM table works. 228 sql = ['%s %s %s;' % \ 229 (style.SQL_KEYWORD('DELETE'), 230 style.SQL_KEYWORD('FROM'), 231 style.SQL_FIELD(quote_name(table)) 232 ) for table in tables] 233 # Since we've just deleted all the rows, running our sequence 234 # ALTER code will reset the sequence to 0. 235 for sequence_info in sequences: 236 table_name = sequence_info['table'] 237 seq_name = get_sequence_name(table_name) 238 query = _get_sequence_reset_sql() % {'sequence':seq_name, 239 'table':quote_name(table_name)} 240 sql.append(query) 241 return sql 242 else: 243 return [] 244 245 def get_sequence_name(table): 246 name_length = get_max_name_length() - 3 247 return '%s_SQ' % util.truncate_name(table, name_length).upper() 137 248 138 249 def get_sql_sequence_reset(style, model_list): 139 250 "Returns a list of the SQL statements to reset sequences for the given models." 140 # No sequence reset required 141 return [] 251 from django.db import models 252 output = [] 253 query = _get_sequence_reset_sql() 254 for model in model_list: 255 for f in model._meta.fields: 256 if isinstance(f, models.AutoField): 257 sequence_name = get_sequence_name(model._meta.db_table) 258 output.append(query % {'sequence':sequence_name, 259 'table':model._meta.db_table}) 260 break # Only one AutoField is allowed per model, so don't bother continuing. 261 for f in model._meta.many_to_many: 262 sequence_name = get_sequence_name(f.m2m_db_table()) 263 output.append(query % {'sequence':sequence_name, 264 'table':f.m2m_db_table()}) 265 return output 266 267 def get_trigger_name(table): 268 name_length = get_max_name_length() - 3 269 return '%s_TR' % util.truncate_name(table, name_length).upper() 270 271 def get_query_set_class(DefaultQuerySet): 272 "Create a custom QuerySet class for Oracle." 273 274 from django.db import backend, connection 275 from django.db.models.query import EmptyResultSet, GET_ITERATOR_CHUNK_SIZE, quote_only_if_word 276 277 class OracleQuerySet(DefaultQuerySet): 278 279 def iterator(self): 280 "Performs the SELECT database lookup of this QuerySet." 281 282 from django.db.models.query import get_cached_row 283 284 # self._select is a dictionary, and dictionaries' key order is 285 # undefined, so we convert it to a list of tuples. 286 extra_select = self._select.items() 287 288 full_query = None 289 290 try: 291 try: 292 select, sql, params, full_query = self._get_sql_clause(get_full_query=True) 293 except TypeError: 294 select, sql, params = self._get_sql_clause() 295 except EmptyResultSet: 296 raise StopIteration 297 if not full_query: 298 full_query = "SELECT %s%s\n%s" % \ 299 ((self._distinct and "DISTINCT " or ""), 300 ', '.join(select), sql) 301 302 cursor = connection.cursor() 303 cursor.execute(full_query, params) 304 305 fill_cache = self._select_related 306 fields = self.model._meta.fields 307 index_end = len(fields) 308 309 # so here's the logic; 310 # 1. retrieve each row in turn 311 # 2. convert NCLOBs 312 313 while 1: 314 rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 315 if not rows: 316 raise StopIteration 317 for row in rows: 318 row = self.resolve_columns(row, fields) 319 if fill_cache: 320 obj, index_end = get_cached_row(klass=self.model, row=row, 321 index_start=0, max_depth=self._max_related_depth) 322 else: 323 obj = self.model(*row[:index_end]) 324 for i, k in enumerate(extra_select): 325 setattr(obj, k[0], row[index_end+i]) 326 yield obj 327 328 329 def _get_sql_clause(self, get_full_query=False): 330 from django.db.models.query import fill_table_cache, \ 331 handle_legacy_orderlist, orderfield2column 332 333 opts = self.model._meta 334 335 # Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z. 336 select = ["%s.%s" % (backend.quote_name(opts.db_table), backend.quote_name(f.column)) for f in opts.fields] 337 tables = [quote_only_if_word(t) for t in self._tables] 338 joins = SortedDict() 339 where = self._where[:] 340 params = self._params[:] 341 342 # Convert self._filters into SQL. 343 joins2, where2, params2 = self._filters.get_sql(opts) 344 joins.update(joins2) 345 where.extend(where2) 346 params.extend(params2) 347 348 # Add additional tables and WHERE clauses based on select_related. 349 if self._select_related: 350 fill_table_cache(opts, select, tables, where, opts.db_table, [opts.db_table]) 351 352 # Add any additional SELECTs. 353 if self._select: 354 select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()]) 355 356 # Start composing the body of the SQL statement. 357 sql = [" FROM", backend.quote_name(opts.db_table)] 358 359 # Compose the join dictionary into SQL describing the joins. 360 if joins: 361 sql.append(" ".join(["%s %s %s ON %s" % (join_type, table, alias, condition) 362 for (alias, (table, join_type, condition)) in joins.items()])) 363 364 # Compose the tables clause into SQL. 365 if tables: 366 sql.append(", " + ", ".join(tables)) 367 368 # Compose the where clause into SQL. 369 if where: 370 sql.append(where and "WHERE " + " AND ".join(where)) 371 372 # ORDER BY clause 373 order_by = [] 374 if self._order_by is not None: 375 ordering_to_use = self._order_by 376 else: 377 ordering_to_use = opts.ordering 378 for f in handle_legacy_orderlist(ordering_to_use): 379 if f == '?': # Special case. 380 order_by.append(backend.get_random_function_sql()) 381 else: 382 if f.startswith('-'): 383 col_name = f[1:] 384 order = "DESC" 385 else:
