Skip to content

Commit aaf912f

Browse files
author
Ruben De Visscher
committed
Merge PR michiya#144 on upstream github repo. JIRA: DEV-5463
2 parents 75cc66d + 85bd6bf commit aaf912f

File tree

2 files changed

+24
-5
lines changed

2 files changed

+24
-5
lines changed

sql_server/pyodbc/features.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
1919
requires_literal_defaults = True
2020
requires_sqlparse_for_splitting = False
2121
supports_index_on_text_field = False
22-
supports_nullable_unique_constraints = False
22+
supports_nullable_unique_constraints = True
2323
supports_paramstyle_pyformat = False
2424
supports_partially_nullable_unique_constraints = False
2525
supports_regex_backreferencing = False

sql_server/pyodbc/schema.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
5252
sql_delete_table = "DROP TABLE %(table)s"
5353
sql_rename_column = "EXEC sp_rename '%(table)s.%(old_column)s', %(new_column)s, 'COLUMN'"
5454
sql_rename_table = "EXEC sp_rename %(old_table)s, %(new_table)s"
55+
sql_create_unique_null = "CREATE UNIQUE INDEX %(name)s ON %(table)s(%(columns)s) " \
56+
"WHERE %(columns)s IS NOT NULL"
5557

5658
def _alter_column_default_sql(self, model, old_field, new_field, drop=False):
5759
"""
@@ -207,6 +209,11 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
207209
# is to look at its name (refs #28053).
208210
continue
209211
self.execute(self._delete_constraint_sql(self.sql_delete_index, model, index_name))
212+
# Drop any unique nullable index/constraints, we'll remake them later if need be
213+
if old_field.unique and old_field.null:
214+
index_names = self._constraint_names(model, [old_field.column], unique=True, index=True)
215+
for index_name in index_names:
216+
self.execute(self._delete_constraint_sql(self.sql_delete_index, model, index_name))
210217
# Change check constraints?
211218
if (old_db_params['check'] != new_db_params['check'] and old_db_params['check']) or (
212219
# SQL Server requires explicit deletion befor altering column type with the same constraint
@@ -316,9 +323,13 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
316323
if post_actions:
317324
for sql, params in post_actions:
318325
self.execute(sql, params)
319-
# Added a unique?
320-
if not old_field.unique and new_field.unique:
321-
self.execute(self._create_unique_sql(model, [new_field.column]))
326+
if new_field.unique:
327+
if new_field.null:
328+
self.execute(
329+
self._create_index_sql(model, [new_field], sql=self.sql_create_unique_null, suffix="_uniq")
330+
)
331+
elif not old_field.unique:
332+
self.execute(self._create_unique_sql(model, [new_field.column]))
322333
# Added an index?
323334
# constraint will no longer be used in lieu of an index. The following
324335
# lines from the truth table show all True cases; the rest are False:
@@ -498,6 +509,10 @@ def add_field(self, model, field):
498509
# It might not actually have a column behind it
499510
if definition is None:
500511
return
512+
if field.null and field.unique:
513+
definition = definition.replace(' UNIQUE', '')
514+
self.deferred_sql.append(
515+
self._create_index_sql(model, [field], sql=self.sql_create_unique_null, suffix="_uniq"))
501516
# Check constraints can go on the column SQL here
502517
db_params = field.db_parameters(connection=self.connection)
503518
if db_params['check']:
@@ -540,6 +555,10 @@ def create_model(self, model):
540555
definition, extra_params = self.column_sql(model, field)
541556
if definition is None:
542557
continue
558+
if field.null and field.unique:
559+
definition = definition.replace(' UNIQUE', '')
560+
self.deferred_sql.append(self._create_index_sql(
561+
model, [field], sql=self.sql_create_unique_null, suffix="_uniq"))
543562
# Check constraints can go on the column SQL here
544563
db_params = field.db_parameters(connection=self.connection)
545564
if db_params['check']:
@@ -724,7 +743,7 @@ def remove_field(self, model, field):
724743
})
725744
# Drop unique constraints, SQL Server requires explicit deletion
726745
for name, infodict in constraints.items():
727-
if field.column in infodict['columns'] and infodict['unique'] and not infodict['primary_key']:
746+
if field.column in infodict['columns'] and infodict['unique'] and not infodict['primary_key'] and not infodict['index']:
728747
self.execute(self.sql_delete_unique % {
729748
"table": self.quote_name(model._meta.db_table),
730749
"name": self.quote_name(name),

0 commit comments

Comments
 (0)