diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 8d3c09ab1a..6274d5bc55 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -654,6 +654,12 @@ class BaseDatabaseFeatures(object): # supported by the Python driver supports_paramstyle_pyformat = True + # Does the backend require literal defaults, rather than parameterised ones? + requires_literal_defaults = False + + # Does the backend require a connection reset after each material schema change? + connection_persists_old_columns = False + def __init__(self, connection): self.connection = connection diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index e6435a9e96..a363c09001 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -94,6 +94,8 @@ class DatabaseFeatures(BaseDatabaseFeatures): supports_combined_alters = False max_index_name_length = 30 nulls_order_largest = True + requires_literal_defaults = True + connection_persists_old_columns = True class DatabaseOperations(BaseDatabaseOperations): diff --git a/django/db/backends/oracle/schema.py b/django/db/backends/oracle/schema.py index c78294cad5..18d67b254f 100644 --- a/django/db/backends/oracle/schema.py +++ b/django/db/backends/oracle/schema.py @@ -1,4 +1,6 @@ import copy +import datetime +from django.utils import six from django.db.backends.schema import BaseDatabaseSchemaEditor from django.db.utils import DatabaseError @@ -89,3 +91,13 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): """ suffix = hex(hash(for_name)).upper()[1:] return self.normalize_name(for_name + "_" + suffix) + + def prepare_default(self, value): + if isinstance(value, (datetime.date, datetime.time, datetime.datetime)): + return "'%s'" % value + elif isinstance(value, six.string_types): + return repr(value) + elif isinstance(value, bool): + return "1" if value else "0" + else: + return str(value) diff --git a/django/db/backends/schema.py b/django/db/backends/schema.py index 7beae7417a..64098499f6 100644 --- a/django/db/backends/schema.py +++ b/django/db/backends/schema.py @@ -116,8 +116,14 @@ class BaseDatabaseSchemaEditor(object): # If we were told to include a default value, do so default_value = self.effective_default(field) if include_default and default_value is not None: - sql += " DEFAULT %s" - params += [default_value] + if self.connection.features.requires_literal_defaults: + # Some databases can't take defaults as a parameter (oracle) + # If this is the case, the individual schema backend should + # implement prepare_default + sql += " DEFAULT %s" % self.prepare_default(default_value) + else: + sql += " DEFAULT %s" + params += [default_value] # Oracle treats the empty string ('') as null, so coerce the null # option whenever '' is a possible value. if (field.empty_strings_allowed and not field.primary_key and @@ -135,6 +141,12 @@ class BaseDatabaseSchemaEditor(object): # Return the sql return sql, params + def prepare_default(self, value): + """ + Only used for backends which have requires_literal_defaults feature + """ + raise NotImplementedError() + def effective_default(self, field): """ Returns a field's effective database default value @@ -385,6 +397,9 @@ class BaseDatabaseSchemaEditor(object): "to_column": self.quote_name(to_column), } ) + # Reset connection if required + if self.connection.features.connection_persists_old_columns: + self.connection.close() def remove_field(self, model, field): """ @@ -405,6 +420,9 @@ class BaseDatabaseSchemaEditor(object): "column": self.quote_name(field.column), } self.execute(sql) + # Reset connection if required + if self.connection.features.connection_persists_old_columns: + self.connection.close() def alter_field(self, model, old_field, new_field, strict=False): """ @@ -523,13 +541,25 @@ class BaseDatabaseSchemaEditor(object): [], )) else: - actions.append(( - self.sql_alter_column_default % { - "column": self.quote_name(new_field.column), - "default": "%s", - }, - [new_default], - )) + if self.connection.features.requires_literal_defaults: + # Some databases can't take defaults as a parameter (oracle) + # If this is the case, the individual schema backend should + # implement prepare_default + actions.append(( + self.sql_alter_column_default % { + "column": self.quote_name(new_field.column), + "default": self.prepare_default(new_default), + }, + [], + )) + else: + actions.append(( + self.sql_alter_column_default % { + "column": self.quote_name(new_field.column), + "default": "%s", + }, + [new_default], + )) # Nullability change? if old_field.null != new_field.null: if new_field.null: @@ -628,6 +658,9 @@ class BaseDatabaseSchemaEditor(object): "check": new_db_params['check'], } ) + # Reset connection if required + if self.connection.features.connection_persists_old_columns: + self.connection.close() def _alter_many_to_many(self, model, old_field, new_field, strict): """