2 Oracle database backend for Django.
4 Requires cx_Oracle: http://cx-oracle.sourceforge.net/
12 from decimal
import Decimal
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'
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
)
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
34 DatabaseError
= Database
.DatabaseError
35 IntegrityError
= Database
.IntegrityError
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
43 convert_unicode
= smart_str
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
55 class DatabaseOperations(BaseDatabaseOperations
):
56 compiler_module
= "django.db.backends.oracle.compiler"
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
)
69 SELECT COUNT(*) INTO i FROM USER_CATALOG
70 WHERE TABLE_NAME = '%(sq_name)s' AND TABLE_TYPE = 'SEQUENCE';
72 EXECUTE IMMEDIATE 'CREATE SEQUENCE "%(sq_name)s"';
77 CREATE OR REPLACE TRIGGER "%(tr_name)s"
78 BEFORE INSERT ON %(tbl_name)s
80 WHEN (new.%(col_name)s IS NULL)
82 SELECT "%(sq_name)s".nextval
83 INTO :new.%(col_name)s FROM dual;
86 return sequence_sql
, trigger_sql
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
94 return "EXTRACT(%s FROM %s)" % (lookup_type
, field_name
)
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
102 sql
= "TRUNC(%s, '%s')" % (field_name
, lookup_type
)
105 def convert_values(self
, value
, field
):
106 if isinstance(value
, Database
.LOB
):
108 if field
and field
.get_internal_type() == 'TextField':
109 value
= force_unicode(value
)
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
115 if value
is None and field
and field
.empty_strings_allowed
:
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'):
120 # Force floats to the correct type
121 elif value
is not None and field
and field
.get_internal_type() == 'FloatField':
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
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
,
143 if field
and field
.get_internal_type() == 'DateTimeField':
145 elif field
and field
.get_internal_type() == 'DateField':
147 elif field
and field
.get_internal_type() == 'TimeField' or (value
.year
== 1900 and value
.month
== value
.day
== 1):
149 elif value
.hour
== value
.minute
== value
.second
== value
.microsecond
== 0:
153 def datetime_cast_sql(self
):
154 return "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS.FF')"
156 def deferrable_sql(self
):
157 return " DEFERRABLE INITIALLY DEFERRED"
159 def drop_sequence_sql(self
, table
):
160 return "DROP SEQUENCE %s;" % self
.quote_name(get_sequence_name(table
))
162 def fetch_returned_insert_id(self
, cursor
):
163 return long(cursor
._insert
_id
_var
.getvalue())
165 def field_cast_sql(self
, db_type
):
166 if db_type
and db_type
.endswith('LOB'):
167 return "DBMS_LOB.SUBSTR(%s)"
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]
176 def lookup_cast(self
, lookup_type
):
177 if lookup_type
in ('iexact', 'icontains', 'istartswith', 'iendswith'):
181 def max_name_length(self
):
184 def prep_for_iexact_query(self
, x
):
187 def process_clob(self
, value
):
190 return force_unicode(value
.read())
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())
202 def random_function_sql(self
):
203 return "DBMS_RANDOM.RANDOM"
205 def regex_lookup_9(self
, lookup_type
):
206 raise NotImplementedError("Regexes are not supported in Oracle before version 10g.")
208 def regex_lookup_10(self
, lookup_type
):
209 if lookup_type
== 'regex':
213 return 'REGEXP_LIKE(%%s, %%s, %s)' % match_option
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
220 return connection
.ops
.regex_lookup(lookup_type
)
222 def return_insert_id(self
):
223 return "RETURNING %s INTO %%s", (InsertIdVar(),)
225 def savepoint_create_sql(self
, sid
):
226 return convert_unicode("SAVEPOINT " + self
.quote_name(sid
))
228 def savepoint_rollback_sql(self
, sid
):
229 return convert_unicode("ROLLBACK TO SAVEPOINT " + self
.quote_name(sid
))
231 def sql_flush(self
, style
, tables
, sequences
):
232 # Return a list of 'TRUNCATE x;', 'TRUNCATE y;',
233 # 'TRUNCATE z;'... style SQL statements
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
)))
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
,
250 'column': column_name
}
256 def sequence_reset_sql(self
, style
, model_list
):
257 from django
.db
import models
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
,
268 'column': column_name
})
269 # Only one AutoField is allowed per model, so don't
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
,
279 'column': column_name
})
282 def start_transaction_sql(self
):
285 def tablespace_sql(self
, tablespace
, inline
=False):
286 return "%sTABLESPACE %s" % ((inline
and "USING INDEX " or ""),
287 self
.quote_name(tablespace
))
289 def value_to_db_time(self
, value
):
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
)
297 def year_lookup_bounds_for_date_field(self
, value
):
300 return [first
% value
, second
% value
]
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
)
313 class DatabaseWrapper(BaseDatabaseWrapper
):
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)",
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)",
329 oracle_version
= None
331 def __init__(self
, *args
, **kwargs
):
332 super(DatabaseWrapper
, self
).__init
__(*args
, **kwargs
)
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
)
341 def _valid_connection(self
):
342 return self
.connection
is not None
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'])
353 dsn
= settings_dict
['NAME']
354 return "%s/%s@%s" % (settings_dict
['USER'],
355 settings_dict
['PASSWORD'], dsn
)
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'")
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
378 self
.ops
.regex_lookup
= self
.ops
.regex_lookup_10
382 self
.connection
.stmtcachesize
= 20
384 # Django docs specify cx_Oracle version 4.3.1 or higher, but
385 # stmtcachesize is available only in 4.3.2 and up.
387 connection_created
.send(sender
=self
.__class
__)
389 cursor
= FormatStylePlaceholderCursor(self
.connection
)
392 # Oracle doesn't support savepoint commits. Ignore them.
393 def _savepoint_commit(self
, sid
):
397 class OracleParam(object):
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.
407 def __init__(self
, param
, cursor
, strings_only
=False):
408 if hasattr(param
, 'bind_parameter'):
409 self
.smart_str
= param
.bind_parameter(cursor
)
411 self
.smart_str
= convert_unicode(param
, cursor
.charset
,
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
420 self
.input_size
= None
423 class VariableWrapper(object):
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.
431 def __init__(self
, var
):
434 def bind_parameter(self
, cursor
):
437 def __getattr__(self
, key
):
438 return getattr(self
.var
, key
)
440 def __setattr__(self
, key
, value
):
442 self
.__dict
__[key
] = value
444 setattr(self
.var
, key
, value
)
447 class InsertIdVar(object):
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
454 def bind_parameter(self
, cursor
):
455 param
= cursor
.cursor
.var(Database
.NUMBER
)
456 cursor
._insert
_id
_var
= param
460 class FormatStylePlaceholderCursor(object):
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".
466 We also do automatic conversion between Unicode on the Python side and
467 UTF-8 -- for talking to Oracle -- in here.
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
478 def _format_params(self
, params
):
479 return tuple([OracleParam(p
, self
, True) for p
in params
])
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
):
486 sizes
[i
] = value
.input_size
487 self
.setinputsizes(*sizes
)
489 def _param_generator(self
, params
):
490 return [p
.smart_str
for p
in params
]
492 def execute(self
, query
, params
=None):
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('/'):
504 query
= convert_unicode(query
% tuple(args
), self
.charset
)
505 self
._guess
_input
_sizes
([params
])
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]
516 def executemany(self
, query
, params
=None):
518 args
= [(':arg%d' % i
) for i
in range(len(params
[0]))]
519 except (IndexError, TypeError):
520 # No params given, nothing to do
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('/'):
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
)
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]
543 row
= self
.cursor
.fetchone()
546 return _rowfactory(row
, self
.cursor
)
548 def fetchmany(self
, size
=None):
550 size
= self
.arraysize
551 return tuple([_rowfactory(r
, self
.cursor
)
552 for r
in self
.cursor
.fetchmany(size
)])
555 return tuple([_rowfactory(r
, self
.cursor
)
556 for r
in self
.cursor
.fetchall()])
558 def var(self
, *args
):
559 return VariableWrapper(self
.cursor
.var(*args
))
561 def arrayvar(self
, *args
):
562 return VariableWrapper(self
.cursor
.arrayvar(*args
))
564 def __getattr__(self
, attr
):
565 if attr
in self
.__dict
__:
566 return self
.__dict
__[attr
]
568 return getattr(self
.cursor
, attr
)
571 return CursorIterator(self
.cursor
)
574 class CursorIterator(object):
576 """Cursor iterator wrapper that invokes our custom row factory."""
578 def __init__(self
, cursor
):
580 self
.iter = iter(cursor
)
586 return _rowfactory(self
.iter.next(), self
.cursor
)
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.
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]
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.
602 value
= Decimal(value
)
606 # FLOAT column: binary-precision floating point.
607 # This comes from FloatField columns.
610 # NUMBER(p,s) column: decimal-precision fixed point.
611 # This comes from IntField and DecimalField columns.
615 value
= Decimal(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
)
623 elif desc
[1] in (Database
.STRING
, Database
.FIXED_CHAR
,
624 Database
.LONG_STRING
):
625 value
= to_unicode(value
)
632 Convert strings to Unicode objects (and return all other data types
635 if isinstance(s
, basestring
):
636 return force_unicode(s
)
640 def _get_sequence_reset_sql():
641 # TODO: colorize this SQL code with style.SQL_KEYWORD(), etc.
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;
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';
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()
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()