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" |
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 |