Skip to content

Commit a39bfe1

Browse files
francoislefebvrepeakwinter
authored andcommitted
Port OPOSM-2386 fix, Support Unique Nullable constraints
1 parent ecf8dde commit a39bfe1

File tree

2 files changed

+17
-3
lines changed

2 files changed

+17
-3
lines changed

sql_server/pyodbc/features.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
1616
ignores_quoted_identifier_case = True
1717
requires_literal_defaults = True
1818
requires_sqlparse_for_splitting = False
19-
supports_nullable_unique_constraints = False
19+
supports_nullable_unique_constraints = True
2020
supports_paramstyle_pyformat = False
2121
supports_partially_nullable_unique_constraints = False
2222
supports_regex_backreferencing = False

sql_server/pyodbc/schema.py

Lines changed: 16 additions & 2 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
"""
@@ -307,9 +309,13 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
307309
if post_actions:
308310
for sql, params in post_actions:
309311
self.execute(sql, params)
310-
# Added a unique?
311312
if not old_field.unique and new_field.unique:
312-
self.execute(self._create_unique_sql(model, [new_field.column]))
313+
if new_field.null:
314+
self.execute(
315+
self._create_index_sql(model, [new_field], sql=self.sql_create_unique_null, suffix="_uniq")
316+
)
317+
else:
318+
self.execute(self._create_unique_sql(model, [new_field.column]))
313319
# Added an index?
314320
# constraint will no longer be used in lieu of an index. The following
315321
# lines from the truth table show all True cases; the rest are False:
@@ -483,6 +489,10 @@ def add_field(self, model, field):
483489
# It might not actually have a column behind it
484490
if definition is None:
485491
return
492+
if field.null and field.unique:
493+
definition = definition.replace(' UNIQUE', '')
494+
self.deferred_sql.append(
495+
self._create_index_sql(model, [field], sql=self.sql_create_unique_null, suffix="_uniq"))
486496
# Check constraints can go on the column SQL here
487497
db_params = field.db_parameters(connection=self.connection)
488498
if db_params['check']:
@@ -525,6 +535,10 @@ def create_model(self, model):
525535
definition, extra_params = self.column_sql(model, field)
526536
if definition is None:
527537
continue
538+
if field.null and field.unique:
539+
definition = definition.replace(' UNIQUE', '')
540+
self.deferred_sql.append(self._create_index_sql(
541+
model, [field], sql=self.sql_create_unique_null, suffix="_uniq"))
528542
# Check constraints can go on the column SQL here
529543
db_params = field.db_parameters(connection=self.connection)
530544
if db_params['check']:

0 commit comments

Comments
 (0)