Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,11 @@ Dictionary. Current available keys are:
Integer. Sets the back off time in seconds for reries of
the database connection process. Default value is ``5``.

- supports_nullable_unique_constraints

Boolean. Supports nullable unique constraints.
Default value is ``False``.

backend-specific settings
~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
2 changes: 1 addition & 1 deletion sql_server/pyodbc/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ def __init__(self, *args, **kwargs):
ops[op] = '%s COLLATE %s' % (sql, collation)
self.operators.update(ops)

self.features = DatabaseFeatures(self)
self.features = DatabaseFeatures(self, opts.get('supports_nullable_unique_constraints', False))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it make more sense to just check connection.settings_dict['options'] within DatabaseFeatures.__init__? That way you don't need to change the signature around for every custom feature that's implemented.

Copy link
Author

@shlee322 shlee322 Jul 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevin-brown
i just wanted to overwirte BaseDatabaseFeatures.supports_nullable_unique_constraint

how would you think about :

self.features = DatabaseFeatures(self)
self.features.supports_nullable_unique_constraints = opts.get('supports_nullable_unique_constraints', False)

self.ops = DatabaseOperations(self)
self.client = DatabaseClient(self)
self.creation = DatabaseCreation(self)
Expand Down
4 changes: 4 additions & 0 deletions sql_server/pyodbc/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
supports_timezones = False
supports_transactions = True
uses_savepoints = True

def __init__(self, connection, supports_nullable_unique_constraints=False):
BaseDatabaseFeatures.__init__(self, connection)
self.supports_nullable_unique_constraints = supports_nullable_unique_constraints
25 changes: 24 additions & 1 deletion sql_server/pyodbc/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
sql_delete_table = "DROP TABLE %(table)s"
sql_rename_column = "EXEC sp_rename '%(table)s.%(old_column)s', %(new_column)s, 'COLUMN'"
sql_rename_table = "EXEC sp_rename %(old_table)s, %(new_table)s"
sql_create_unique_null = "CREATE UNIQUE INDEX %(name)s ON %(table)s(%(columns)s) " \
"WHERE %(columns)s IS NOT NULL"

def _alter_column_type_sql(self, table, old_field, new_field, new_type):
new_type = self._set_field_new_type_null_status(old_field, new_type)
Expand Down Expand Up @@ -269,7 +271,14 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
self.execute(sql, params)
# Added a unique?
if not old_field.unique and new_field.unique:
self.execute(self._create_unique_sql(model, [new_field.column]))
if new_field.null and self.connection.features.supports_nullable_unique_constraints:
self.execute(
self._create_index_sql(
model, [new_field], sql=self.sql_create_unique_null, suffix="_uniq"
)
)
else:
self.execute(self._create_unique_sql(model, [new_field.column]))
# Added an index?
if (not old_field.db_index and new_field.db_index and
not new_field.unique and not
Expand Down Expand Up @@ -398,6 +407,13 @@ def add_field(self, model, field):
# It might not actually have a column behind it
if definition is None:
return

if field.null and field.unique and self.connection.features.supports_nullable_unique_constraints:
definition = definition.replace(' UNIQUE', '')
self.deferred_sql.append(self._create_index_sql(
model, [field], sql=self.sql_create_unique_null, suffix="_uniq"
))

# Check constraints can go on the column SQL here
db_params = field.db_parameters(connection=self.connection)
if db_params['check']:
Expand Down Expand Up @@ -452,6 +468,13 @@ def create_model(self, model):
definition, extra_params = self.column_sql(model, field)
if definition is None:
continue

if field.null and field.unique and self.connection.features.supports_nullable_unique_constraints:
definition = definition.replace(' UNIQUE', '')
self.deferred_sql.append(self._create_index_sql(
model, [field], sql=self.sql_create_unique_null, suffix="_uniq"
))

# Check constraints can go on the column SQL here
db_params = field.db_parameters(connection=self.connection)
if db_params['check']:
Expand Down