web/lib/django/db/backends/oracle/base.py
changeset 29 cc9b7e14412b
parent 0 0d40e90630ef
equal deleted inserted replaced
28:b758351d191f 29:cc9b7e14412b
     2 Oracle database backend for Django.
     2 Oracle database backend for Django.
     3 
     3 
     4 Requires cx_Oracle: http://cx-oracle.sourceforge.net/
     4 Requires cx_Oracle: http://cx-oracle.sourceforge.net/
     5 """
     5 """
     6 
     6 
       
     7 
       
     8 import datetime
     7 import os
     9 import os
     8 import datetime
    10 import sys
     9 import time
    11 import time
    10 try:
    12 from decimal import Decimal
    11     from decimal import Decimal
       
    12 except ImportError:
       
    13     from django.utils._decimal import Decimal
       
    14 
    13 
    15 # Oracle takes client-side character set encoding from the environment.
    14 # Oracle takes client-side character set encoding from the environment.
    16 os.environ['NLS_LANG'] = '.UTF8'
    15 os.environ['NLS_LANG'] = '.UTF8'
    17 # This prevents unicode from getting mangled by getting encoded into the
    16 # This prevents unicode from getting mangled by getting encoded into the
    18 # potentially non-unicode database character set.
    17 # potentially non-unicode database character set.
    22     import cx_Oracle as Database
    21     import cx_Oracle as Database
    23 except ImportError, e:
    22 except ImportError, e:
    24     from django.core.exceptions import ImproperlyConfigured
    23     from django.core.exceptions import ImproperlyConfigured
    25     raise ImproperlyConfigured("Error loading cx_Oracle module: %s" % e)
    24     raise ImproperlyConfigured("Error loading cx_Oracle module: %s" % e)
    26 
    25 
       
    26 from django.db import utils
    27 from django.db.backends import *
    27 from django.db.backends import *
    28 from django.db.backends.signals import connection_created
    28 from django.db.backends.signals import connection_created
    29 from django.db.backends.oracle import query
       
    30 from django.db.backends.oracle.client import DatabaseClient
    29 from django.db.backends.oracle.client import DatabaseClient
    31 from django.db.backends.oracle.creation import DatabaseCreation
    30 from django.db.backends.oracle.creation import DatabaseCreation
    32 from django.db.backends.oracle.introspection import DatabaseIntrospection
    31 from django.db.backends.oracle.introspection import DatabaseIntrospection
    33 from django.utils.encoding import smart_str, force_unicode
    32 from django.utils.encoding import smart_str, force_unicode
    34 
    33 
    45 
    44 
    46 
    45 
    47 class DatabaseFeatures(BaseDatabaseFeatures):
    46 class DatabaseFeatures(BaseDatabaseFeatures):
    48     empty_fetchmany_value = ()
    47     empty_fetchmany_value = ()
    49     needs_datetime_string_cast = False
    48     needs_datetime_string_cast = False
    50     uses_custom_query_class = True
       
    51     interprets_empty_strings_as_nulls = True
    49     interprets_empty_strings_as_nulls = True
    52     uses_savepoints = True
    50     uses_savepoints = True
    53     can_return_id_from_insert = True
    51     can_return_id_from_insert = True
       
    52     allow_sliced_subqueries = False
    54 
    53 
    55 
    54 
    56 class DatabaseOperations(BaseDatabaseOperations):
    55 class DatabaseOperations(BaseDatabaseOperations):
       
    56     compiler_module = "django.db.backends.oracle.compiler"
    57 
    57 
    58     def autoinc_sql(self, table, column):
    58     def autoinc_sql(self, table, column):
    59         # To simulate auto-incrementing primary keys in Oracle, we have to
    59         # To simulate auto-incrementing primary keys in Oracle, we have to
    60         # create a sequence and a trigger.
    60         # create a sequence and a trigger.
    61         sq_name = get_sequence_name(table)
    61         sq_name = get_sequence_name(table)
   100             sql = 'TRUNC(%s)' % field_name
   100             sql = 'TRUNC(%s)' % field_name
   101         else:
   101         else:
   102             sql = "TRUNC(%s, '%s')" % (field_name, lookup_type)
   102             sql = "TRUNC(%s, '%s')" % (field_name, lookup_type)
   103         return sql
   103         return sql
   104 
   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 
   105     def datetime_cast_sql(self):
   153     def datetime_cast_sql(self):
   106         return "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS.FF')"
   154         return "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS.FF')"
   107 
   155 
   108     def deferrable_sql(self):
   156     def deferrable_sql(self):
   109         return " DEFERRABLE INITIALLY DEFERRED"
   157         return " DEFERRABLE INITIALLY DEFERRED"
   138 
   186 
   139     def process_clob(self, value):
   187     def process_clob(self, value):
   140         if value is None:
   188         if value is None:
   141             return u''
   189             return u''
   142         return force_unicode(value.read())
   190         return force_unicode(value.read())
   143 
       
   144     def query_class(self, DefaultQueryClass):
       
   145         return query.query_class(DefaultQueryClass, Database)
       
   146 
   191 
   147     def quote_name(self, name):
   192     def quote_name(self, name):
   148         # SQL92 requires delimited (quoted) names to be case-sensitive.  When
   193         # SQL92 requires delimited (quoted) names to be case-sensitive.  When
   149         # not quoted, Oracle has case-insensitive behavior for identifiers, but
   194         # not quoted, Oracle has case-insensitive behavior for identifiers, but
   150         # always defaults to uppercase.
   195         # always defaults to uppercase.
   268 class DatabaseWrapper(BaseDatabaseWrapper):
   313 class DatabaseWrapper(BaseDatabaseWrapper):
   269 
   314 
   270     operators = {
   315     operators = {
   271         'exact': '= %s',
   316         'exact': '= %s',
   272         'iexact': '= UPPER(%s)',
   317         'iexact': '= UPPER(%s)',
   273         'contains': "LIKEC %s ESCAPE '\\'",
   318         'contains': "LIKE TRANSLATE(%s USING NCHAR_CS) ESCAPE TRANSLATE('\\' USING NCHAR_CS)",
   274         'icontains': "LIKEC UPPER(%s) ESCAPE '\\'",
   319         'icontains': "LIKE UPPER(TRANSLATE(%s USING NCHAR_CS)) ESCAPE TRANSLATE('\\' USING NCHAR_CS)",
   275         'gt': '> %s',
   320         'gt': '> %s',
   276         'gte': '>= %s',
   321         'gte': '>= %s',
   277         'lt': '< %s',
   322         'lt': '< %s',
   278         'lte': '<= %s',
   323         'lte': '<= %s',
   279         'startswith': "LIKEC %s ESCAPE '\\'",
   324         'startswith': "LIKE TRANSLATE(%s USING NCHAR_CS) ESCAPE TRANSLATE('\\' USING NCHAR_CS)",
   280         'endswith': "LIKEC %s ESCAPE '\\'",
   325         'endswith': "LIKE TRANSLATE(%s USING NCHAR_CS) ESCAPE TRANSLATE('\\' USING NCHAR_CS)",
   281         'istartswith': "LIKEC UPPER(%s) ESCAPE '\\'",
   326         'istartswith': "LIKE UPPER(TRANSLATE(%s USING NCHAR_CS)) ESCAPE TRANSLATE('\\' USING NCHAR_CS)",
   282         'iendswith': "LIKEC UPPER(%s) ESCAPE '\\'",
   327         'iendswith': "LIKE UPPER(TRANSLATE(%s USING NCHAR_CS)) ESCAPE TRANSLATE('\\' USING NCHAR_CS)",
   283     }
   328     }
   284     oracle_version = None
   329     oracle_version = None
   285 
   330 
   286     def __init__(self, *args, **kwargs):
   331     def __init__(self, *args, **kwargs):
   287         super(DatabaseWrapper, self).__init__(*args, **kwargs)
   332         super(DatabaseWrapper, self).__init__(*args, **kwargs)
   289         self.features = DatabaseFeatures()
   334         self.features = DatabaseFeatures()
   290         self.ops = DatabaseOperations()
   335         self.ops = DatabaseOperations()
   291         self.client = DatabaseClient(self)
   336         self.client = DatabaseClient(self)
   292         self.creation = DatabaseCreation(self)
   337         self.creation = DatabaseCreation(self)
   293         self.introspection = DatabaseIntrospection(self)
   338         self.introspection = DatabaseIntrospection(self)
   294         self.validation = BaseDatabaseValidation()
   339         self.validation = BaseDatabaseValidation(self)
   295 
   340 
   296     def _valid_connection(self):
   341     def _valid_connection(self):
   297         return self.connection is not None
   342         return self.connection is not None
   298 
   343 
   299     def _connect_string(self):
   344     def _connect_string(self):
   300         settings_dict = self.settings_dict
   345         settings_dict = self.settings_dict
   301         if len(settings_dict['DATABASE_HOST'].strip()) == 0:
   346         if len(settings_dict['HOST'].strip()) == 0:
   302             settings_dict['DATABASE_HOST'] = 'localhost'
   347             settings_dict['HOST'] = 'localhost'
   303         if len(settings_dict['DATABASE_PORT'].strip()) != 0:
   348         if len(settings_dict['PORT'].strip()) != 0:
   304             dsn = Database.makedsn(settings_dict['DATABASE_HOST'],
   349             dsn = Database.makedsn(settings_dict['HOST'],
   305                                    int(settings_dict['DATABASE_PORT']),
   350                                    int(settings_dict['PORT']),
   306                                    settings_dict['DATABASE_NAME'])
   351                                    settings_dict['NAME'])
   307         else:
   352         else:
   308             dsn = settings_dict['DATABASE_NAME']
   353             dsn = settings_dict['NAME']
   309         return "%s/%s@%s" % (settings_dict['DATABASE_USER'],
   354         return "%s/%s@%s" % (settings_dict['USER'],
   310                              settings_dict['DATABASE_PASSWORD'], dsn)
   355                              settings_dict['PASSWORD'], dsn)
   311 
   356 
   312     def _cursor(self):
   357     def _cursor(self):
   313         cursor = None
   358         cursor = None
   314         if not self._valid_connection():
   359         if not self._valid_connection():
   315             conn_string = convert_unicode(self._connect_string())
   360             conn_string = convert_unicode(self._connect_string())
   316             self.connection = Database.connect(conn_string, **self.settings_dict['DATABASE_OPTIONS'])
   361             self.connection = Database.connect(conn_string, **self.settings_dict['OPTIONS'])
   317             cursor = FormatStylePlaceholderCursor(self.connection)
   362             cursor = FormatStylePlaceholderCursor(self.connection)
   318             # Set oracle date to ansi date format.  This only needs to execute
   363             # Set oracle date to ansi date format.  This only needs to execute
   319             # once when we create a new connection. We also set the Territory
   364             # once when we create a new connection. We also set the Territory
   320             # to 'AMERICA' which forces Sunday to evaluate to a '1' in TO_CHAR().
   365             # to 'AMERICA' which forces Sunday to evaluate to a '1' in TO_CHAR().
   321             cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS' "
   366             cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS' "
   373             self.input_size = Database.CLOB
   418             self.input_size = Database.CLOB
   374         else:
   419         else:
   375             self.input_size = None
   420             self.input_size = None
   376 
   421 
   377 
   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 
   378 class InsertIdVar(object):
   447 class InsertIdVar(object):
   379     """
   448     """
   380     A late-binding cursor variable that can be passed to Cursor.execute
   449     A late-binding cursor variable that can be passed to Cursor.execute
   381     as a parameter, in order to receive the id of the row created by an
   450     as a parameter, in order to receive the id of the row created by an
   382     insert statement.
   451     insert statement.
   383     """
   452     """
   384 
   453 
   385     def bind_parameter(self, cursor):
   454     def bind_parameter(self, cursor):
   386         param = cursor.var(Database.NUMBER)
   455         param = cursor.cursor.var(Database.NUMBER)
   387         cursor._insert_id_var = param
   456         cursor._insert_id_var = param
   388         return param
   457         return param
   389 
   458 
   390 
   459 
   391 class FormatStylePlaceholderCursor(object):
   460 class FormatStylePlaceholderCursor(object):
   434             query = query[:-1]
   503             query = query[:-1]
   435         query = convert_unicode(query % tuple(args), self.charset)
   504         query = convert_unicode(query % tuple(args), self.charset)
   436         self._guess_input_sizes([params])
   505         self._guess_input_sizes([params])
   437         try:
   506         try:
   438             return self.cursor.execute(query, self._param_generator(params))
   507             return self.cursor.execute(query, self._param_generator(params))
   439         except DatabaseError, e:
   508         except Database.IntegrityError, e:
       
   509             raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
       
   510         except Database.DatabaseError, e:
   440             # cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
   511             # cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
   441             if e.args[0].code == 1400 and not isinstance(e, IntegrityError):
   512             if hasattr(e.args[0], 'code') and e.args[0].code == 1400 and not isinstance(e, IntegrityError):
   442                 e = IntegrityError(e.args[0])
   513                 raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
   443             raise e
   514             raise utils.DatabaseError, utils.DatabaseError(*tuple(e)), sys.exc_info()[2]
   444 
   515 
   445     def executemany(self, query, params=None):
   516     def executemany(self, query, params=None):
   446         try:
   517         try:
   447             args = [(':arg%d' % i) for i in range(len(params[0]))]
   518             args = [(':arg%d' % i) for i in range(len(params[0]))]
   448         except (IndexError, TypeError):
   519         except (IndexError, TypeError):
   458         formatted = [self._format_params(i) for i in params]
   529         formatted = [self._format_params(i) for i in params]
   459         self._guess_input_sizes(formatted)
   530         self._guess_input_sizes(formatted)
   460         try:
   531         try:
   461             return self.cursor.executemany(query,
   532             return self.cursor.executemany(query,
   462                                 [self._param_generator(p) for p in formatted])
   533                                 [self._param_generator(p) for p in formatted])
   463         except DatabaseError, e:
   534         except Database.IntegrityError, e:
       
   535             raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
       
   536         except Database.DatabaseError, e:
   464             # cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
   537             # cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
   465             if e.args[0].code == 1400 and not isinstance(e, IntegrityError):
   538             if hasattr(e.args[0], 'code') and e.args[0].code == 1400 and not isinstance(e, IntegrityError):
   466                 e = IntegrityError(e.args[0])
   539                 raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
   467             raise e
   540             raise utils.DatabaseError, utils.DatabaseError(*tuple(e)), sys.exc_info()[2]
   468 
   541 
   469     def fetchone(self):
   542     def fetchone(self):
   470         row = self.cursor.fetchone()
   543         row = self.cursor.fetchone()
   471         if row is None:
   544         if row is None:
   472             return row
   545             return row
   473         return self._rowfactory(row)
   546         return _rowfactory(row, self.cursor)
   474 
   547 
   475     def fetchmany(self, size=None):
   548     def fetchmany(self, size=None):
   476         if size is None:
   549         if size is None:
   477             size = self.arraysize
   550             size = self.arraysize
   478         return tuple([self._rowfactory(r)
   551         return tuple([_rowfactory(r, self.cursor)
   479                       for r in self.cursor.fetchmany(size)])
   552                       for r in self.cursor.fetchmany(size)])
   480 
   553 
   481     def fetchall(self):
   554     def fetchall(self):
   482         return tuple([self._rowfactory(r)
   555         return tuple([_rowfactory(r, self.cursor)
   483                       for r in self.cursor.fetchall()])
   556                       for r in self.cursor.fetchall()])
   484 
   557 
   485     def _rowfactory(self, row):
   558     def var(self, *args):
   486         # Cast numeric values as the appropriate Python type based upon the
   559         return VariableWrapper(self.cursor.var(*args))
   487         # cursor description, and convert strings to unicode.
   560 
   488         casted = []
   561     def arrayvar(self, *args):
   489         for value, desc in zip(row, self.cursor.description):
   562         return VariableWrapper(self.cursor.arrayvar(*args))
   490             if value is not None and desc[1] is Database.NUMBER:
       
   491                 precision, scale = desc[4:6]
       
   492                 if scale == -127:
       
   493                     if precision == 0:
       
   494                         # NUMBER column: decimal-precision floating point
       
   495                         # This will normally be an integer from a sequence,
       
   496                         # but it could be a decimal value.
       
   497                         if '.' in value:
       
   498                             value = Decimal(value)
       
   499                         else:
       
   500                             value = int(value)
       
   501                     else:
       
   502                         # FLOAT column: binary-precision floating point.
       
   503                         # This comes from FloatField columns.
       
   504                         value = float(value)
       
   505                 elif precision > 0:
       
   506                     # NUMBER(p,s) column: decimal-precision fixed point.
       
   507                     # This comes from IntField and DecimalField columns.
       
   508                     if scale == 0:
       
   509                         value = int(value)
       
   510                     else:
       
   511                         value = Decimal(value)
       
   512                 elif '.' in value:
       
   513                     # No type information. This normally comes from a
       
   514                     # mathematical expression in the SELECT list. Guess int
       
   515                     # or Decimal based on whether it has a decimal point.
       
   516                     value = Decimal(value)
       
   517                 else:
       
   518                     value = int(value)
       
   519             elif desc[1] in (Database.STRING, Database.FIXED_CHAR,
       
   520                              Database.LONG_STRING):
       
   521                 value = to_unicode(value)
       
   522             casted.append(value)
       
   523         return tuple(casted)
       
   524 
   563 
   525     def __getattr__(self, attr):
   564     def __getattr__(self, attr):
   526         if attr in self.__dict__:
   565         if attr in self.__dict__:
   527             return self.__dict__[attr]
   566             return self.__dict__[attr]
   528         else:
   567         else:
   529             return getattr(self.cursor, attr)
   568             return getattr(self.cursor, attr)
   530 
   569 
   531     def __iter__(self):
   570     def __iter__(self):
   532         return iter(self.cursor)
   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)
   533 
   628 
   534 
   629 
   535 def to_unicode(s):
   630 def to_unicode(s):
   536     """
   631     """
   537     Convert strings to Unicode objects (and return all other data types
   632     Convert strings to Unicode objects (and return all other data types