diff options
author | Andrew Godwin <andrew@aeracode.org> | 2013-08-13 20:54:57 +0100 |
---|---|---|
committer | Andrew Godwin <andrew@aeracode.org> | 2013-08-13 20:54:57 +0100 |
commit | 157604a87fa7e1331c25fcbed558f0799aa5b8df (patch) | |
tree | e379b3b5395f2b804035bc168663e6575b97842f /django/db/backends/oracle/schema.py | |
parent | 44f907dd980defaab2c06b4ead2255ec3566bcd5 (diff) | |
download | django-157604a87fa7e1331c25fcbed558f0799aa5b8df.tar.gz |
Oracle schema backend, passes most tests and is pretty complete.
Diffstat (limited to 'django/db/backends/oracle/schema.py')
-rw-r--r-- | django/db/backends/oracle/schema.py | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/django/db/backends/oracle/schema.py b/django/db/backends/oracle/schema.py index 4a679e79eb..c78294cad5 100644 --- a/django/db/backends/oracle/schema.py +++ b/django/db/backends/oracle/schema.py @@ -1,4 +1,6 @@ +import copy from django.db.backends.schema import BaseDatabaseSchemaEditor +from django.db.utils import DatabaseError class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): @@ -12,3 +14,78 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): sql_delete_column = "ALTER TABLE %(table)s DROP COLUMN %(column)s" sql_delete_table = "DROP TABLE %(table)s CASCADE CONSTRAINTS" + def delete_model(self, model): + # Run superclass action + super(DatabaseSchemaEditor, self).delete_model(model) + # Clean up any autoincrement trigger + self.execute(""" + DECLARE + i INTEGER; + BEGIN + SELECT COUNT(*) INTO i FROM USER_CATALOG + WHERE TABLE_NAME = '%(sq_name)s' AND TABLE_TYPE = 'SEQUENCE'; + IF i = 1 THEN + EXECUTE IMMEDIATE 'DROP SEQUENCE "%(sq_name)s"'; + END IF; + END; + /""" % {'sq_name': self.connection.ops._get_sequence_name(model._meta.db_table)}) + + def alter_field(self, model, old_field, new_field, strict=False): + try: + # Run superclass action + super(DatabaseSchemaEditor, self).alter_field(model, old_field, new_field, strict) + except DatabaseError as e: + description = str(e) + # If we're changing to/from LOB fields, we need to do a + # SQLite-ish workaround + if 'ORA-22858' in description or 'ORA-22859' in description: + self._alter_field_lob_workaround(model, old_field, new_field) + else: + raise + + def _alter_field_lob_workaround(self, model, old_field, new_field): + """ + Oracle refuses to change a column type from/to LOB to/from a regular + column. In Django, this shows up when the field is changed from/to + a TextField. + What we need to do instead is: + - Add the desired field with a temporary name + - Update the table to transfer values from old to new + - Drop old column + - Rename the new column + """ + # Make a new field that's like the new one but with a temporary + # column name. + new_temp_field = copy.deepcopy(new_field) + new_temp_field.column = self._generate_temp_name(new_field.column) + # Add it + self.add_field(model, new_temp_field) + # Transfer values across + self.execute("UPDATE %s set %s=%s" % ( + self.quote_name(model._meta.db_table), + self.quote_name(new_temp_field.column), + self.quote_name(old_field.column), + )) + # Drop the old field + self.remove_field(model, old_field) + # Rename the new field + self.alter_field(model, new_temp_field, new_field) + # Close the connection to force cx_Oracle to get column types right + # on a new cursor + self.connection.close() + + def normalize_name(self, name): + """ + Get the properly shortened and uppercased identifier as returned by quote_name(), but without the actual quotes. + """ + nn = self.quote_name(name) + if nn[0] == '"' and nn[-1] == '"': + nn = nn[1:-1] + return nn + + def _generate_temp_name(self, for_name): + """ + Generates temporary names for workarounds that need temp columns + """ + suffix = hex(hash(for_name)).upper()[1:] + return self.normalize_name(for_name + "_" + suffix) |