OracleBranch: django-oracle-rev5392.diff
| File django-oracle-rev5392.diff, 96.1 kB (added by bouldersprinters, 1 year ago) |
|---|
-
django/test/utils.py
old new 1 1 import sys, time 2 2 from django.conf import settings 3 from django.db import connection, transaction, backend3 from django.db import connection, backend, get_creation_module 4 4 from django.core import management, mail 5 5 from django.dispatch import dispatcher 6 6 from django.test import signals … … 88 88 return '' 89 89 90 90 def create_test_db(verbosity=1, autoclobber=False): 91 # If the database backend wants to create the test DB itself, let it 92 creation_module = get_creation_module() 93 if hasattr(creation_module, "create_test_db"): 94 creation_module.create_test_db(settings, connection, backend, verbosity, autoclobber) 95 return 96 91 97 if verbosity >= 1: 92 98 print "Creating test database..." 93 99 # If we're using SQLite, it's more convenient to test against an … … 142 148 cursor = connection.cursor() 143 149 144 150 def destroy_test_db(old_database_name, verbosity=1): 151 # If the database wants to drop the test DB itself, let it 152 creation_module = get_creation_module() 153 if hasattr(creation_module, "destroy_test_db"): 154 creation_module.destroy_test_db(settings, connection, backend, old_database_name, verbosity) 155 return 156 145 157 # Unless we're using SQLite, remove the test database to clean up after 146 158 # ourselves. Connect to the previous database (not the test database) 147 159 # to do so, because it's not allowed to delete a database while being -
django/db/models/base.py
old new 96 96 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. 104 104 args_len = len(args) … … 122 122 # Maintain compatibility with existing calls. 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: 131 131 if isinstance(field.rel, ManyToOneRel): … … 147 147 try: 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: 153 153 val = kwargs.pop(field.attname, field.get_default()) … … 210 210 record_exists = True 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: 219 220 cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \ 220 221 (backend.quote_name(self._meta.db_table), 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 226 227 if not pk_set or not record_exists: -
django/db/models/options.py
old new 13 13 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): 19 19 def __init__(self, meta): … … 27 27 self.object_name, self.app_label = None, None 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 32 33 self.pk = None … … 59 60 del self.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) 64 67 self.ordering = ('_order',) … … 73 76 # If the db_table wasn't provided, use the app_label + module_name. 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): 78 83 # Insert the given field in the order in which it was created, using … … 88 93 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." 97 102 to_search = many_to_many and (self.fields + self.many_to_many) or self.fields -
django/db/models/fields/__init__.py
old new 74 74 core=False, rel=None, default=NOT_PROVIDED, editable=True, serialize=True, 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 80 80 self.primary_key = primary_key 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 85 89 self.serialize = serialize … … 91 95 self.radio_admin = radio_admin 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. 96 101 self.db_index = db_index … … 875 880 Field.__init__(self, verbose_name, name, **kwargs) 876 881 877 882 def get_db_prep_lookup(self, lookup_type, value): 883 if settings.DATABASE_ENGINE == 'oracle': 884 # Oracle requires a date in order to parse. 885 def prep(value): 886 if isinstance(value, datetime.time): 887 value = datetime.datetime.combine(datetime.date(1900, 1, 1), value) 888 return str(value) 889 else: 890 prep = str 878 891 if lookup_type == 'range': 879 value = [ str(v) for v in value]892 value = [prep(v) for v in value] 880 893 else: 881 value = str(value)894 value = prep(value) 882 895 return Field.get_db_prep_lookup(self, lookup_type, value) 883 896 884 897 def pre_save(self, model_instance, add): … … 896 909 # doesn't support microseconds. 897 910 if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'): 898 911 value = value.replace(microsecond=0) 912 if settings.DATABASE_ENGINE == 'oracle': 913 # cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field. 914 if isinstance(value, datetime.time): 915 value = datetime.datetime(1900, 1, 1, value.hour, value.minute, 916 value.second, value.microsecond) 917 elif isinstance(value, basestring): 918 value = datetime.datetime(*(time.strptime(value, '%H:%M:%S')[:6])) 919 else: 899 920 value = str(value) 900 921 return Field.get_db_prep_save(self, value) 901 922 -
django/db/models/fields/related.py
old new 335 335 (target_col_name, self.join_table, source_col_name, 336 336 target_col_name, ",".join(['%s'] * len(new_ids))), 337 337 [self._pk_val] + list(new_ids)) 338 if cursor.rowcount is not None and cursor.rowcount != 0: 339 existing_ids = set([row[0] for row in cursor.fetchmany(cursor.rowcount)]) 340 else: 341 existing_ids = set() 338 existing_ids = set([row[0] for row in cursor.fetchall()]) 342 339 343 340 # Add the ones that aren't there already 344 341 for obj_id in (new_ids - existing_ids): -
django/db/models/query.py
old new 4 4 from django.dispatch import dispatcher 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 9 10 … … 77 78 else: 78 79 return backend.quote_name(word) 79 80 80 class QuerySet(object):81 class _QuerySet(object): 81 82 "Represents a lazy database lookup for a set of objects" 82 83 def __init__(self, model=None): 83 84 self.model = model … … 181 182 182 183 cursor = connection.cursor() 183 184 cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 185 184 186 fill_cache = self._select_related 185 index_end = len(self.model._meta.fields) 187 fields = self.model._meta.fields 188 index_end = len(fields) 189 has_resolve_columns = hasattr(self, 'resolve_columns') 186 190 while 1: 187 191 rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 188 192 if not rows: 189 193 raise StopIteration 190 194 for row in rows: 195 if has_resolve_columns: 196 row = self.resolve_columns(row, fields) 191 197 if fill_cache: 192 198 obj, index_end = get_cached_row(klass=self.model, row=row, 193 199 index_start=0, max_depth=self._max_related_depth) … … 551 557 552 558 return select, " ".join(sql), params 553 559 560 # Use the backend's QuerySet class if it defines one, otherwise use _QuerySet. 561 if hasattr(backend, 'get_query_set_class'): 562 QuerySet = backend.get_query_set_class(_QuerySet) 563 else: 564 QuerySet = _QuerySet 565 554 566 class ValuesQuerySet(QuerySet): 555 567 def __init__(self, *args, **kwargs): 556 568 super(ValuesQuerySet, self).__init__(*args, **kwargs) 557 # select_related isn't supported in values().569 # select_related and select aren't supported in values(). 558 570 self._select_related = False 571 self._select = {} 559 572 560 573 def iterator(self): 561 574 try: … … 565 578 566 579 # self._fields is a list of field names to fetch. 567 580 if self._fields: 568 #columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields] 569 if not self._select: 570 columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields] 571 else: 572 columns = [] 573 for f in self._fields: 574 if f in [field.name for field in self.model._meta.fields]: 575 columns.append( self.model._meta.get_field(f, many_to_many=False).column ) 576 elif not self._select.has_key( f ): 577 raise FieldDoesNotExist, '%s has no field named %r' % ( self.model._meta.object_name, f ) 578 579 field_names = self._fields 581 fields = [self.model._meta.get_field(f, many_to_many=False) for f in self._fields] 580 582 else: # Default to all fields. 581 columns = [f.column for f in self.model._meta.fields] 582 field_names = [f.attname for f in self.model._meta.fields] 583 fields = self.model._meta.fields 584 columns = [f.column for f in fields] 585 field_names = [f.attname for f in fields] 583 586 584 587 select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns] 585 586 # Add any additional SELECTs.587 if self._select:588 select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()])589 590 588 cursor = connection.cursor() 591 589 cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 590 591 has_resolve_columns = hasattr(self, 'resolve_columns') 592 592 while 1: 593 593 rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 594 594 if not rows: 595 595 raise StopIteration 596 596 for row in rows: 597 if has_resolve_columns: 598 row = self.resolve_columns(row, fields) 597 599 yield dict(zip(field_names, row)) 598 600 599 601 def _clone(self, klass=None, **kwargs): … … 604 606 class DateQuerySet(QuerySet): 605 607 def iterator(self): 606 608 from django.db.backends.util import typecast_timestamp 609 from django.db.models.fields import DateTimeField 607 610 self._order_by = () # Clear this because it'll mess things up otherwise. 608 611 if self._field.null: 609 612 self._where.append('%s.%s IS NOT NULL' % \ 610 613 (backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column))) 611 612 614 try: 613 615 select, sql, params = self._get_sql_clause() 614 616 except EmptyResultSet: 615 617 raise StopIteration 616 618 617 sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \ 619 table_name = backend.quote_name(self.model._meta.db_table) 620 field_name = backend.quote_name(self._field.column) 621 622 if backend.allows_group_by_ordinal: 623 group_by = '1' 624 else: 625 group_by = backend.get_date_trunc_sql(self._kind, 626 '%s.%s' % (table_name, field_name)) 627 628 sql = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s' % \ 618 629 (backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table), 619 backend.quote_name(self._field.column))), sql, self._order)630 backend.quote_name(self._field.column))), sql, group_by, self._order) 620 631 cursor = connection.cursor() 621 632 cursor.execute(sql, params) 622 # We have to manually run typecast_timestamp(str()) on the results, because623 # MySQL doesn't automatically cast the result of date functions as datetime624 # objects -- MySQL returns the values as strings, instead.625 return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()]626 633 634 has_resolve_columns = hasattr(self, 'resolve_columns') 635 needs_datetime_string_cast = backend.needs_datetime_string_cast 636 dates = [] 637 # It would be better to use self._field here instead of DateTimeField(), 638 # but in Oracle that will result in a list of datetime.date instead of 639 # datetime.datetime. 640 fields = [DateTimeField()] 641 while 1: 642 rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 643 if not rows: 644 return dates 645 for row in rows: 646 date = row[0] 647 if has_resolve_columns: 648 date = self.resolve_columns([date], fields)[0] 649 elif needs_datetime_string_cast: 650 date = typecast_timestamp(str(date)) 651 dates.append(date) 652 627 653 def _clone(self, klass=None, **kwargs): 628 654 c = super(DateQuerySet, self)._clone(klass, **kwargs) 629 655 c._field = self._field … … 730 756 if table_prefix.endswith('.'): 731 757 table_prefix = backend.quote_name(table_prefix[:-1])+'.' 732 758 field_name = backend.quote_name(field_name) 759 if type(value) == datetime.datetime and backend.get_datetime_cast_sql(): 760 cast_sql = backend.get_datetime_cast_sql() 761 else: 762 cast_sql = '%s' 763 if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith') and backend.needs_upper_for_iops: 764 format = 'UPPER(%s%s) %s' 765 else: 766 format = '%s%s %s' 733 767 try: 734 return '%s%s %s' % (table_prefix, field_name, (backend.OPERATOR_MAPPING[lookup_type] % '%s')) 768 return format % (table_prefix, field_name, 769 backend.OPERATOR_MAPPING[lookup_type] % cast_sql) 735 770 except KeyError: 736 771 pass 737 772 if lookup_type == 'in': -
django/db/backends/ado_mssql/base.py
old new 89 89 self.connection.close() 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): 95 102 if name.startswith('[') and name.endswith(']'): … … 117 124 if lookup_type=='day': 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. 122 132 sql = "LIMIT %s" % limit … … 139 149 def get_pk_default_value(): 140 150 return "DEFAULT" 141 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 163 142 164 def get_sql_flush(style, tables, sequences): 143 165 """Return a list of SQL statements required to remove all data from 144 166 all tables in the database (without actually removing the tables -
django/db/backends/postgresql/base.py
old new 87 87 global postgres_version 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) 93 93 return cursor … … 105 105 self.connection.close() 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): 111 118 if name.startswith('"') and name.endswith('"'): … … 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) 140 147 148 def get_datetime_cast_sql(): 149 return None 150 141 151 def get_limit_offset_sql(limit, offset=None): 142 152 sql = "LIMIT %s" % limit 143 153 if offset and offset != 0: … … 149 159 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 155 165 … … 159 169 def get_pk_default_value(): 160 170 return "DEFAULT" 161 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 180 162 181 def get_sql_flush(style, tables, sequences): 163 182 """Return a list of SQL statements required to remove all data from 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: 170 189 # Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to* in order to be able to … … 175 194 style.SQL_FIELD(', '.join([quote_name(table) for table in tables])) 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;' % \ 181 200 (style.SQL_KEYWORD('DELETE'), … … 238 257 style.SQL_KEYWORD('FROM'), 239 258 style.SQL_TABLE(f.m2m_db_table()))) 240 259 return output 241 260 242 261 # Register these custom typecasts, because Django expects dates/times to be 243 262 # in Python's native (standard-library) datetime/time format, whereas psycopg 244 263 # use mx.DateTime by default. -
django/db/backends/sqlite3/base.py
old new 107 107 def convert_query(self, query, num_params): 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): 113 120 if name.startswith('"') and name.endswith('"'): … … 139 146 # sqlite doesn't support DATE_TRUNC, so we fake it as above. 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 144 154 if offset and offset != 0: … … 160 170 def get_pk_default_value(): 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 170 189 # Note: The DELETE FROM... SQL generated below works for SQLite databases … … 182 201 "Returns a list of the SQL statements to reset sequences for the given models." 183 202 # No sequence reset required 184 203 return [] 185 204 186 205 def _sqlite_date_trunc(lookup_type, dt): 187 206 try: 188 207 dt = util.typecast_timestamp(dt) -
django/db/backends/util.py
old new 1 1 import datetime 2 import md5 2 3 from time import time 3 4 4 5 try: … … 107 108 return None 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 # 112 123 ################################################################################## -
django/db/backends/mysql/base.py
old new 134 134 self.server_version = tuple([int(x) for x in m.groups()]) 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): 140 147 if name.startswith("`") and name.endswith("`"): … … 167 174 sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str) 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 " 172 182 if offset and offset != 0: … … 188 198 def get_pk_default_value(): 189 199 return "DEFAULT" 190 200 201 def get_max_name_length(): 202 return 64; 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 198 217 # 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements … … 204 223 style.SQL_FIELD(quote_name(table)) 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 210 229 sql.extend(["%s %s %s %s %s;" % \ -
django/db/backends/oracle/base.py
old new 4 4 Requires cx_Oracle: http://www.python.net/crew/atuining/cx_Oracle/ 5 5 """ 6 6 7 from django.conf import settings 7 8 from django.db.backends import util 8 9 try: 9 10 import cx_Oracle as Database 10 11 except ImportError, e: 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 13 16 17 14 18 DatabaseError = Database.Error 15 19 IntegrityError = Database.IntegrityError 16 20 … … 31 35 return self.connection is not None 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: 37 40 settings.DATABASE_HOST = 'localhost' … … 41 44 else: 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): 58 66 if self.connection is not None: 59 67 self.connection.close() 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): 65 80 """ … … 67 82 This fixes it -- but note that if you want to use a literal "%s" in a query, 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 89 124 dictfetchmany = util.dictfetchmany 90 125 dictfetchall = util.dictfetchall 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 104 146 147 def get_datetime_cast_sql(): 148 return "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS.FF')" 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(): 111 156 return "DBMS_RANDOM.RANDOM" … … 117 162 raise NotImplementedError 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" 124 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 /""" 217 125 218 def get_sql_flush(style, tables, sequences): 126 219 """Return a list of SQL statements required to remove all data from 127 220 all tables in the database (without actually removing the tables 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'), 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'), 135 231 style.SQL_FIELD(quote_name(table)) 136 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: 24
