web/lib/django/db/backends/oracle/base.py
changeset 38 77b6da96e6f1
equal deleted inserted replaced
37:8d941af65caf 38:77b6da96e6f1
       
     1 """
       
     2 Oracle database backend for Django.
       
     3 
       
     4 Requires cx_Oracle: http://cx-oracle.sourceforge.net/
       
     5 """
       
     6 
       
     7 
       
     8 import datetime
       
     9 import os
       
    10 import sys
       
    11 import time
       
    12 from decimal import Decimal
       
    13 
       
    14 # Oracle takes client-side character set encoding from the environment.
       
    15 os.environ['NLS_LANG'] = '.UTF8'
       
    16 # This prevents unicode from getting mangled by getting encoded into the
       
    17 # potentially non-unicode database character set.
       
    18 os.environ['ORA_NCHAR_LITERAL_REPLACE'] = 'TRUE'
       
    19 
       
    20 try:
       
    21     import cx_Oracle as Database
       
    22 except ImportError, e:
       
    23     from django.core.exceptions import ImproperlyConfigured
       
    24     raise ImproperlyConfigured("Error loading cx_Oracle module: %s" % e)
       
    25 
       
    26 from django.db import utils
       
    27 from django.db.backends import *
       
    28 from django.db.backends.signals import connection_created
       
    29 from django.db.backends.oracle.client import DatabaseClient
       
    30 from django.db.backends.oracle.creation import DatabaseCreation
       
    31 from django.db.backends.oracle.introspection import DatabaseIntrospection
       
    32 from django.utils.encoding import smart_str, force_unicode
       
    33 
       
    34 DatabaseError = Database.DatabaseError
       
    35 IntegrityError = Database.IntegrityError
       
    36 
       
    37 
       
    38 # Check whether cx_Oracle was compiled with the WITH_UNICODE option.  This will
       
    39 # also be True in Python 3.0.
       
    40 if int(Database.version.split('.', 1)[0]) >= 5 and not hasattr(Database, 'UNICODE'):
       
    41     convert_unicode = force_unicode
       
    42 else:
       
    43     convert_unicode = smart_str
       
    44 
       
    45 
       
    46 class DatabaseFeatures(BaseDatabaseFeatures):
       
    47     empty_fetchmany_value = ()
       
    48     needs_datetime_string_cast = False
       
    49     interprets_empty_strings_as_nulls = True
       
    50     uses_savepoints = True
       
    51     can_return_id_from_insert = True
       
    52     allow_sliced_subqueries = False
       
    53 
       
    54 
       
    55 class DatabaseOperations(BaseDatabaseOperations):
       
    56     compiler_module = "django.db.backends.oracle.compiler"
       
    57 
       
    58     def autoinc_sql(self, table, column):
       
    59         # To simulate auto-incrementing primary keys in Oracle, we have to
       
    60         # create a sequence and a trigger.
       
    61         sq_name = get_sequence_name(table)
       
    62         tr_name = get_trigger_name(table)
       
    63         tbl_name = self.quote_name(table)
       
    64         col_name = self.quote_name(column)
       
    65         sequence_sql = """
       
    66 DECLARE
       
    67     i INTEGER;
       
    68 BEGIN
       
    69     SELECT COUNT(*) INTO i FROM USER_CATALOG
       
    70         WHERE TABLE_NAME = '%(sq_name)s' AND TABLE_TYPE = 'SEQUENCE';
       
    71     IF i = 0 THEN
       
    72         EXECUTE IMMEDIATE 'CREATE SEQUENCE "%(sq_name)s"';
       
    73     END IF;
       
    74 END;
       
    75 /""" % locals()
       
    76         trigger_sql = """
       
    77 CREATE OR REPLACE TRIGGER "%(tr_name)s"
       
    78 BEFORE INSERT ON %(tbl_name)s
       
    79 FOR EACH ROW
       
    80 WHEN (new.%(col_name)s IS NULL)
       
    81     BEGIN
       
    82         SELECT "%(sq_name)s".nextval
       
    83         INTO :new.%(col_name)s FROM dual;
       
    84     END;
       
    85 /""" % locals()
       
    86         return sequence_sql, trigger_sql
       
    87 
       
    88     def date_extract_sql(self, lookup_type, field_name):
       
    89         # http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions42a.htm#1017163
       
    90         if lookup_type == 'week_day':
       
    91             # TO_CHAR(field, 'D') returns an integer from 1-7, where 1=Sunday.
       
    92             return "TO_CHAR(%s, 'D')" % field_name
       
    93         else:
       
    94             return "EXTRACT(%s FROM %s)" % (lookup_type, field_name)
       
    95 
       
    96     def date_trunc_sql(self, lookup_type, field_name):
       
    97         # Oracle uses TRUNC() for both dates and numbers.
       
    98         # http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions155a.htm#SQLRF06151
       
    99         if lookup_type == 'day':
       
   100             sql = 'TRUNC(%s)' % field_name
       
   101         else:
       
   102             sql = "TRUNC(%s, '%s')" % (field_name, lookup_type)
       
   103         return sql
       
   104 
       
   105     def convert_values(self, value, field):
       
   106         if isinstance(value, Database.LOB):
       
   107             value = value.read()
       
   108             if field and field.get_internal_type() == 'TextField':
       
   109                 value = force_unicode(value)
       
   110 
       
   111         # Oracle stores empty strings as null. We need to undo this in
       
   112         # order to adhere to the Django convention of using the empty
       
   113         # string instead of null, but only if the field accepts the
       
   114         # empty string.
       
   115         if value is None and field and field.empty_strings_allowed:
       
   116             value = u''
       
   117         # Convert 1 or 0 to True or False
       
   118         elif value in (1, 0) and field and field.get_internal_type() in ('BooleanField', 'NullBooleanField'):
       
   119             value = bool(value)
       
   120         # Force floats to the correct type
       
   121         elif value is not None and field and field.get_internal_type() == 'FloatField':
       
   122             value = float(value)
       
   123         # Convert floats to decimals
       
   124         elif value is not None and field and field.get_internal_type() == 'DecimalField':
       
   125             value = util.typecast_decimal(field.format_number(value))
       
   126         # cx_Oracle always returns datetime.datetime objects for
       
   127         # DATE and TIMESTAMP columns, but Django wants to see a
       
   128         # python datetime.date, .time, or .datetime.  We use the type
       
   129         # of the Field to determine which to cast to, but it's not
       
   130         # always available.
       
   131         # As a workaround, we cast to date if all the time-related
       
   132         # values are 0, or to time if the date is 1/1/1900.
       
   133         # This could be cleaned a bit by adding a method to the Field
       
   134         # classes to normalize values from the database (the to_python
       
   135         # method is used for validation and isn't what we want here).
       
   136         elif isinstance(value, Database.Timestamp):
       
   137             # In Python 2.3, the cx_Oracle driver returns its own
       
   138             # Timestamp object that we must convert to a datetime class.
       
   139             if not isinstance(value, datetime.datetime):
       
   140                 value = datetime.datetime(value.year, value.month,
       
   141                         value.day, value.hour, value.minute, value.second,
       
   142                         value.fsecond)
       
   143             if field and field.get_internal_type() == 'DateTimeField':
       
   144                 pass
       
   145             elif field and field.get_internal_type() == 'DateField':
       
   146                 value = value.date()
       
   147             elif field and field.get_internal_type() == 'TimeField' or (value.year == 1900 and value.month == value.day == 1):
       
   148                 value = value.time()
       
   149             elif value.hour == value.minute == value.second == value.microsecond == 0:
       
   150                 value = value.date()
       
   151         return value
       
   152 
       
   153     def datetime_cast_sql(self):
       
   154         return "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS.FF')"
       
   155 
       
   156     def deferrable_sql(self):
       
   157         return " DEFERRABLE INITIALLY DEFERRED"
       
   158 
       
   159     def drop_sequence_sql(self, table):
       
   160         return "DROP SEQUENCE %s;" % self.quote_name(get_sequence_name(table))
       
   161 
       
   162     def fetch_returned_insert_id(self, cursor):
       
   163         return long(cursor._insert_id_var.getvalue())
       
   164 
       
   165     def field_cast_sql(self, db_type):
       
   166         if db_type and db_type.endswith('LOB'):
       
   167             return "DBMS_LOB.SUBSTR(%s)"
       
   168         else:
       
   169             return "%s"
       
   170 
       
   171     def last_insert_id(self, cursor, table_name, pk_name):
       
   172         sq_name = get_sequence_name(table_name)
       
   173         cursor.execute('SELECT "%s".currval FROM dual' % sq_name)
       
   174         return cursor.fetchone()[0]
       
   175 
       
   176     def lookup_cast(self, lookup_type):
       
   177         if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith'):
       
   178             return "UPPER(%s)"
       
   179         return "%s"
       
   180 
       
   181     def max_name_length(self):
       
   182         return 30
       
   183 
       
   184     def prep_for_iexact_query(self, x):
       
   185         return x
       
   186 
       
   187     def process_clob(self, value):
       
   188         if value is None:
       
   189             return u''
       
   190         return force_unicode(value.read())
       
   191 
       
   192     def quote_name(self, name):
       
   193         # SQL92 requires delimited (quoted) names to be case-sensitive.  When
       
   194         # not quoted, Oracle has case-insensitive behavior for identifiers, but
       
   195         # always defaults to uppercase.
       
   196         # We simplify things by making Oracle identifiers always uppercase.
       
   197         if not name.startswith('"') and not name.endswith('"'):
       
   198             name = '"%s"' % util.truncate_name(name.upper(),
       
   199                                                self.max_name_length())
       
   200         return name.upper()
       
   201 
       
   202     def random_function_sql(self):
       
   203         return "DBMS_RANDOM.RANDOM"
       
   204 
       
   205     def regex_lookup_9(self, lookup_type):
       
   206         raise NotImplementedError("Regexes are not supported in Oracle before version 10g.")
       
   207 
       
   208     def regex_lookup_10(self, lookup_type):
       
   209         if lookup_type == 'regex':
       
   210             match_option = "'c'"
       
   211         else:
       
   212             match_option = "'i'"
       
   213         return 'REGEXP_LIKE(%%s, %%s, %s)' % match_option
       
   214 
       
   215     def regex_lookup(self, lookup_type):
       
   216         # If regex_lookup is called before it's been initialized, then create
       
   217         # a cursor to initialize it and recur.
       
   218         from django.db import connection
       
   219         connection.cursor()
       
   220         return connection.ops.regex_lookup(lookup_type)
       
   221 
       
   222     def return_insert_id(self):
       
   223         return "RETURNING %s INTO %%s", (InsertIdVar(),)
       
   224 
       
   225     def savepoint_create_sql(self, sid):
       
   226         return convert_unicode("SAVEPOINT " + self.quote_name(sid))
       
   227 
       
   228     def savepoint_rollback_sql(self, sid):
       
   229         return convert_unicode("ROLLBACK TO SAVEPOINT " + self.quote_name(sid))
       
   230 
       
   231     def sql_flush(self, style, tables, sequences):
       
   232         # Return a list of 'TRUNCATE x;', 'TRUNCATE y;',
       
   233         # 'TRUNCATE z;'... style SQL statements
       
   234         if tables:
       
   235             # Oracle does support TRUNCATE, but it seems to get us into
       
   236             # FK referential trouble, whereas DELETE FROM table works.
       
   237             sql = ['%s %s %s;' % \
       
   238                     (style.SQL_KEYWORD('DELETE'),
       
   239                      style.SQL_KEYWORD('FROM'),
       
   240                      style.SQL_FIELD(self.quote_name(table)))
       
   241                     for table in tables]
       
   242             # Since we've just deleted all the rows, running our sequence
       
   243             # ALTER code will reset the sequence to 0.
       
   244             for sequence_info in sequences:
       
   245                 sequence_name = get_sequence_name(sequence_info['table'])
       
   246                 table_name = self.quote_name(sequence_info['table'])
       
   247                 column_name = self.quote_name(sequence_info['column'] or 'id')
       
   248                 query = _get_sequence_reset_sql() % {'sequence': sequence_name,
       
   249                                                      'table': table_name,
       
   250                                                      'column': column_name}
       
   251                 sql.append(query)
       
   252             return sql
       
   253         else:
       
   254             return []
       
   255 
       
   256     def sequence_reset_sql(self, style, model_list):
       
   257         from django.db import models
       
   258         output = []
       
   259         query = _get_sequence_reset_sql()
       
   260         for model in model_list:
       
   261             for f in model._meta.local_fields:
       
   262                 if isinstance(f, models.AutoField):
       
   263                     table_name = self.quote_name(model._meta.db_table)
       
   264                     sequence_name = get_sequence_name(model._meta.db_table)
       
   265                     column_name = self.quote_name(f.column)
       
   266                     output.append(query % {'sequence': sequence_name,
       
   267                                            'table': table_name,
       
   268                                            'column': column_name})
       
   269                     # Only one AutoField is allowed per model, so don't
       
   270                     # continue to loop
       
   271                     break
       
   272             for f in model._meta.many_to_many:
       
   273                 if not f.rel.through:
       
   274                     table_name = self.quote_name(f.m2m_db_table())
       
   275                     sequence_name = get_sequence_name(f.m2m_db_table())
       
   276                     column_name = self.quote_name('id')
       
   277                     output.append(query % {'sequence': sequence_name,
       
   278                                            'table': table_name,
       
   279                                            'column': column_name})
       
   280         return output
       
   281 
       
   282     def start_transaction_sql(self):
       
   283         return ''
       
   284 
       
   285     def tablespace_sql(self, tablespace, inline=False):
       
   286         return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""),
       
   287             self.quote_name(tablespace))
       
   288 
       
   289     def value_to_db_time(self, value):
       
   290         if value is None:
       
   291             return None
       
   292         if isinstance(value, basestring):
       
   293             return datetime.datetime(*(time.strptime(value, '%H:%M:%S')[:6]))
       
   294         return datetime.datetime(1900, 1, 1, value.hour, value.minute,
       
   295                                  value.second, value.microsecond)
       
   296 
       
   297     def year_lookup_bounds_for_date_field(self, value):
       
   298         first = '%s-01-01'
       
   299         second = '%s-12-31'
       
   300         return [first % value, second % value]
       
   301 
       
   302     def combine_expression(self, connector, sub_expressions):
       
   303         "Oracle requires special cases for %% and & operators in query expressions"
       
   304         if connector == '%%':
       
   305             return 'MOD(%s)' % ','.join(sub_expressions)
       
   306         elif connector == '&':
       
   307             return 'BITAND(%s)' % ','.join(sub_expressions)
       
   308         elif connector == '|':
       
   309             raise NotImplementedError("Bit-wise or is not supported in Oracle.")
       
   310         return super(DatabaseOperations, self).combine_expression(connector, sub_expressions)
       
   311 
       
   312 
       
   313 class DatabaseWrapper(BaseDatabaseWrapper):
       
   314 
       
   315     operators = {
       
   316         'exact': '= %s',
       
   317         'iexact': '= UPPER(%s)',
       
   318         'contains': "LIKE TRANSLATE(%s USING NCHAR_CS) ESCAPE TRANSLATE('\\' USING NCHAR_CS)",
       
   319         'icontains': "LIKE UPPER(TRANSLATE(%s USING NCHAR_CS)) ESCAPE TRANSLATE('\\' USING NCHAR_CS)",
       
   320         'gt': '> %s',
       
   321         'gte': '>= %s',
       
   322         'lt': '< %s',
       
   323         'lte': '<= %s',
       
   324         'startswith': "LIKE TRANSLATE(%s USING NCHAR_CS) ESCAPE TRANSLATE('\\' USING NCHAR_CS)",
       
   325         'endswith': "LIKE TRANSLATE(%s USING NCHAR_CS) ESCAPE TRANSLATE('\\' USING NCHAR_CS)",
       
   326         'istartswith': "LIKE UPPER(TRANSLATE(%s USING NCHAR_CS)) ESCAPE TRANSLATE('\\' USING NCHAR_CS)",
       
   327         'iendswith': "LIKE UPPER(TRANSLATE(%s USING NCHAR_CS)) ESCAPE TRANSLATE('\\' USING NCHAR_CS)",
       
   328     }
       
   329     oracle_version = None
       
   330 
       
   331     def __init__(self, *args, **kwargs):
       
   332         super(DatabaseWrapper, self).__init__(*args, **kwargs)
       
   333 
       
   334         self.features = DatabaseFeatures()
       
   335         self.ops = DatabaseOperations()
       
   336         self.client = DatabaseClient(self)
       
   337         self.creation = DatabaseCreation(self)
       
   338         self.introspection = DatabaseIntrospection(self)
       
   339         self.validation = BaseDatabaseValidation(self)
       
   340 
       
   341     def _valid_connection(self):
       
   342         return self.connection is not None
       
   343 
       
   344     def _connect_string(self):
       
   345         settings_dict = self.settings_dict
       
   346         if len(settings_dict['HOST'].strip()) == 0:
       
   347             settings_dict['HOST'] = 'localhost'
       
   348         if len(settings_dict['PORT'].strip()) != 0:
       
   349             dsn = Database.makedsn(settings_dict['HOST'],
       
   350                                    int(settings_dict['PORT']),
       
   351                                    settings_dict['NAME'])
       
   352         else:
       
   353             dsn = settings_dict['NAME']
       
   354         return "%s/%s@%s" % (settings_dict['USER'],
       
   355                              settings_dict['PASSWORD'], dsn)
       
   356 
       
   357     def _cursor(self):
       
   358         cursor = None
       
   359         if not self._valid_connection():
       
   360             conn_string = convert_unicode(self._connect_string())
       
   361             self.connection = Database.connect(conn_string, **self.settings_dict['OPTIONS'])
       
   362             cursor = FormatStylePlaceholderCursor(self.connection)
       
   363             # Set oracle date to ansi date format.  This only needs to execute
       
   364             # once when we create a new connection. We also set the Territory
       
   365             # to 'AMERICA' which forces Sunday to evaluate to a '1' in TO_CHAR().
       
   366             cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS' "
       
   367                            "NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF' "
       
   368                            "NLS_TERRITORY = 'AMERICA'")
       
   369             try:
       
   370                 self.oracle_version = int(self.connection.version.split('.')[0])
       
   371                 # There's no way for the DatabaseOperations class to know the
       
   372                 # currently active Oracle version, so we do some setups here.
       
   373                 # TODO: Multi-db support will need a better solution (a way to
       
   374                 # communicate the current version).
       
   375                 if self.oracle_version <= 9:
       
   376                     self.ops.regex_lookup = self.ops.regex_lookup_9
       
   377                 else:
       
   378                     self.ops.regex_lookup = self.ops.regex_lookup_10
       
   379             except ValueError:
       
   380                 pass
       
   381             try:
       
   382                 self.connection.stmtcachesize = 20
       
   383             except:
       
   384                 # Django docs specify cx_Oracle version 4.3.1 or higher, but
       
   385                 # stmtcachesize is available only in 4.3.2 and up.
       
   386                 pass
       
   387             connection_created.send(sender=self.__class__)
       
   388         if not cursor:
       
   389             cursor = FormatStylePlaceholderCursor(self.connection)
       
   390         return cursor
       
   391 
       
   392     # Oracle doesn't support savepoint commits.  Ignore them.
       
   393     def _savepoint_commit(self, sid):
       
   394         pass
       
   395 
       
   396 
       
   397 class OracleParam(object):
       
   398     """
       
   399     Wrapper object for formatting parameters for Oracle. If the string
       
   400     representation of the value is large enough (greater than 4000 characters)
       
   401     the input size needs to be set as CLOB. Alternatively, if the parameter
       
   402     has an `input_size` attribute, then the value of the `input_size` attribute
       
   403     will be used instead. Otherwise, no input size will be set for the
       
   404     parameter when executing the query.
       
   405     """
       
   406 
       
   407     def __init__(self, param, cursor, strings_only=False):
       
   408         if hasattr(param, 'bind_parameter'):
       
   409             self.smart_str = param.bind_parameter(cursor)
       
   410         else:
       
   411             self.smart_str = convert_unicode(param, cursor.charset,
       
   412                                              strings_only)
       
   413         if hasattr(param, 'input_size'):
       
   414             # If parameter has `input_size` attribute, use that.
       
   415             self.input_size = param.input_size
       
   416         elif isinstance(param, basestring) and len(param) > 4000:
       
   417             # Mark any string param greater than 4000 characters as a CLOB.
       
   418             self.input_size = Database.CLOB
       
   419         else:
       
   420             self.input_size = None
       
   421 
       
   422 
       
   423 class VariableWrapper(object):
       
   424     """
       
   425     An adapter class for cursor variables that prevents the wrapped object
       
   426     from being converted into a string when used to instanciate an OracleParam.
       
   427     This can be used generally for any other object that should be passed into
       
   428     Cursor.execute as-is.
       
   429     """
       
   430 
       
   431     def __init__(self, var):
       
   432         self.var = var
       
   433 
       
   434     def bind_parameter(self, cursor):
       
   435         return self.var
       
   436 
       
   437     def __getattr__(self, key):
       
   438         return getattr(self.var, key)
       
   439 
       
   440     def __setattr__(self, key, value):
       
   441         if key == 'var':
       
   442             self.__dict__[key] = value
       
   443         else:
       
   444             setattr(self.var, key, value)
       
   445 
       
   446 
       
   447 class InsertIdVar(object):
       
   448     """
       
   449     A late-binding cursor variable that can be passed to Cursor.execute
       
   450     as a parameter, in order to receive the id of the row created by an
       
   451     insert statement.
       
   452     """
       
   453 
       
   454     def bind_parameter(self, cursor):
       
   455         param = cursor.cursor.var(Database.NUMBER)
       
   456         cursor._insert_id_var = param
       
   457         return param
       
   458 
       
   459 
       
   460 class FormatStylePlaceholderCursor(object):
       
   461     """
       
   462     Django uses "format" (e.g. '%s') style placeholders, but Oracle uses ":var"
       
   463     style. This fixes it -- but note that if you want to use a literal "%s" in
       
   464     a query, you'll need to use "%%s".
       
   465 
       
   466     We also do automatic conversion between Unicode on the Python side and
       
   467     UTF-8 -- for talking to Oracle -- in here.
       
   468     """
       
   469     charset = 'utf-8'
       
   470 
       
   471     def __init__(self, connection):
       
   472         self.cursor = connection.cursor()
       
   473         # Necessary to retrieve decimal values without rounding error.
       
   474         self.cursor.numbersAsStrings = True
       
   475         # Default arraysize of 1 is highly sub-optimal.
       
   476         self.cursor.arraysize = 100
       
   477 
       
   478     def _format_params(self, params):
       
   479         return tuple([OracleParam(p, self, True) for p in params])
       
   480 
       
   481     def _guess_input_sizes(self, params_list):
       
   482         sizes = [None] * len(params_list[0])
       
   483         for params in params_list:
       
   484             for i, value in enumerate(params):
       
   485                 if value.input_size:
       
   486                     sizes[i] = value.input_size
       
   487         self.setinputsizes(*sizes)
       
   488 
       
   489     def _param_generator(self, params):
       
   490         return [p.smart_str for p in params]
       
   491 
       
   492     def execute(self, query, params=None):
       
   493         if params is None:
       
   494             params = []
       
   495         else:
       
   496             params = self._format_params(params)
       
   497         args = [(':arg%d' % i) for i in range(len(params))]
       
   498         # cx_Oracle wants no trailing ';' for SQL statements.  For PL/SQL, it
       
   499         # it does want a trailing ';' but not a trailing '/'.  However, these
       
   500         # characters must be included in the original query in case the query
       
   501         # is being passed to SQL*Plus.
       
   502         if query.endswith(';') or query.endswith('/'):
       
   503             query = query[:-1]
       
   504         query = convert_unicode(query % tuple(args), self.charset)
       
   505         self._guess_input_sizes([params])
       
   506         try:
       
   507             return self.cursor.execute(query, self._param_generator(params))
       
   508         except Database.IntegrityError, e:
       
   509             raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
       
   510         except Database.DatabaseError, e:
       
   511             # cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
       
   512             if hasattr(e.args[0], 'code') and e.args[0].code == 1400 and not isinstance(e, IntegrityError):
       
   513                 raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
       
   514             raise utils.DatabaseError, utils.DatabaseError(*tuple(e)), sys.exc_info()[2]
       
   515 
       
   516     def executemany(self, query, params=None):
       
   517         try:
       
   518             args = [(':arg%d' % i) for i in range(len(params[0]))]
       
   519         except (IndexError, TypeError):
       
   520             # No params given, nothing to do
       
   521             return None
       
   522         # cx_Oracle wants no trailing ';' for SQL statements.  For PL/SQL, it
       
   523         # it does want a trailing ';' but not a trailing '/'.  However, these
       
   524         # characters must be included in the original query in case the query
       
   525         # is being passed to SQL*Plus.
       
   526         if query.endswith(';') or query.endswith('/'):
       
   527             query = query[:-1]
       
   528         query = convert_unicode(query % tuple(args), self.charset)
       
   529         formatted = [self._format_params(i) for i in params]
       
   530         self._guess_input_sizes(formatted)
       
   531         try:
       
   532             return self.cursor.executemany(query,
       
   533                                 [self._param_generator(p) for p in formatted])
       
   534         except Database.IntegrityError, e:
       
   535             raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
       
   536         except Database.DatabaseError, e:
       
   537             # cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
       
   538             if hasattr(e.args[0], 'code') and e.args[0].code == 1400 and not isinstance(e, IntegrityError):
       
   539                 raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
       
   540             raise utils.DatabaseError, utils.DatabaseError(*tuple(e)), sys.exc_info()[2]
       
   541 
       
   542     def fetchone(self):
       
   543         row = self.cursor.fetchone()
       
   544         if row is None:
       
   545             return row
       
   546         return _rowfactory(row, self.cursor)
       
   547 
       
   548     def fetchmany(self, size=None):
       
   549         if size is None:
       
   550             size = self.arraysize
       
   551         return tuple([_rowfactory(r, self.cursor)
       
   552                       for r in self.cursor.fetchmany(size)])
       
   553 
       
   554     def fetchall(self):
       
   555         return tuple([_rowfactory(r, self.cursor)
       
   556                       for r in self.cursor.fetchall()])
       
   557 
       
   558     def var(self, *args):
       
   559         return VariableWrapper(self.cursor.var(*args))
       
   560 
       
   561     def arrayvar(self, *args):
       
   562         return VariableWrapper(self.cursor.arrayvar(*args))
       
   563 
       
   564     def __getattr__(self, attr):
       
   565         if attr in self.__dict__:
       
   566             return self.__dict__[attr]
       
   567         else:
       
   568             return getattr(self.cursor, attr)
       
   569 
       
   570     def __iter__(self):
       
   571         return CursorIterator(self.cursor)
       
   572 
       
   573 
       
   574 class CursorIterator(object):
       
   575 
       
   576     """Cursor iterator wrapper that invokes our custom row factory."""
       
   577 
       
   578     def __init__(self, cursor):
       
   579         self.cursor = cursor
       
   580         self.iter = iter(cursor)
       
   581 
       
   582     def __iter__(self):
       
   583         return self
       
   584 
       
   585     def next(self):
       
   586         return _rowfactory(self.iter.next(), self.cursor)
       
   587 
       
   588 
       
   589 def _rowfactory(row, cursor):
       
   590     # Cast numeric values as the appropriate Python type based upon the
       
   591     # cursor description, and convert strings to unicode.
       
   592     casted = []
       
   593     for value, desc in zip(row, cursor.description):
       
   594         if value is not None and desc[1] is Database.NUMBER:
       
   595             precision, scale = desc[4:6]
       
   596             if scale == -127:
       
   597                 if precision == 0:
       
   598                     # NUMBER column: decimal-precision floating point
       
   599                     # This will normally be an integer from a sequence,
       
   600                     # but it could be a decimal value.
       
   601                     if '.' in value:
       
   602                         value = Decimal(value)
       
   603                     else:
       
   604                         value = int(value)
       
   605                 else:
       
   606                     # FLOAT column: binary-precision floating point.
       
   607                     # This comes from FloatField columns.
       
   608                     value = float(value)
       
   609             elif precision > 0:
       
   610                 # NUMBER(p,s) column: decimal-precision fixed point.
       
   611                 # This comes from IntField and DecimalField columns.
       
   612                 if scale == 0:
       
   613                     value = int(value)
       
   614                 else:
       
   615                     value = Decimal(value)
       
   616             elif '.' in value:
       
   617                 # No type information. This normally comes from a
       
   618                 # mathematical expression in the SELECT list. Guess int
       
   619                 # or Decimal based on whether it has a decimal point.
       
   620                 value = Decimal(value)
       
   621             else:
       
   622                 value = int(value)
       
   623         elif desc[1] in (Database.STRING, Database.FIXED_CHAR,
       
   624                          Database.LONG_STRING):
       
   625             value = to_unicode(value)
       
   626         casted.append(value)
       
   627     return tuple(casted)
       
   628 
       
   629 
       
   630 def to_unicode(s):
       
   631     """
       
   632     Convert strings to Unicode objects (and return all other data types
       
   633     unchanged).
       
   634     """
       
   635     if isinstance(s, basestring):
       
   636         return force_unicode(s)
       
   637     return s
       
   638 
       
   639 
       
   640 def _get_sequence_reset_sql():
       
   641     # TODO: colorize this SQL code with style.SQL_KEYWORD(), etc.
       
   642     return """
       
   643 DECLARE
       
   644     startvalue integer;
       
   645     cval integer;
       
   646 BEGIN
       
   647     LOCK TABLE %(table)s IN SHARE MODE;
       
   648     SELECT NVL(MAX(%(column)s), 0) INTO startvalue FROM %(table)s;
       
   649     SELECT "%(sequence)s".nextval INTO cval FROM dual;
       
   650     cval := startvalue - cval;
       
   651     IF cval != 0 THEN
       
   652         EXECUTE IMMEDIATE 'ALTER SEQUENCE "%(sequence)s" MINVALUE 0 INCREMENT BY '||cval;
       
   653         SELECT "%(sequence)s".nextval INTO cval FROM dual;
       
   654         EXECUTE IMMEDIATE 'ALTER SEQUENCE "%(sequence)s" INCREMENT BY 1';
       
   655     END IF;
       
   656     COMMIT;
       
   657 END;
       
   658 /"""
       
   659 
       
   660 
       
   661 def get_sequence_name(table):
       
   662     name_length = DatabaseOperations().max_name_length() - 3
       
   663     return '%s_SQ' % util.truncate_name(table, name_length).upper()
       
   664 
       
   665 
       
   666 def get_trigger_name(table):
       
   667     name_length = DatabaseOperations().max_name_length() - 3
       
   668     return '%s_TR' % util.truncate_name(table, name_length).upper()