OracleBranch: django-oracle-rev5036.diff
| File django-oracle-rev5036.diff, 87.4 kB (added by ian.g.kelly@gmail.com, 1 year ago) |
|---|
-
django.oracle/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 5 5 from django.dispatch import dispatcher 6 6 from django.test import signals … … 44 44 connection.connection.set_isolation_level(0) 45 45 46 46 def create_test_db(verbosity=1, autoclobber=False): 47 # If the database backend wants to create the test DB itself, let it 48 creation_module = get_creation_module() 49 if hasattr(creation_module, "create_test_db"): 50 creation_module.create_test_db(settings, connection, backend, verbosity, autoclobber) 51 return 52 47 53 if verbosity >= 1: 48 54 print "Creating test database..." 49 55 # If we're using SQLite, it's more convenient to test against an … … 92 98 cursor = connection.cursor() 93 99 94 100 def destroy_test_db(old_database_name, verbosity=1): 101 # If the database wants to drop the test DB itself, let it 102 creation_module = get_creation_module() 103 if hasattr(creation_module, "destroy_test_db"): 104 creation_module.destroy_test_db(settings, connection, backend, old_database_name, verbosity) 105 return 106 95 107 # Unless we're using SQLite, remove the test database to clean up after 96 108 # ourselves. Connect to the previous database (not the test database) 97 109 # to do so, because it's not allowed to delete a database while being -
django.oracle/django/db/models/base.py
old new 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" % \213 cursor.execute("SELECT COUNT(*) FROM %s WHERE %s=%%s" % \ 214 214 (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), [pk_val]) 215 215 # If it does already exist, do an UPDATE. 216 if cursor.fetchone() :216 if cursor.fetchone()[0] > 0: 217 217 db_values = [f.get_db_prep_save(f.pre_save(self, False)) for f in non_pks] 218 218 if db_values: 219 219 cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \ -
django.oracle/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.oracle/django/db/models/fields/__init__.py
old new 70 70 core=False, rel=None, default=NOT_PROVIDED, editable=True, serialize=True, 71 71 prepopulate_from=None, unique_for_date=None, unique_for_month=None, 72 72 unique_for_year=None, validator_list=None, choices=None, radio_admin=None, 73 help_text='', db_column=None ):73 help_text='', db_column=None, db_tablespace=None): 74 74 self.name = name 75 75 self.verbose_name = verbose_name 76 76 self.primary_key = primary_key … … 87 87 self.radio_admin = radio_admin 88 88 self.help_text = help_text 89 89 self.db_column = db_column 90 self.db_tablespace = db_tablespace 90 91 91 92 # Set db_index to True if the field has a relationship and doesn't explicitly set db_index. 92 93 self.db_index = db_index … … 161 162 162 163 def get_db_prep_save(self, value): 163 164 "Returns field's value prepared for saving into a database." 165 # Oracle treats empty strings ('') the same as NULLs. 166 # To get around this wart, we need to change it to something else... 167 if settings.DATABASE_ENGINE == 'oracle' and value == '': 168 value = ' ' 164 169 return value 165 170 166 171 def get_db_prep_lookup(self, lookup_type, value): … … 528 533 def get_db_prep_save(self, value): 529 534 # Casts dates into string format for entry into database. 530 535 if value is not None: 531 # MySQL will throw a warning if microseconds are given, because it532 # doesn't supportmicroseconds.533 if settings.DATABASE_ENGINE == 'mysql'and hasattr(value, 'microsecond'):536 # MySQL/Oracle will throw a warning if microseconds are given, because 537 # neither database supports microseconds. 538 if settings.DATABASE_ENGINE in ('mysql', 'oracle') and hasattr(value, 'microsecond'): 534 539 value = value.replace(microsecond=0) 535 value = str(value)536 540 return Field.get_db_prep_save(self, value) 537 541 538 542 def get_db_prep_lookup(self, lookup_type, value): 543 # Oracle will throw an error if microseconds are given, because it 544 # doesn't support microseconds. 545 if settings.DATABASE_ENGINE == 'oracle' and hasattr(value, 'microsecond'): 546 value = value.replace(microsecond=0) 539 547 if lookup_type == 'range': 540 548 value = [str(v) for v in value] 541 549 else: … … 808 816 Field.__init__(self, verbose_name, name, **kwargs) 809 817 810 818 def get_db_prep_lookup(self, lookup_type, value): 819 if settings.DATABASE_ENGINE == 'oracle': 820 # Oracle requires a date in order to parse. 821 def prep(value): 822 if isinstance(value, datetime.time): 823 value = datetime.datetime.combine(datetime.date(1900, 1, 1), value) 824 return str(value) 825 else: 826 prep = str 811 827 if lookup_type == 'range': 812 value = [ str(v) for v in value]828 value = [prep(v) for v in value] 813 829 else: 814 value = str(value)830 value = prep(value) 815 831 return Field.get_db_prep_lookup(self, lookup_type, value) 816 832 817 833 def pre_save(self, model_instance, add): … … 827 843 if value is not None: 828 844 # MySQL will throw a warning if microseconds are given, because it 829 845 # doesn't support microseconds. 830 if settings.DATABASE_ENGINE == 'mysql'and hasattr(value, 'microsecond'):846 if settings.DATABASE_ENGINE in ('mysql', 'oracle') and hasattr(value, 'microsecond'): 831 847 value = value.replace(microsecond=0) 832 value = str(value) 848 if settings.DATABASE_ENGINE == 'oracle': 849 # cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field. 850 if isinstance(value, datetime.time): 851 value = datetime.datetime(1900, 1, 1, value.hour, value.minute, value.second) 852 elif isinstance(value, basestring): 853 value = datetime.datetime(*(time.strptime(value, '%H:%M:%S')[:6])) 854 else: 855 value = str(value) 833 856 return Field.get_db_prep_save(self, value) 834 857 835 858 def get_manipulator_field_objs(self): -
django.oracle/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.oracle/django/db/models/query.py
old new 4 4 from django.db.models import signals 5 5 from django.dispatch import dispatcher 6 6 from django.utils.datastructures import SortedDict 7 from django.conf import settings 8 import datetime 7 9 import operator 8 10 import re 9 11 … … 77 79 else: 78 80 return backend.quote_name(word) 79 81 80 class QuerySet(object):82 class _QuerySet(object): 81 83 "Represents a lazy database lookup for a set of objects" 82 84 def __init__(self, model=None): 83 85 self.model = model … … 181 183 182 184 cursor = connection.cursor() 183 185 cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 186 184 187 fill_cache = self._select_related 185 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') 186 191 while 1: 187 192 rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 188 193 if not rows: 189 194 raise StopIteration 190 195 for row in rows: 196 if has_resolve_columns: 197 row = self.resolve_columns(row, fields) 191 198 if fill_cache: 192 199 obj, index_end = get_cached_row(klass=self.model, row=row, 193 200 index_start=0, max_depth=self._max_related_depth) … … 551 558 552 559 return select, " ".join(sql), params 553 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 554 567 class ValuesQuerySet(QuerySet): 555 568 def __init__(self, *args, **kwargs): 556 569 super(ValuesQuerySet, self).__init__(*args, **kwargs) … … 566 579 567 580 # self._fields is a list of field names to fetch. 568 581 if self._fields: 569 columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields] 570 field_names = self._fields 582 fields = [self.model._meta.get_field(f, many_to_many=False) for f in self._fields] 571 583 else: # Default to all fields. 572 columns = [f.column for f in self.model._meta.fields] 573 field_names = [f.attname for f in self.model._meta.fields] 584 fields = self.model._meta.fields 585 columns = [f.column for f in fields] 586 field_names = [f.attname for f in fields] 574 587 575 588 select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns] 576 589 cursor = connection.cursor() 577 590 cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 591 592 has_resolve_columns = hasattr(self, 'resolve_columns') 578 593 while 1: 579 594 rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 580 595 if not rows: 581 596 raise StopIteration 582 597 for row in rows: 598 if has_resolve_columns: 599 row = self.resolve_columns(row, fields) 583 600 yield dict(zip(field_names, row)) 584 601 585 602 def _clone(self, klass=None, **kwargs): … … 590 607 class DateQuerySet(QuerySet): 591 608 def iterator(self): 592 609 from django.db.backends.util import typecast_timestamp 610 from django.db.models.fields import DateTimeField 593 611 self._order_by = () # Clear this because it'll mess things up otherwise. 594 612 if self._field.null: 595 613 self._where.append('%s.%s IS NOT NULL' % \ 596 614 (backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column))) 597 598 615 try: 599 616 select, sql, params = self._get_sql_clause() 600 617 except EmptyResultSet: 601 618 raise StopIteration 602 619 603 sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \ 620 table_name = backend.quote_name(self.model._meta.db_table) 621 field_name = backend.quote_name(self._field.column) 622 623 if backend.allows_group_by_ordinal: 624 group_by = '1' 625 else: 626 group_by = backend.get_date_trunc_sql(self._kind, 627 '%s.%s' % (table_name, field_name)) 628 629 sql = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s' % \ 604 630 (backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table), 605 backend.quote_name(self._field.column))), sql, self._order)631 backend.quote_name(self._field.column))), sql, group_by, self._order) 606 632 cursor = connection.cursor() 607 633 cursor.execute(sql, params) 608 # We have to manually run typecast_timestamp(str()) on the results, because609 # MySQL doesn't automatically cast the result of date functions as datetime610 # objects -- MySQL returns the values as strings, instead.611 return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()]612 634 635 has_resolve_columns = hasattr(self, 'resolve_columns') 636 needs_datetime_string_cast = backend.needs_datetime_string_cast 637 dates = [] 638 # It would be better to use self._field here instead of DateTimeField(), 639 # but in Oracle that will result in a list of datetime.date instead of 640 # datetime.datetime. 641 fields = [DateTimeField()] 642 while 1: 643 rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 644 if not rows: 645 return dates 646 for row in rows: 647 date = row[0] 648 if has_resolve_columns: 649 date = self.resolve_columns([date], fields)[0] 650 elif needs_datetime_string_cast: 651 date = typecast_timestamp(str(date)) 652 dates.append(date) 653 613 654 def _clone(self, klass=None, **kwargs): 614 655 c = super(DateQuerySet, self)._clone(klass, **kwargs) 615 656 c._field = self._field … … 716 757 if table_prefix.endswith('.'): 717 758 table_prefix = backend.quote_name(table_prefix[:-1])+'.' 718 759 field_name = backend.quote_name(field_name) 760 if type(value) == datetime.datetime and backend.get_datetime_cast_sql(): 761 cast_sql = backend.get_datetime_cast_sql() 762 else: 763 cast_sql = '%s' 764 if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith') and backend.needs_upper_for_iops: 765 format = 'UPPER(%s%s) %s' 766 else: 767 format = '%s%s %s' 719 768 try: 720 return '%s%s %s' % (table_prefix, field_name, (backend.OPERATOR_MAPPING[lookup_type] % '%s')) 769 return format % (table_prefix, field_name, 770 backend.OPERATOR_MAPPING[lookup_type] % cast_sql) 721 771 except KeyError: 722 772 pass 723 773 if lookup_type == 'in': -
django.oracle/django/db/backends/ado_mssql/base.py
old new 88 88 self.connection.close() 89 89 self.connection = None 90 90 91 allows_group_by_ordinal = True 92 allows_unique_and_pk = True 93 autoindexes_primary_keys = True 94 needs_datetime_string_cast = True 95 needs_upper_for_iops = False 91 96 supports_constraints = True 97 supports_tablespaces = True 98 uses_case_insensitive_names = False 92 99 93 100 def quote_name(name): 94 101 if name.startswith('[') and name.endswith(']'): … … 116 123 if lookup_type=='day': 117 124 return "Convert(datetime, Convert(varchar(12), %s))" % field_name 118 125 126 def get_datetime_cast_sql(): 127 return None 128 119 129 def get_limit_offset_sql(limit, offset=None): 120 130 # TODO: This is a guess. Make sure this is correct. 121 131 sql = "LIMIT %s" % limit … … 138 148 def get_pk_default_value(): 139 149 return "DEFAULT" 140 150 151 def get_max_name_length(): 152 return None 153 154 def get_start_transaction_sql(): 155 return "BEGIN;" 156 157 def get_tablespace_sql(tablespace, inline=False): 158 return "ON %s" % quote_name(tablespace) 159 160 def get_autoinc_sql(table): 161 return None 162 141 163 def get_sql_flush(style, tables, sequences): 142 164 """Return a list of SQL statements required to remove all data from 143 165 all tables in the database (without actually removing the tables -
django.oracle/django/db/backends/postgresql/base.py
old new 86 86 global postgres_version 87 87 if not postgres_version: 88 88 cursor.execute("SELECT version()") 89 postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')] 89 postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')] 90 90 if settings.DEBUG: 91 91 return util.CursorDebugWrapper(cursor, self) 92 92 return cursor … … 104 104 self.connection.close() 105 105 self.connection = None 106 106 107 allows_group_by_ordinal = True 108 allows_unique_and_pk = True 109 autoindexes_primary_keys = True 110 needs_datetime_string_cast = True 111 needs_upper_for_iops = False 107 112 supports_constraints = True 113 supports_tablespaces = False 114 uses_case_insensitive_names = False 108 115 109 116 def quote_name(name): 110 117 if name.startswith('"') and name.endswith('"'): … … 137 144 # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC 138 145 return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) 139 146 147 def get_datetime_cast_sql(): 148 return None 149 140 150 def get_limit_offset_sql(limit, offset=None): 141 151 sql = "LIMIT %s" % limit 142 152 if offset and offset != 0: … … 148 158 149 159 def get_deferrable_sql(): 150 160 return " DEFERRABLE INITIALLY DEFERRED" 151 161 152 162 def get_fulltext_search_sql(field_name): 153 163 raise NotImplementedError 154 164 … … 158 168 def get_pk_default_value(): 159 169 return "DEFAULT" 160 170 171 def get_max_name_length(): 172 return None 173 174 def get_start_transaction_sql(): 175 return "BEGIN;" 176 177 def get_autoinc_sql(table): 178 return None 179 161 180 def get_sql_flush(style, tables, sequences): 162 181 """Return a list of SQL statements required to remove all data from 163 182 all tables in the database (without actually removing the tables 164 183 themselves) and put the database in an empty 'initial' state 165 166 """ 184 185 """ 167 186 if tables: 168 187 if postgres_version[0] >= 8 and postgres_version[1] >= 1: 169 188 # Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to* in order to be able to … … 174 193 style.SQL_FIELD(', '.join([quote_name(table) for table in tables])) 175 194 )] 176 195 else: 177 # Older versions of Postgres can't do TRUNCATE in a single call, so they must use 196 # Older versions of Postgres can't do TRUNCATE in a single call, so they must use 178 197 # a simple delete. 179 198 sql = ['%s %s %s;' % \ 180 199 (style.SQL_KEYWORD('DELETE'), … … 237 256 style.SQL_KEYWORD('FROM'), 238 257 style.SQL_TABLE(f.m2m_db_table()))) 239 258 return output 240 259 241 260 # Register these custom typecasts, because Django expects dates/times to be 242 261 # in Python's native (standard-library) datetime/time format, whereas psycopg 243 262 # use mx.DateTime by default. -
django.oracle/django/db/backends/sqlite3/base.py
old new 99 99 def convert_query(self, query, num_params): 100 100 return query % tuple("?" * num_params) 101 101 102 allows_group_by_ordinal = True 103 allows_unique_and_pk = True 104 autoindexes_primary_keys = True 105 needs_datetime_string_cast = True 106 needs_upper_for_iops = False 102 107 supports_constraints = False 108 supports_tablespaces = False 109 uses_case_insensitive_names = False 103 110 104 111 def quote_name(name): 105 112 if name.startswith('"') and name.endswith('"'): … … 131 138 # sqlite doesn't support DATE_TRUNC, so we fake it as above. 132 139 return 'django_date_trunc("%s", %s)' % (lookup_type.lower(), field_name) 133 140 141 def get_datetime_cast_sql(): 142 return None 143 134 144 def get_limit_offset_sql(limit, offset=None): 135 145 sql = "LIMIT %s" % limit 136 146 if offset and offset != 0: … … 152 162 def get_pk_default_value(): 153 163 return "NULL" 154 164 165 def get_max_name_length(): 166 return None 167 168 def get_start_transaction_sql(): 169 return "BEGIN;" 170 171 def get_autoinc_sql(table): 172 return None 173 155 174 def get_sql_flush(style, tables, sequences): 156 175 """Return a list of SQL statements required to remove all data from 157 176 all tables in the database (without actually removing the tables 158 177 themselves) and put the database in an empty 'initial' state 159 178 160 179 """ 161 180 # NB: The generated SQL below is specific to SQLite 162 181 # Note: The DELETE FROM... SQL generated below works for SQLite databases … … 174 193 "Returns a list of the SQL statements to reset sequences for the given models." 175 194 # No sequence reset required 176 195 return [] 177 196 178 197 def _sqlite_date_trunc(lookup_type, dt): 179 198 try: 180 199 dt = util.typecast_timestamp(dt) -
django.oracle/django/db/backends/util.py
old new 1 1 import datetime 2 import md5 2 3 from time import time 3 4 4 5 class CursorDebugWrapper(object): … … 92 93 def rev_typecast_boolean(obj, d): 93 94 return obj and '1' or '0' 94 95 96 def truncate_name(name, length=None): 97 """Shortens a string to a repeatable mangled version with the given length. 98 """ 99 if length is None or len(name) <= length: 100 return name 101 102 hash = md5.md5(name).hexdigest()[:4] 103 104 return '%s%s' % (name[:length-4], hash) 105 95 106 ################################################################################## 96 107 # Helper functions for dictfetch* for databases that don't natively support them # 97 108 ################################################################################## -
django.oracle/django/db/backends/mysql/base.py
old new 15 15 # lexicographic ordering in this check because then (1, 2, 1, 'gamma') 16 16 # inadvertently passes the version test. 17 17 version = Database.version_info 18 if (version < (1,2,1) or (version[:3] == (1, 2, 1) and 18 if (version < (1,2,1) or (version[:3] == (1, 2, 1) and 19 19 (len(version) < 5 or version[3] != 'final' or version[4] < 2))): 20 20 raise ImportError, "MySQLdb-1.2.1p2 or newer is required; you have %s" % Database.__version__ 21 21 … … 131 131 self.server_version = tuple([int(x) for x in m.groups()]) 132 132 return self.server_version 133 133 134 allows_group_by_ordinal = True 135 allows_unique_and_pk = True 136 autoindexes_primary_keys = False 137 needs_datetime_string_cast = True # MySQLdb requires a typecast for dates 138 needs_upper_for_iops = False 134 139 supports_constraints = True 140 supports_tablespaces = False 141 uses_case_insensitive_names = False 135 142 136 143 def quote_name(name): 137 144 if name.startswith("`") and name.endswith("`"): … … 164 171 sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str) 165 172 return sql 166 173 174 def get_datetime_cast_sql(): 175 return None 176 167 177 def get_limit_offset_sql(limit, offset=None): 168 178 sql = "LIMIT " 169 179 if offset and offset != 0: … … 185 195 def get_pk_default_value(): 186 196 return "DEFAULT" 187 197 198 def get_max_name_length(): 199 return 64; 200 201 def get_start_transaction_sql(): 202 return "BEGIN;" 203 204 def get_autoinc_sql(table): 205 return None 206 188 207 def get_sql_flush(style, tables, sequences): 189 208 """Return a list of SQL statements required to remove all data from 190 209 all tables in the database (without actually removing the tables 191 210 themselves) and put the database in an empty 'initial' state 192 211 193 212 """ 194 213 # NB: The generated SQL below is specific to MySQL 195 214 # 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements … … 201 220 style.SQL_FIELD(quote_name(table)) 202 221 ) for table in tables] + \ 203 222 ['SET FOREIGN_KEY_CHECKS = 1;'] 204 223 205 224 # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements 206 225 # to reset sequence indices 207 226 sql.extend(["%s %s %s %s %s;" % \ -
django.oracle/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 16 20 try: … … 30 34 return self.connection is not None 31 35 32 36 def cursor(self): 33 from django.conf import settings34 37 if not self._valid_connection(): 35 38 if len(settings.DATABASE_HOST.strip()) == 0: 36 39 settings.DATABASE_HOST = 'localhost' … … 40 43 else: 41 44 conn_string = "%s/%s@%s" % (settings.DATABASE_USER, settings.DATABASE_PASSWORD, settings.DATABASE_NAME) 42 45 self.connection = Database.connect(conn_string, **self.options) 43 return FormatStylePlaceholderCursor(self.connection) 46 cursor = FormatStylePlaceholderCursor(self.connection) 47 # default arraysize of 1 is highly sub-optimal 48 cursor.arraysize = 100 49 # set oracle date to ansi date format 50 cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD'") 51 cursor.execute("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'") 52 if settings.DEBUG: 53 return util.CursorDebugWrapper(cursor, self) 54 return cursor 44 55 45 56 def _commit(self): 46 57 if self.connection is not None: 47 self.connection.commit()58 return self.connection.commit() 48 59 49 60 def _rollback(self): 50 61 if self.connection is not None: 51 try: 52 self.connection.rollback() 53 except Database.NotSupportedError: 54 pass 62 return self.connection.rollback() 55 63 56 64 def close(self): 57 65 if self.connection is not None: 58 66 self.connection.close() 59 67 self.connection = None 60 68 69 allows_group_by_ordinal = False 70 allows_unique_and_pk = False # Suppress UNIQUE/PK for Oracle (ORA-02259) 71 autoindexes_primary_keys = True 72 needs_datetime_string_cast = False 73 needs_upper_for_iops = True 61 74 supports_constraints = True 75 supports_tablespaces = True 76 uses_case_insensitive_names = True 62 77 63 78 class FormatStylePlaceholderCursor(Database.Cursor): 64 79 """ … … 66 81 This fixes it -- but note that if you want to use a literal "%s" in a query, 67 82 you'll need to use "%%s". 68 83 """ 84 def _rewrite_args(self, query, params=None): 85 if params is None: 86 params = [] 87 else: 88 # cx_Oracle can't handle unicode parameters, so cast to str for now 89 for i, param in enumerate(params): 90 if type(param) == unicode: 91 try: 92 params[i] = param.encode('utf-8') 93 except UnicodeError: 94 params[i] = str(param) 95 args = [(':arg%d' % i) for i in range(len(params))] 96 query = query % tuple(args) 97 # cx_Oracle cannot execute a query with the closing ';' 98 if query.endswith(';'): 99 query = query[:-1] 100 return query, params 101 69 102 def execute(self, query, params=None): 70 if params is None: params = [] 71 query = self.convert_arguments(query, len(params)) 103 query, params = self._rewrite_args(query, params) 72 104 return Database.Cursor.execute(self, query, params) 73 105 74 106 def executemany(self, query, params=None): 75 if params is None: params = [] 76 query = self.convert_arguments(query, len(params[0])) 107 query, params = self._rewrite_args(query, params) 77 108 return Database.Cursor.executemany(self, query, params) 78 109 79 def convert_arguments(self, query, num_params):80 # replace occurances of "%s" with ":arg" - Oracle requires colons for parameter placeholders.81 args = [':arg' for i in range(num_params)]82 return query % tuple(args)83 84 110 def quote_name(name): 85 return name 111 # SQL92 requires delimited (quoted) names to be case-sensitive. When 112 # not quoted, Oracle has case-insensitive behavior for identifiers, but 113 # always defaults to uppercase. 114 # We simplify things by making Oracle identifiers always uppercase. 115 if not name.startswith('"') and not name.endswith('"'): 116 name = '"%s"' % util.truncate_name(name.upper(), get_max_name_length()) 117 return name.upper() 86 118 87 119 dictfetchone = util.dictfetchone 88 120 dictfetchmany = util.dictfetchmany 89 121 dictfetchall = util.dictfetchall 90 122 91 123 def get_last_insert_id(cursor, table_name, pk_name): 92 query = "SELECT %s_sq.currval from dual" % table_name93 cursor.execute( query)124 sq_name = util.truncate_name(table_name, get_max_name_length()-3) 125 cursor.execute('SELECT %s_sq.currval FROM dual' % sq_name) 94 126 return cursor.fetchone()[0] 95 127 96 128 def get_date_extract_sql(lookup_type, table_name): 97 129 # lookup_type is 'year', 'month', 'day' 98 # http:// www.psoug.org/reference/date_func.html130 # http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions42a.htm#1017163 99 131 return "EXTRACT(%s FROM %s)" % (lookup_type, table_name) 100 132 101 133 def get_date_trunc_sql(lookup_type, field_name): 102 return "EXTRACT(%s FROM TRUNC(%s))" % (lookup_type, field_name) 134 # lookup_type is 'year', 'month', 'day' 135 # Oracle uses TRUNC() for both dates and numbers. 136 # http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions155a.htm#SQLRF06151 137 if lookup_type == 'day': 138 sql = 'TRUNC(%s)' % (field_name,) 139 else: 140 sql = "TRUNC(%s, '%s')" % (field_name, lookup_type) 141 return sql 103 142 143 def get_datetime_cast_sql(): 144 return "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS')" 145 104 146 def get_limit_offset_sql(limit, offset=None): 105 147 # Limits and offset are too complicated to be handled here. 106 # Instead, they are handled in django/db/ query.py.107 pass148 # Instead, they are handled in django/db/backends/oracle/query.py. 149 return "" 108 150 109 151 def get_random_function_sql(): 110 152 return "DBMS_RANDOM.RANDOM" … … 116 158 raise NotImplementedError 117 159 118 160 def get_drop_foreignkey_sql(): 119 return "DROP FOREIGN KEY"161 return "DROP CONSTRAINT" 120 162 121 163 def get_pk_default_value(): 122 164 return "DEFAULT" 123 165 166 def get_max_name_length(): 167 return 30 168 169 def get_start_transaction_sql(): 170 return None 171 172 def get_tablespace_sql(tablespace, inline=False): 173 return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), quote_name(tablespace)) 174 175 def get_autoinc_sql(table): 176 # To simulate auto-incrementing primary keys in Oracle, we have to 177 # create a sequence and a trigger. 178 sq_name = get_sequence_name(table) 179 tr_name = get_trigger_name(table) 180 sequence_sql = 'CREATE SEQUENCE %s;' % sq_name 181 trigger_sql = """CREATE OR REPLACE TRIGGER %s 182 BEFORE INSERT ON %s 183 FOR EACH ROW 184 WHEN (new.id IS NULL) 185 BEGIN 186 SELECT %s.nextval INTO :new.id FROM dual; 187 END;\n""" % (tr_name, quote_name(table), sq_name) 188 return sequence_sql, trigger_sql 189 190 def get_drop_sequence(table): 191 return "DROP SEQUENCE %s;" % quote_name(get_sequence_name(table)) 192 193 def _get_sequence_reset_sql(): 194 # TODO: colorize this SQL code with style.SQL_KEYWORD(), etc. 195 return """ 196 DECLARE 197 startvalue integer; 198 cval integer; 199 BEGIN 200 LOCK TABLE %(table)s IN SHARE MODE; 201 SELECT NVL(MAX(id), 0) INTO startvalue FROM %(table)s; 202 SELECT %(sequence)s.nextval INTO cval FROM dual; 203 cval := startvalue - cval; 204 IF cval != 0 THEN 205 EXECUTE IMMEDIATE 'ALTER SEQUENCE %(sequence)s MINVALUE 0 INCREMENT BY '||cval; 206 SELECT %(sequence)s.nextval INTO cval FROM dual; 207 EXECUTE IMMEDIATE 'ALTER SEQUENCE %(sequence)s INCREMENT BY 1'; 208 END IF; 209 COMMIT; 210 END;\n""" 211 124 212 def get_sql_flush(style, tables, sequences): 125 213 """Return a list of SQL statements required to remove all data from 126 214 all tables in the database (without actually removing the tables 127 215 themselves) and put the database in an empty 'initial' state 128 216 """ 129 # Return a list of 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements 130 # TODO - SQL not actually tested against Oracle yet! 131 # TODO - autoincrement indices reset required? See other get_sql_flush() implementations 132 sql = ['%s %s;' % \ 133 (style.SQL_KEYWORD('TRUNCATE'), 134 style.SQL_FIELD(quote_name(table)) 135 ) for table in tables] 217 # Return a list of 'TRUNCATE x;', 'TRUNCATE y;', 218 # 'TRUNCATE z;'... style SQL statements 219 if tables: 220 # Oracle does support TRUNCATE, but it seems to get us into 221 # FK referential trouble, whereas DELETE FROM table works. 222 sql = ['%s %s %s;' % \ 223 (style.SQL_KEYWORD('DELETE'), 224 style.SQL_KEYWORD('FROM'), 225 style.SQL_FIELD(quote_name(table)) 226 ) for table in tables] 227 # Since we've just deleted all the rows, running our sequence 228 # ALTER code will reset the sequence to 0. 229 for sequence_info in sequences: 230 table_name = sequence_info['table'] 231 seq_name = get_sequence_name(table_name) 232 query = _get_sequence_reset_sql() % {'sequence':seq_name, 233 'table':quote_name(table_name)} 234 sql.append(query) 235 return sql 236 else: 237 return [] 136 238 239 def get_sequence_name(table): 240 name_length = get_max_name_length() - 3 241 return '%s_SQ' % util.truncate_name(table, name_length).upper() 242 137 243 def get_sql_sequence_reset(style, model_list): 138 244 "Returns a list of the SQL statements to reset sequences for the given models." 139 # No sequence reset required 140 return [] 245 from django.db import models 246 output = [] 247 query = _get_sequence_reset_sql() 248 for model in model_list: 249 for f in model._meta.fields: 250 if isinstance(f, models.AutoField): 251 sequence_name = get_sequence_name(model._meta.db_table) 252 output.append(query % {'sequence':sequence_name, 253 'table':model._meta.db_table}) 254 break # Only one AutoField is allowed per model, so don't bother continuing. 255 for f in model._meta.many_to_many: 256 sequence_name = get_sequence_name(f.m2m_db_table()) 257 output.append(query % {'sequence':sequence_name, 258 'table':f.m2m_db_table()}) 259 return output 141 260 261 def get_trigger_name(table): 262 name_length = get_max_name_length() - 3 263 return '%s_TR' % util.truncate_name(table, name_length).upper() 264 265 def get_query_set_class(DefaultQuerySet): 266 "Create a custom QuerySet class for Oracle." 267 268 from django.db import backend, connection 269 from django.db.models.query import EmptyResultSet, GET_ITERATOR_CHUNK_SIZE 270 271 class OracleQuerySet(DefaultQuerySet): 272 273 def iterator(self): 274 "Performs the SELECT database lookup of this QuerySet." 275 276 from django.db.models.query import get_cached_row 277 278 # self._select is a dictionary, and dicti
