Changeset 5519
- Timestamp:
- 06/23/07 09:16:00 (2 years 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: 386 col_name = f 387 order = "ASC" 388 if "." in col_name: 389 table_prefix, col_name = col_name.split('.', 1) 390 table_prefix = backend.quote_name(table_prefix) + '.' 391 else: 392 # Use the database table as a column prefix if it wasn't given, 393 # and if the requested column isn't a custom SELECT. 394 if "." not in col_name and col_name not in (self._select or ()): 395 table_prefix = backend.quote_name(opts.db_table) + '.' 396 else: 397 table_prefix = '' 398 order_by.append('%s%s %s' % (table_prefix, backend.quote_name(orderfield2column(col_name, opts)), order)) 399 if order_by: 400 sql.append("ORDER BY " + ", ".join(order_by)) 401 402 # Look for column name collisions in the select elements 403 # and fix them with an AS alias. This allows us to do a 404 # SELECT * later in the paging query. 405 cols = [clause.split('.')[-1] for clause in select] 406 for index, col in enumerate(cols): 407 if cols.count(col) > 1: 408 col = '%s%d' % (col.replace('"', ''), index) 409 cols[index] = col 410 select[index] = '%s AS %s' % (select[index], col) 411 412 # LIMIT and OFFSET clauses 413 # To support limits and offsets, Oracle requires some funky rewriting of an otherwise normal looking query. 414 select_clause = ",".join(select) 415 distinct = (self._distinct and "DISTINCT " or "") 416 417 if order_by: 418 order_by_clause = " OVER (ORDER BY %s )" % (", ".join(order_by)) 419 else: 420 #Oracle's row_number() function always requires an order-by clause. 421 #So we need to define a default order-by, since none was provided. 422 order_by_clause = " OVER (ORDER BY %s.%s)" % \ 423 (backend.quote_name(opts.db_table), 424 backend.quote_name(opts.fields[0].db_column or opts.fields[0].column)) 425 # limit_and_offset_clause 426 if self._limit is None: 427 assert self._offset is None, "'offset' is not allowed without 'limit'" 428 429 if self._offset is not None: 430 offset = int(self._offset) 431 else: 432 offset = 0 433 if self._limit is not None: 434 limit = int(self._limit) 435 else: 436 limit = None 437 438 limit_and_offset_clause = '' 439 if limit is not None: 440 limit_and_offset_clause = "WHERE rn > %s AND rn <= %s" % (offset, limit+offset) 441 elif offset: 442 limit_and_offset_clause = "WHERE rn > %s" % (offset) 443 444 if len(limit_and_offset_clause) > 0: 445 fmt = \ 446 """SELECT * FROM 447 (SELECT %s%s, 448 ROW_NUMBER()%s AS rn 449 %s) 450 %s""" 451 full_query = fmt % (distinct, select_clause, 452 order_by_clause, ' '.join(sql).strip(), 453 limit_and_offset_clause) 454 else: 455 full_query = None 456 457 if get_full_query: 458 return select, " ".join(sql), params, full_query 459 else: 460 return select, " ".join(sql), params 461 462 def resolve_columns(self, row, fields=()): 463 from django.db.models.fields import DateField, DateTimeField, \ 464 TimeField, BooleanField, NullBooleanField, DecimalField, Field 465 values = [] 466 for value, field in map(None, row, fields): 467 if isinstance(value, Database.LOB): 468 value = value.read() 469 # Oracle stores empty strings as null. We need to undo this in 470 # order to adhere to the Django convention of using the empty 471 # string instead of null, but only if the field accepts the 472 # empty string. 473 if value is None and isinstance(field, Field) and field.empty_strings_allowed: 474 value = '' 475 # Convert 1 or 0 to True or False 476 elif value in (1, 0) and isinstance(field, (BooleanField, NullBooleanField)): 477 value = bool(value) 478 # Convert floats to decimals 479 elif value is not None and isinstance(field, DecimalField): 480 value = util.typecast_decimal(field.format_number(value)) 481 # cx_Oracle always returns datetime.datetime objects for 482 # DATE and TIMESTAMP columns, but Django wants to see a 483 # python datetime.date, .time, or .datetime. We use the type 484 # of the Field to determine which to cast to, but it's not 485 # always available. 486 # As a workaround, we cast to date if all the time-related 487 # values are 0, or to time if the date is 1/1/1900. 488 # This could be cleaned a bit by adding a method to the Field 489 # classes to normalize values from the database (the to_python 490 # method is used for validation and isn't what we want here). 491 elif isinstance(value, Database.Timestamp): 492 # In Python 2.3, the cx_Oracle driver returns its own 493 # Timestamp object that we must convert to a datetime class. 494 if not isinstance(value, datetime.datetime): 495 value = datetime.datetime(value.year, value.month, value.day, value.hour, 496 value.minute, value.second, value.fsecond) 497 if isinstance(field, DateTimeField): 498 pass # DateTimeField subclasses DateField so must be checked first. 499 elif isinstance(field, DateField): 500 value = value.date() 501 elif isinstance(field, TimeField) or (value.year == 1900 and value.month == value.day == 1): 502 value = value.time() 503 elif value.hour == value.minute == value.second == value.microsecond == 0: 504 value = value.date() 505 values.append(value) 506 return values 507 508 return OracleQuerySet 509 142 510 143 511 OPERATOR_MAPPING = { 144 512 'exact': '= %s', 145 'iexact': ' LIKE %s',146 'contains': 'LIKE %s',147 'icontains': 'LIKE %s',513 'iexact': '= UPPER(%s)', 514 'contains': "LIKE %s ESCAPE '\\'", 515 'icontains': "LIKE UPPER(%s) ESCAPE '\\'", 148 516 'gt': '> %s', 149 517 'gte': '>= %s', 150 518 'lt': '< %s', 151 519 'lte': '<= %s', 152 'startswith': 'LIKE %s',153 'endswith': 'LIKE %s',154 'istartswith': 'LIKE %s',155 'iendswith': 'LIKE %s',520 'startswith': "LIKE %s ESCAPE '\\'", 521 'endswith': "LIKE %s ESCAPE '\\'", 522 'istartswith': "LIKE UPPER(%s) ESCAPE '\\'", 523 'iendswith': "LIKE UPPER(%s) ESCAPE '\\'", 156 524 } django/trunk/django/db/backends/oracle/client.py
r4265 r5519 3 3 4 4 def runshell(): 5 args = '' 6 args += settings.DATABASE_USER 5 dsn = settings.DATABASE_USER 7 6 if settings.DATABASE_PASSWORD: 8 args += "/%s" % settings.DATABASE_PASSWORD 9 args += "@%s" % settings.DATABASE_NAME 10 os.execvp('sqlplus', args) 7 dsn += "/%s" % settings.DATABASE_PASSWORD 8 if settings.DATABASE_NAME: 9 dsn += "@%s" % settings.DATABASE_NAME 10 args = ["sqlplus", "-L", dsn] 11 os.execvp("sqlplus", args) django/trunk/django/db/backends/oracle/creation.py
r5302 r5519 1 import sys, time 2 from django.core import management 3 4 # This dictionary maps Field objects to their associated Oracle column 5 # types, as strings. Column-type strings can contain format strings; they'll 6 # be interpolated against the values of Field.__dict__ before being output. 7 # If a column type is set to None, it won't be included in the output. 1 8 DATA_TYPES = { 2 'AutoField': 'number(38)', 3 'BooleanField': 'number(1)', 4 'CharField': 'varchar2(%(maxlength)s)', 5 'CommaSeparatedIntegerField': 'varchar2(%(maxlength)s)', 6 'DateField': 'date', 7 'DateTimeField': 'date', 8 'DecimalField': 'number(%(max_digits)s, %(decimal_places)s)', 9 'FileField': 'varchar2(100)', 10 'FilePathField': 'varchar2(100)', 11 'FloatField': 'double precision', 12 'ImageField': 'varchar2(100)', 13 'IntegerField': 'integer', 14 'IPAddressField': 'char(15)', 15 'ManyToManyField': None, 16 'NullBooleanField': 'integer', 17 'OneToOneField': 'integer', 18 'PhoneNumberField': 'varchar(20)', 19 'PositiveIntegerField': 'integer', 20 'PositiveSmallIntegerField': 'smallint', 21 'SlugField': 'varchar(50)', 22 'SmallIntegerField': 'smallint', 23 'TextField': 'long', 24 'TimeField': 'timestamp', 25 'USStateField': 'varchar(2)', 9 'AutoField': 'NUMBER(11)', 10 'BooleanField': 'NUMBER(1) CHECK (%(column)s IN (0,1))', 11 'CharField': 'VARCHAR2(%(maxlength)s)', 12 'CommaSeparatedIntegerField': 'VARCHAR2(%(maxlength)s)', 13 'DateField': 'DATE', 14 'DateTimeField': 'TIMESTAMP', 15 'DecimalField': 'NUMBER(%(max_digits)s, %(decimal_places)s)', 16 'FileField': 'VARCHAR2(100)', 17 'FilePathField': 'VARCHAR2(100)', 18 'FloatField': 'DOUBLE PRECISION', 19 'ImageField': 'VARCHAR2(100)', 20 'IntegerField': 'NUMBER(11)', 21 'IPAddressField': 'VARCHAR2(15)', 22 'ManyToManyField': None, 23 'NullBooleanField': 'NUMBER(1) CHECK ((%(column)s IN (0,1)) OR (%(column)s IS NULL))', 24 'OneToOneField': 'NUMBER(11)', 25 'PhoneNumberField': 'VARCHAR2(20)', 26 'PositiveIntegerField': 'NUMBER(11) CHECK (%(column)s >= 0)', 27 'PositiveSmallIntegerField': 'NUMBER(11) CHECK (%(column)s >= 0)', 28 'SlugField': 'VARCHAR2(50)', 29 'SmallIntegerField': 'NUMBER(11)', 30 'TextField': 'NCLOB', 31 'TimeField': 'TIMESTAMP', 32 'URLField': 'VARCHAR2(200)', 33 'USStateField': 'CHAR(2)', 26 34 } 35 36 TEST_DATABASE_PREFIX = 'test_' 37 PASSWORD = 'Im_a_lumberjack' 38 REMEMBER = {} 39 40 41 def create_test_db(settings, connection, backend, verbosity=1, autoclobber=False): 42 43 TEST_DATABASE_NAME = _test_database_name(settings) 44 TEST_DATABASE_USER = _test_database_user(settings) 45 TEST_DATABASE_PASSWD = _test_database_passwd(settings) 46 TEST_DATABASE_TBLSPACE = _test_database_tblspace(settings) 47 TEST_DATABASE_TBLSPACE_TMP = _test_database_tblspace_tmp(settings) 48 49 parameters = { 50 'dbname': TEST_DATABASE_NAME, 51 'user': TEST_DATABASE_USER, 52 'password': TEST_DATABASE_PASSWD, 53 'tblspace': TEST_DATABASE_TBLSPACE, 54 'tblspace_temp': TEST_DATABASE_TBLSPACE_TMP, 55 } 56 57 REMEMBER['user'] = settings.DATABASE_USER 58 REMEMBER['passwd'] = settings.DATABASE_PASSWORD 59 60 cursor = connection.cursor() 61 if _test_database_create(settings): 62 if verbosity >= 1: 63 print 'Creating test database...' 64 try: 65 _create_test_db(cursor, parameters, verbosity) 66 except Exception, e: 67 sys.stderr.write("Got an error creating the test database: %s\n" % e) 68 if not autoclobber: 69 confirm = raw_input("It appears the test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_NAME) 70 if autoclobber or confirm == 'yes': 71 try: 72 if verbosity >= 1: 73 print "Destroying old test database..." 74 _destroy_test_db(cursor, parameters, verbosity) 75 if verbosity >= 1: 76 print "Creating test database..." 77 _create_test_db(cursor, parameters, verbosity) 78 except Exception, e: 79 sys.stderr.write("Got an error recreating the test database: %s\n" % e) 80 sys.exit(2) 81 else: 82 print "Tests cancelled." 83 sys.exit(1) 84 85 if _test_user_create(settings): 86 if verbosity >= 1: 87 print "Creating test user..." 88 try: 89 _create_test_user(cursor, parameters, verbosity) 90 except Exception, e: 91 sys.stderr.write("Got an error creating the test user: %s\n" % e) 92 if not autoclobber: 93 confirm = raw_input("It appears the test user, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_USER) 94 if autoclobber or confirm == 'yes': 95 try: 96 if verbosity >= 1: 97 print "Destroying old test user..." 98 _destroy_test_user(cursor, parameters, verbosity) 99 if verbosity >= 1: 100 print "Creating test user..." 101 _create_test_user(cursor, parameters, verbosity) 102 except Exception, e: 103 sys.stderr.write("Got an error recreating the test user: %s\n" % e) 104 sys.exit(2) 105 else: 106 print "Tests cancelled." 107 sys.exit(1) 108 109 connection.close() 110 settings.DATABASE_USER = TEST_DATABASE_USER 111 settings.DATABASE_PASSWORD = TEST_DATABASE_PASSWD 112 113 management.syncdb(verbosity, interactive=False) 114 115 # Get a cursor (even though we don't need one yet). This has 116 # the side effect of initializing the test database. 117 cursor = connection.cursor() 118 119 120 def destroy_test_db(settings, connection, backend, old_database_name, verbosity=1): 121 connection.close() 122 123 TEST_DATABASE_NAME = _test_database_name(settings) 124 TEST_DATABASE_USER = _test_database_user(settings) 125 TEST_DATABASE_PASSWD = _test_database_passwd(settings) 126 TEST_DATABASE_TBLSPACE = _test_database_tblspace(settings) 127 TEST_DATABASE_TBLSPACE_TMP = _test_database_tblspace_tmp(settings) 128 129 settings.DATABASE_NAME = old_database_name 130 settings.DATABASE_USER = REMEMBER['user'] 131 settings.DATABASE_PASSWORD = REMEMBER['passwd'] 132 133 parameters = { 134 'dbname': TEST_DATABASE_NAME, 135 'user': TEST_DATABASE_USER, 136 'password': TEST_DATABASE_PASSWD, 137 'tblspace': TEST_DATABASE_TBLSPACE, 138 'tblspace_temp': TEST_DATABASE_TBLSPACE_TMP, 139 } 140 141 REMEMBER['user'] = settings.DATABASE_USER 142 REMEMBER['passwd'] = settings.DATABASE_PASSWORD 143 144 cursor = connection.cursor() 145 time.sleep(1) # To avoid "database is being accessed by other users" errors. 146 if _test_user_create(settings): 147 if verbosity >= 1: 148 print 'Destroying test user...' 149 _destroy_test_user(cursor, parameters, verbosity) 150 if _test_database_create(settings): 151 if verbosity >= 1: 152 print 'Destroying test database...' 153 _destroy_test_db(cursor, parameters, verbosity) 154 connection.close() 155 156 157 def _create_test_db(cursor, parameters, verbosity): 158 if verbosity >= 2: 159 print "_create_test_db(): dbname = %s" % parameters['dbname'] 160 statements = [ 161 """CREATE TABLESPACE %(tblspace)s 162 DATAFILE '%(tblspace)s.dbf' SIZE 20M 163 REUSE AUTOEXTEND ON NEXT 10M MAXSIZE 100M 164 """, 165 """CREATE TEMPORARY TABLESPACE %(tblspace_temp)s 166 TEMPFILE '%(tblspace_temp)s.dbf' SIZE 20M 167 REUSE AUTOEXTEND ON NEXT 10M MAXSIZE 100M 168 """, 169 ] 170 _execute_statements(cursor, statements, parameters, verbosity) 171 172 173 def _create_test_user(cursor, parameters, verbosity): 174 if verbosity >= 2: 175 print "_create_test_user(): username = %s" % parameters['user'] 176 statements = [ 177 """CREATE USER %(user)s 178 IDENTIFIED BY %(password)s 179 DEFAULT TABLESPACE %(tblspace)s 180 TEMPORARY TABLESPACE %(tblspace_temp)s 181 """, 182 """GRANT CONNECT, RESOURCE TO %(user)s""", 183 ] 184 _execute_statements(cursor, statements, parameters, verbosity) 185 186 187 def _destroy_test_db(cursor, parameters, verbosity): 188 if verbosity >= 2: 189 print "_destroy_test_db(): dbname=%s" % parameters['dbname'] 190 statements = [ 191 'DROP TABLESPACE %(tblspace)s INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS', 192 'DROP TABLESPACE %(tblspace_temp)s INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS', 193 ] 194 _execute_statements(cursor, statements, parameters, verbosity) 195 196 197 def _destroy_test_user(cursor, parameters, verbosity): 198 if verbosity >= 2: 199 print "_destroy_test_user(): user=%s" % parameters['user'] 200 print "Be patient. This can take some time..." 201 statements = [ 202 'DROP USER %(user)s CASCADE', 203 ] 204 _execute_statements(cursor, statements, parameters, verbosity) 205 206 207 def _execute_statements(cursor, statements, parameters, verbosity): 208 for template in statements: 209 stmt = template % parameters 210 if verbosity >= 2: 211 print stmt 212 try: 213 cursor.execute(stmt) 214 except Exception, err: 215 sys.stderr.write("Failed (%s)\n" % (err)) 216 raise 217 218 219 def _test_database_name(settings): 220 name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME 221 try: 222 if settings.TEST_DATABASE_NAME: 223 name = settings.TEST_DATABASE_NAME 224 except AttributeError: 225 pass 226 except: 227 raise 228 return name 229 230 231 def _test_database_create(settings): 232 name = True 233 try: 234 if settings.TEST_DATABASE_CREATE: 235 name = True 236 else: 237 name = False 238 except AttributeError: 239 pass 240 except: 241 raise 242 return name 243 244 245 def _test_user_create(settings): 246 name = True 247 try: 248 if settings.TEST_USER_CREATE: 249 name = True 250 else: 251 name = False 252 except AttributeError: 253 pass 254 except: 255 raise 256 return name 257 258 259 def _test_database_user(settings): 260 name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME 261 try: 262 if settings.TEST_DATABASE_USER: 263 name = settings.TEST_DATABASE_USER 264 except AttributeError: 265 pass 266 except: 267 raise 268 return name 269 270 271 def _test_database_passwd(settings): 272 name = PASSWORD 273 try: 274 if settings.TEST_DATABASE_PASSWD: 275 name = settings.TEST_DATABASE_PASSWD 276 except AttributeError: 277 pass 278 except: 279 raise 280 return name 281 282 283 def _test_database_tblspace(settings): 284 name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME 285 try: 286 if settings.TEST_DATABASE_TBLSPACE: 287 name = settings.TEST_DATABASE_TBLSPACE 288 except AttributeError: 289 pass 290 except: 291 raise 292 return name 293 294 295 def _test_database_tblspace_tmp(settings): 296 name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME + '_temp' 297 try: 298 if settings.TEST_DATABASE_TBLSPACE_TMP: 299 name = settings.TEST_DATABASE_TBLSPACE_TMP 300 except AttributeError: 301 pass 302 except: 303 raise 304 return name django/trunk/django/db/backends/oracle/introspection.py
r5302 r5519 1 from django.db.backends.oracle.base import quote_name 1 2 import re 3 import cx_Oracle 4 2 5 3 6 foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)") … … 6 9 "Returns a list of table names in the current database." 7 10 cursor.execute("SELECT TABLE_NAME FROM USER_TABLES") 8 return [row[0] for row in cursor.fetchall()]11 return [row[0].upper() for row in cursor.fetchall()] 9 12 10 13 def get_table_description(cursor, table_name): 11 return table_name 14 "Returns a description of the table, with the DB-API cursor.description interface." 15 cursor.execute("SELECT * FROM %s WHERE ROWNUM < 2" % quote_name(table_name)) 16 return cursor.description 12 17 13 18 def _name_to_index(cursor, table_name): … … 23 28 representing all relationships to the given table. Indexes are 0-based. 24 29 """ 25 raise NotImplementedError 30 cursor.execute(""" 31 SELECT ta.column_id - 1, tb.table_name, tb.column_id - 1 32 FROM user_constraints, USER_CONS_COLUMNS ca, USER_CONS_COLUMNS cb, 33 user_tab_cols ta, user_tab_cols tb 34 WHERE user_constraints.table_name = %s AND 35 ta.table_name = %s AND 36 ta.column_name = ca.column_name AND 37 ca.table_name = %s AND 38 user_constraints.constraint_name = ca.constraint_name AND 39 user_constraints.r_constraint_name = cb.constraint_name AND 40 cb.table_name = tb.table_name AND 41 cb.column_name = tb.column_name AND 42 ca.position = cb.position""", [table_name, table_name, table_name]) 43 44 relations = {} 45 for row in cursor.fetchall(): 46 relations[row[0]] = (row[2], row[1]) 47 return relations 26 48 27 49 def get_indexes(cursor, table_name): … … 32 54 'unique': boolean representing whether it's a unique index} 33 55 """ 34 raise NotImplementedError 56 # This query retrieves each index on the given table, including the 57 # first associated field name 58 # "We were in the nick of time; you were in great peril!" 59 sql = """ 60 WITH primarycols AS ( 61 SELECT user_cons_columns.table_name, user_cons_columns.column_name, 1 AS PRIMARYCOL 62 FROM user_cons_columns, user_constraints 63 WHERE user_cons_columns.constraint_name = user_constraints.constraint_name AND 64 user_constraints.constraint_type = 'P' AND 65 user_cons_columns.table_name = %s), 66 uniquecols AS ( 67 SELECT user_ind_columns.table_name, user_ind_columns.column_name, 1 AS UNIQUECOL 68 FROM user_indexes, user_ind_columns 69 WHERE uniqueness = 'UNIQUE' AND 70 user_indexes.index_name = user_ind_columns.index_name AND 71 user_ind_columns.table_name = %s) 72 SELECT allcols.column_name, primarycols.primarycol, uniquecols.UNIQUECOL 73 FROM (SELECT column_name FROM primarycols UNION SELECT column_name FROM 74 uniquecols) allcols, 75 primarycols, uniquecols 76 WHERE allcols.column_name = primarycols.column_name (+) AND 77 allcols.column_name = uniquecols.column_name (+) 78 """ 79 cursor.execute(sql, [table_name, table_name]) 80 indexes = {} 81 for row in cursor.fetchall(): 82 # row[1] (idx.indkey) is stored in the DB as an array. It comes out as 83 # a string of space-separated integers. This designates the field 84 # indexes (1-based) of the fields that have indexes on the table. 85 # Here, we skip any indexes across multiple fields. 86 indexes[row[0]] = {'primary_key': row[1], 'unique': row[2]} 87 return indexes 35 88 36 # Maps type codes to Django Field types.89 # Maps type objects to Django Field types. 37 90 DATA_TYPES_REVERSE = { 38 16: 'BooleanField', 39 21: 'SmallIntegerField', 40 23: 'IntegerField', 41 25: 'TextField', 42 869: 'IPAddressField', 43 1043: 'CharField', 44 1082: 'DateField', 45 1083: 'TimeField', 46 1114: 'DateTimeField', 47 1184: 'DateTimeField', 48 1266: 'TimeField', 49 1700: 'DecimalField', 91 cx_Oracle.CLOB: 'TextField', 92 cx_Oracle.DATETIME: 'DateTimeField', 93 cx_Oracle.FIXED_CHAR: 'CharField', 94 cx_Oracle.NCLOB: 'TextField', 95 cx_Oracle.NUMBER: 'DecimalField', 96 cx_Oracle.STRING: 'CharField', 97 cx_Oracle.TIMESTAMP: 'DateTimeField', 50 98 } django/trunk/django/db/backends/postgresql/base.py
r5455 r5519 88 88 if not postgres_version: 89 89 cursor.execute("SELECT version()") 90 postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')] 90 postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')] 91 91 if settings.DEBUG: 92 92 return util.CursorDebugWrapper(cursor, self) … … 106 106 self.connection = None 107 107 108 allows_group_by_ordinal = True 109 allows_unique_and_pk = True 110 autoindexes_primary_keys = True 111 needs_datetime_string_cast = True 112 needs_upper_for_iops = False 108 113 supports_constraints = True 114 supports_tablespaces = False 115 uses_case_insensitive_names = False 109 116 110 117 def quote_name(name): … … 138 145 # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC 139 146 return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) 147 148 def get_datetime_cast_sql(): 149 return None 140 150 141 151 def get_limit_offset_sql(limit, offset=None): … … 150 160 def get_deferrable_sql(): 151 161 return " DEFERRABLE INITIALLY DEFERRED" 152 162 153 163 def get_fulltext_search_sql(field_name): 154 164 raise NotImplementedError … … 159 169 def get_pk_default_value(): 160 170 return "DEFAULT" 171 172 def get_max_name_length(): 173 return None 174 175 def get_start_transaction_sql(): 176 return "BEGIN;" 177 178 def get_autoinc_sql(table): 179 return None 161 180 162 181 def get_sql_flush(style, tables, sequences): … … 164 183 all tables in the database (without actually removing the tables 165 184 themselves) and put the database in an empty 'initial' state 166 167 """ 185 186 """ 168 187 if tables: 169 188 if postgres_version[0] >= 8 and postgres_version[1] >= 1: … … 176 195 )] 177 196 else: 178 # Older versions of Postgres can't do TRUNCATE in a single call, so they must use 197 # Older versions of Postgres can't do TRUNCATE in a single call, so they must use 179 198 # a simple delete. 180 199 sql = ['%s %s %s;' % \ … … 244 263 style.SQL_TABLE(f.m2m_db_table()))) 245 264 return output 246 265 247 266 # Register these custom typecasts, because Django expects dates/times to be 248 267 # in Python's native (standard-library) datetime/time format, whereas psycopg django/trunk/django/db/backends/postgresql_psycopg2/base.py
r5455 r5519 56 56 if not postgres_version: 57 57 cursor.execute("SELECT version()") 58 postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')] 58 postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')] 59 59 if settings.DEBUG: 60 60 return util.CursorDebugWrapper(cursor, self) … … 74 74 self.connection = None 75 75 76 allows_group_by_ordinal = True 77 allows_unique_and_pk = True 78 autoindexes_primary_keys = True 79 needs_datetime_string_cast = False 80 needs_upper_for_iops = False 76 81 supports_constraints = True 82 supports_tablespaces = False 83 uses_case_insensitive_names = True 77 84 78 85 def quote_name(name): … … 98 105 # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC 99 106 return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) 107 108 def get_datetime_cast_sql(): 109 return None 100 110 101 111 def get_limit_offset_sql(limit, offset=None): … … 119 129 def get_pk_default_value(): 120 130 return "DEFAULT" 131 132 def get_max_name_length(): 133 return None 134 135 def get_start_transaction_sql(): 136 return "BEGIN;" 137 138 def get_autoinc_sql(table): 139 return None 121 140 122 141 def get_sql_flush(style, tables, sequences): … … 140 159 style.SQL_FIELD(quote_name(table)) 141 160 ) for table in tables] 142 161 143 162 # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements 144 163 # to reset sequence indices … … 201 220 style.SQL_TABLE(f.m2m_db_table()))) 202 221 return output 203 222 204 223 OPERATOR_MAPPING = { 205 224 'exact': '= %s', django/trunk/django/db/backends/sqlite3/base.py
r5302 r5519 108 108 return query % tuple("?" * num_params) 109 109 110 allows_group_by_ordinal = True 111 allows_unique_and_pk = True 112 autoindexes_primary_keys = True 113 needs_datetime_string_cast = True 114 needs_upper_for_iops = False 110 115 supports_constraints = False 116 supports_tablespaces = False 117 uses_case_insensitive_names = False 111 118 112 119 def quote_name(name): … … 140 147 return 'django_date_trunc("%s", %s)' % (lookup_type.lower(), field_name) 141 148 149 def get_datetime_cast_sql(): 150 return None 151 142 152 def get_limit_offset_sql(limit, offset=None): 143 153 sql = "LIMIT %s" % limit … … 161 171 return "NULL" 162 172 173 def get_max_name_length(): 174 return None 175 176 def get_start_transaction_sql(): 177 return "BEGIN;" 178 179 def get_autoinc_sql(table): 180 return None 181 163 182 def get_sql_flush(style, tables, sequences): 164 183 """Return a list of SQL statements required to remove all data from 165 184 all tables in the database (without actually removing the tables 166 185 themselves) and put the database in an empty 'initial' state 167 186 168 187 """ 169 188 # NB: The generated SQL below is specific to SQLite … … 183 202 # No sequence reset required 184 203 return [] 185 204 186 205 def _sqlite_date_trunc(lookup_type, dt): 187 206 try: django/trunk/django/db/backends/util.py
r5450 r5519 1 1 import datetime 2 import md5 2 3 from time import time 3 4 … … 108 109 return str(d) 109 110 111 def truncate_name(name, length=None): 112 """Shortens a string to a repeatable mangled version with the given length. 113 """ 114 if length is None or len(name) <= length: 115 return name 116 117 hash = md5.md5(name).hexdigest()[:4] 118 119 return '%s%s' % (name[:length-4], hash) 120 110 121 ################################################################################## 111 122 # Helper functions for dictfetch* for databases that don't natively support them # django/trunk/django/db/models/base.py
r5163 r5519 97 97 def __init__(self, *args, **kwargs): 98 98 dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs) 99 99 100 100 # There is a rather weird disparity here; if kwargs, it's set, then args 101 # overrides it. It should be one or the other; don't duplicate the work 101 # overrides it. It should be one or the other; don't duplicate the work 102 102 # The reason for the kwargs check is that standard iterator passes in by 103 103 # args, and nstantiation for iteration is 33% faster. … … 123 123 if isinstance(field.rel, ManyToOneRel): 124 124 kwargs.pop(field.attname, None) 125 125 126 126 # Now we're left with the unprocessed fields that *must* come from 127 127 # keywords, or default. 128 128 129 129 for field in fields_iter: 130 130 if kwargs: … … 148 148 val = getattr(rel_obj, field.rel.get_related_field().attname) 149 149 except AttributeError: 150 raise TypeError("Invalid value: %r should be a %s instance, not a %s" % 150 raise TypeError("Invalid value: %r should be a %s instance, not a %s" % 151 151 (field.name, field.rel.to, type(rel_obj))) 152 152 else: … … 211 211 if pk_set: 212 212 # Determine whether a record with the primary key already exists. 213 cursor.execute("SELECT 1 FROM %s WHERE %s=%%s LIMIT 1" % \ 214 (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), [pk_val]) 213 cursor.execute("SELECT COUNT(*) FROM %s WHERE %s=%%s" % \ 214 (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), 215 self._meta.pk.get_db_prep_lookup('exact', pk_val)) 215 216 # If it does already exist, do an UPDATE. 216 if cursor.fetchone() :217 if cursor.fetchone()[0] > 0: 217 218 db_values = [f.get_db_prep_save(f.pre_save(self, False)) for f in non_pks] 218 219 if db_values: … … 221 222 ','.join(['%s=%%s' % backend.quote_name(f.column) for f in non_pks]), 222 223 backend.quote_name(self._meta.pk.column)), 223 db_values + [pk_val])224 db_values + self._meta.pk.get_db_prep_lookup('exact', pk_val)) 224 225 else: 225 226 record_exists = False django/trunk/django/db/models/fields/__init__.py
r5302 r5519 75 75 prepopulate_from=None, unique_for_date=None, unique_for_month=None, 76 76 unique_for_year=None, validator_list=None, choices=None, radio_admin=None, 77 help_text='', db_column=None ):77 help_text='', db_column=None, db_tablespace=None): 78 78 self.name = name 79 79 self.verbose_name = verbose_name … … 81 81 self.maxlength, self.unique = maxlength, unique 82 82 self.blank, self.null = blank, null 83 # Oracle treats the empty string ('') as null, so coerce the null 84 # option whenever '' is a possible value. 85 if self.empty_strings_allowed and settings.DATABASE_ENGINE == 'oracle': 86 self.null = True 83 87 self.core, self.rel, self.default = core, rel, default 84 88 self.editable = editable … … 92 96 self.help_text = help_text 93 97 self.db_column = db_column 98 self.db_tablespace = db_tablespace 94 99 95 100 # Set db_index to True if the field has a relationship and doesn't explicitly set db_index. … … 202 207 return self.default() 203 208 return self.default 204 if not self.empty_strings_allowed or self.null:209 if not self.empty_strings_allowed or (self.null and settings.DATABASE_ENGINE != 'oracle'): 205 210 return None 206 211 return "" … … 807 812 808 813 class NullBooleanField(Field): 814 empty_strings_allowed = False 809 815 def __init__(self, *args, **kwargs): 810 816 kwargs['null'] = True … … 876 882 877 883 def get_db_prep_lookup(self, lookup_type, value): 884 if settings.DATABASE_ENGINE == 'oracle': 885 # Oracle requires a date in order to parse. 886 def prep(value): 887 if isinstance(value, datetime.time): 888 value = datetime.datetime.combine(datetime.date(1900, 1, 1), value) 889 return str(value) 890 else: 891 prep = str 878 892 if lookup_type == 'range': 879 value = [ str(v) for v in value]880 else: 881 value = str(value)893 value = [prep(v) for v in value] 894 else: 895 value = prep(value) 882 896 return Field.get_db_prep_lookup(self, lookup_type, value) 883 897 … … 897 911 if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'): 898 912 value = value.replace(microsecond=0) 899 value = str(value) 913 if settings.DATABASE_ENGINE == 'oracle': 914 # cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field. 915 if isinstance(value, datetime.time): 916 value = datetime.datetime(1900, 1, 1, value.hour, value.minute, 917 value.second, value.microsecond) 918 elif isinstance(value, basestring): 919 value = datetime.datetime(*(time.strptime(value, '%H:%M:%S')[:6])) 920 else: 921 value = str(value) 900 922 return Field.get_db_prep_save(self, value) 901 923 django/trunk/django/db/models/fields/related.py
r5514 r5519 337 337 target_col_name, ",".join(['%s'] * len(new_ids))), 338 338 [self._pk_val] + list(new_ids)) 339 if cursor.rowcount is not None and cursor.rowcount != 0: 340 existing_ids = set([row[0] for row in cursor.fetchmany(cursor.rowcount)]) 341 else: 342 existing_ids = set() 339 existing_ids = set([row[0] for row in cursor.fetchall()]) 343 340 344 341 # Add the ones that aren't there already django/trunk/django/db/models/options.py
r5091 r5519 14 14 DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering', 15 15 'unique_together', 'permissions', 'get_latest_by', 16 'order_with_respect_to', 'app_label' )16 'order_with_respect_to', 'app_label', 'db_tablespace') 17 17 18 18 class Options(object): … … 28 28 self.get_latest_by = None 29 29 self.order_with_respect_to = None 30 self.db_tablespace = None 30 31 self.admin = None 31 32 self.meta = meta … … 60 61 61 62 def _prepare(self, model): 63 from django.db import backend 64 from django.db.backends.util import truncate_name 62 65 if self.order_with_respect_to: 63 66 self.order_with_respect_to = self.get_field(self.order_with_respect_to) … … 74 77 if not self.db_table: 75 78 self.db_table = "%s_%s" % (self.app_label, self.module_name) 79 self.db_table = truncate_name(self.db_table, 80 backend.get_max_name_length()) 76 81 77 82 def add_field(self, field): … … 89 94 def __repr__(self): 90 95 return '<Options for %s>' % self.object_name 91 96 92 97 def __str__(self): 93 98 return "%s.%s" % (self.app_label, self.module_name) 94 99 95 100 def get_field(self, name, many_to_many=True): 96 101 "Returns the requested field by name. Raises FieldDoesNotExist on error." django/trunk/django/db/models/query.py
r5514 r5519 5 5 from django.utils.datastructures import SortedDict 6 6 from django.contrib.contenttypes import generic 7 import datetime 7 8 import operator 8 9 import re … … 79 80 return backend.quote_name(word) 80 81 81 class QuerySet(object):82 class _QuerySet(object): 82 83 "Represents a lazy database lookup for a set of objects" 83 84 def __init__(self, model=None): … … 183 184 cursor = connection.cursor() 184 185 cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 186 185 187 fill_cache = self._select_related 186 index_end = len(self.model._meta.fields) 188 fields = self.model._meta.fields 189 index_end = len(fields) 190 has_resolve_columns = hasattr(self, 'resolve_columns') 187 191 while 1: 188 192 rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) … … 190 194 raise StopIteration 191 195 for row in rows: 196 if has_resolve_columns: 197 row = self.resolve_columns(row, fields) 192 198 if fill_cache: 193 199 obj, index_end = get_cached_row(klass=self.model, row=row, … … 553 559 return select, " ".join(sql), params 554 560 561 # Use the backend's QuerySet class if it defines one, otherwise use _QuerySet. 562 if hasattr(backend, 'get_query_set_class'): 563 QuerySet = backend.get_query_set_class(_QuerySet) 564 else: 565 QuerySet = _QuerySet 566 555 567 class ValuesQuerySet(QuerySet): 556 568 def __init__(self, *args, **kwargs): … … 567 579 # self._fields is a list of field names to fetch. 568 580 if self._fields: 569 #columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields]570 581 if not self._select: 571 columns = [self.model._meta.get_field(f, many_to_many=False).columnfor f in self._fields]582 fields = [self.model._meta.get_field(f, many_to_many=False) for f in self._fields] 572 583 else: 573 columns = []584 fields = [] 574 585 for f in self._fields: 575 586 if f in [field.name for field in self.model._meta.fields]: 576 columns.append( self.model._meta.get_field(f, many_to_many=False).column)587 fields.append(self.model._meta.get_field(f, many_to_many=False)) 577 588 elif not self._select.has_key( f ): 578 589 raise FieldDoesNotExist, '%s has no field named %r' % ( self.model._meta.object_name, f ) … … 580 591 field_names = self._fields 581 592 else: # Default to all fields. 582 columns = [f.column for f in self.model._meta.fields] 583 field_names = [f.attname for f in self.model._meta.fields] 584 593 fields = self.model._meta.fields 594 field_names = [f.attname for f in fields] 595 596 columns = [f.column for f in fields] 585 597 select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns] 586 587 598 # Add any additional SELECTs. 588 599 if self._select: … … 591 602 cursor = connection.cursor() 592 603 cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 604 605 has_resolve_columns = hasattr(self, 'resolve_columns') 593 606 while 1: 594 607 rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) … … 596 609 raise StopIteration 597 610 for row in rows: 611 if has_resolve_columns: 612 row = self.resolve_columns(row, fields) 598 613 yield dict(zip(field_names, row)) 599 614 … … 606 621 def iterator(self): 607 622 from django.db.backends.util import typecast_timestamp 623 from django.db.models.fields import DateTimeField 608 624 self._order_by = () # Clear this because it'll mess things up otherwise. 609 625 if self._field.null: 610 626 self._where.append('%s.%s IS NOT NULL' % \ 611 627 (backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column))) 612 613 628 try: 614 629 select, sql, params = self._get_sql_clause() … … 616 631 raise StopIteration 617 632 618 sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \ 633 table_name = backend.quote_name(self.model._meta.db_table) 634 field_name = backend.quote_name(self._field.column) 635 636 if backend.allows_group_by_ordinal: 637 group_by = '1' 638 else: 639 group_by = backend.get_date_trunc_sql(self._kind, 640 '%s.%s' % (table_name, field_name)) 641 642 sql = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s' % \ 619 643 (backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table), 620 backend.quote_name(self._field.column))), sql, self._order)644 backend.quote_name(self._field.column))), sql, group_by, self._order) 621 645 cursor = connection.cursor() 622 646 cursor.execute(sql, params) 623 # We have to manually run typecast_timestamp(str()) on the results, because 624 # MySQL doesn't automatically cast the result of date functions as datetime 625 # objects -- MySQL returns the values as strings, instead. 626 return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()] 647 648 has_resolve_columns = hasattr(self, 'resolve_columns') 649 needs_datetime_string_cast = backend.needs_datetime_string_cast 650 dates = [] 651 # It would be better to use self._field here instead of DateTimeField(), 652 # but in Oracle that will result in a list of datetime.date instead of 653 # datetime.datetime. 654 fields = [DateTimeField()] 655 while 1: 656 rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 657 if not rows: 658 return dates 659 for row in rows: 660 date = row[0] 661 if has_resolve_columns: 662 date = self.resolve_columns([date], fields)[0] 663 elif needs_datetime_string_cast: 664 date = typecast_timestamp(str(date)) 665 dates.append(date) 627 666 628 667 def _clone(self, klass=None, **kwargs): … … 732 771 table_prefix = backend.quote_name(table_prefix[:-1])+'.' 733 772 field_name = backend.quote_name(field_name) 773 if type(value) == datetime.datetime and backend.get_datetime_cast_sql(): 774 cast_sql = backend.get_datetime_cast_sql() 775 else: 776 cast_sql = '%s' 777 if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith') and backend.needs_upper_for_iops: 778 format = 'UPPER(%s%s) %s' 779 else: 780 format = '%s%s %s' 734 781 try: 735 return '%s%s %s' % (table_prefix, field_name, (backend.OPERATOR_MAPPING[lookup_type] % '%s')) 782 return format % (table_prefix, field_name, 783 backend.OPERATOR_MAPPING[lookup_type] % cast_sql) 736 784 except KeyError: 737 785 pass django/trunk/django/newforms/widgets.py
r5514 r5519 9 9 10 10 from itertools import chain 11 12 11 from django.utils.datastructures import MultiValueDict 13 12 from django.utils.html import escape 14 13 from django.utils.translation import gettext 15 14 from django.utils.encoding import StrAndUnicode, smart_unicode 16 17 15 from util import flatatt 18 16 django/trunk/django/test/utils.py
r5511 r5519 1 1 import sys, time 2 2 from django.conf import settings 3 from django.db import connection, transaction, backend 3 from django.db import connection, backend, get_creation_module 4 from django.core import management, mail 4 5 from django.core import management, mail 5 6 from django.dispatch import dispatcher … … 89 90 90 91 def create_test_db(verbosity=1, autoclobber=False): 92 # If the database backend wants to create the test DB itself, let it 93 creation_module = get_creation_module() 94 if hasattr(creation_module, "create_test_db"): 95 creation_module.create_test_db(settings, connection, backend, verbosity, autoclobber) 96 return 97 91 98 if verbosity >= 1: 92 99 print "Creating test database..." … … 143 150 144 151 def destroy_test_db(old_database_name, verbosity=1): 152 # If the database wants to drop the test DB itself, let it 153 creation_module = get_creation_module() 154 if hasattr(creation_module, "destroy_test_db"): 155 creation_module.destroy_test_db(settings, connection, backend, old_database_name, verbosity) 156 return 157 145 158 # Unless we're using SQLite, remove the test database to clean up after 146 159 # ourselves. Connect to the previous database (not the test database) django/trunk/docs/faq.txt
r5064 r5519 302 302 If you want to use Django with a database, which is probably the case, you'll 303 303 also need a database engine. PostgreSQL_ is recommended, because we're 304 PostgreSQL fans, and MySQL_ and `SQLite 3`_ are also supported.304 PostgreSQL fans, and MySQL_, `SQLite 3`_, and Oracle_ are also supported. 305 305 306 306 .. _Python: http://www.python.org/ … … 311 311 .. _MySQL: http://www.mysql.com/ 312 312 .. _`SQLite 3`: http://www.sqlite.org/ 313 .. _Oracle: http://www.oracle.com/ 313 314 314 315 Do I lose anything by using Python 2.3 versus newer Python versions, such as Python 2.5? django/trunk/docs/install.txt
r5503 r5519 65 65 * If you're using SQLite, you'll need pysqlite_. Use version 2.0.3 or higher. 66 66 67 * If you're using Oracle, you'll need cx_Oracle_, version 4.3.1 or higher. 68 67 69 .. _PostgreSQL: http://www.postgresql.org/ 68 70 .. _MySQL: http://www.mysql.com/ … … 74 76 .. _pysqlite: http://initd.org/tracker/pysqlite 75 77 .. _MySQL backend: ../databases/ 78 .. _cx_Oracle: http://www.python.net/crew/atuining/cx_Oracle/ 76 79 77 80 Remove any old versions of Django django/trunk/docs/model-api.txt
r5505 r5519 493 493 string, not ``NULL``. 494 494 495 .. note:: 496 Due to database limitations, when using the Oracle backend the 497 ``null=True`` option will be coerced for string-based fields that can 498 blank, and the value ``NULL`` will be stored to denote the empty string. 499 495 500 ``blank`` 496 501 ~~~~~~~~~ … … 586 591 If ``True``, ``django-admin.py sqlindexes`` will output a ``CREATE INDEX`` 587 592 statement for this field. 593 594 ``db_tablespace`` 595 ~~~~~~~~~~~~~~~~~ 596 597 If this field is indexed, the name of the database tablespace to use for the 598 index. The default is the ``db_tablespace`` of the model, if any. If the 599 backend doesn't support tablespaces, this option is ignored. 588 600 589 601 ``default`` … … 996 1008 that aren't allowed in Python variable names -- notably, the hyphen -- 997 1009 that's OK. Django quotes column and table names behind the scenes. 1010 1011 ``db_tablespace`` 1012 ----------------- 1013 1014 The name of the database tablespace to use for the model. If the backend 1015 doesn't support tablespaces, this option is ignored. 998 1016 999 1017 ``get_latest_by`` django/trunk/docs/settings.txt
r5413 r5519 245 245 Default: ``''`` (Empty string) 246 246 247 Which database backend to use. Either ``'postgresql_psycopg2'``, 248 ``'postgresql'``, ``'mysql'``, ``'mysql_old'``, ``'sqlite3'`` or249 ``' ado_mssql'``.247 The database backend to use. Either ``'postgresql_psycopg2'``, 248 ``'postgresql'``, ``'mysql'``, ``'mysql_old'``, ``'sqlite3'``, 249 ``'oracle'``, or ``'ado_mssql'``. 250 250 251 251 DATABASE_HOST django/trunk/tests/regressiontests/datatypes/models.py
r5416 r5519 5 5 6 6 from django.db import models 7 from django.conf import settings 7 8 8 9 class Donut(models.Model): … … 32 33 >>> d.has_sprinkles 33 34 >>> d.has_sprinkles = True 34 >>> d.has_sprinkles 35 >>> d.has_sprinkles == True 35 36 True 36 37 >>> d.save() … … 38 39 >>> d2 39 40 <Donut: Apple Fritter> 40 >>> d2.is_frosted 41 False42 >>> d2.has_sprinkles 41 >>> d2.is_frosted == False 42 True 43 >>> d2.has_sprinkles == True 43 44 True 44 45 django/trunk/tests/regressiontests/serializers_regress/tests.py
r5453 r5519 16 16 from django.db import transaction 17 17 from django.core import management 18 from django.conf import settings 18 19 19 20 from models import * … … 117 118 (data_obj, 40, EmailData, "hovercraft@example.com"), 118 119 (data_obj, 41, EmailData, None), 120 (data_obj, 42, EmailData, ""), 119 121 (data_obj, 50, FileData, 'file:///foo/bar/whiz.txt'), 120 122 (data_obj, 51, FileData, None), 123 (data_obj, 52, FileData, ""), 121 124 (data_obj, 60, FilePathData, "/foo/bar/whiz.txt"), 122 125 (data_obj, 61, FilePathData, None), 126 (data_obj, 62, FilePathData, ""), 123 127 (data_obj, 70, DecimalData, decimal.Decimal('12.345')), 124 128 (data_obj, 71, DecimalData, decimal.Decimal('-12.345')), … … 147 151 (data_obj, 140, SlugData, "this-is-a-slug"), 148 152 (data_obj, 141, SlugData, None), 153 (data_obj, 142, SlugData, ""), 149 154 (data_obj, 150, SmallData, 12), 150 155 (data_obj, 151, SmallData, -12), … … 161 166 (data_obj, 180, USStateData, "MA"), 162 167 (data_obj, 181, USStateData, None), 168 (data_obj, 182, USStateData, ""), 163 169 (data_obj, 190, XMLData, "<foo></foo>"), 164 170 (data_obj, 191, XMLData, None), 171 (data_obj, 192, XMLData, ""), 165 172 166 173 (generic_obj, 200, GenericData, ['Generic Object 1', 'tag1', 'tag2']), … … 242 249 ] 243 250 251 # Because Oracle treats the empty string as NULL, Oracle is expected to fail 252 # when field.empty_strings_allowed is True and the value is None; skip these 253 # tests. 254 if settings.DATABASE_ENGINE == 'oracle': 255 test_data = [data for data in test_data 256 if not (data[0] == data_obj and 257 data[2]._meta.get_field('data').empty_strings_allowed and 258 data[3] is None)] 259 244 260 # Dynamically create serializer tests to ensure that all 245 261 # registered serializers are automatically tested.
