Skip to content

Commit d6e8f4a

Browse files
authored
Merge pull request #1 from OskarPersson/nullable-unique-constraints
2 parents b54fc20 + d473909 commit d6e8f4a

File tree

2 files changed

+25
-2
lines changed

2 files changed

+25
-2
lines changed

sql_server/pyodbc/features.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
2020
requires_literal_defaults = True
2121
requires_sqlparse_for_splitting = False
2222
supports_1000_query_parameters = False
23-
supports_nullable_unique_constraints = False
23+
supports_nullable_unique_constraints = True
2424
supports_paramstyle_pyformat = False
2525
supports_partially_nullable_unique_constraints = False
2626
supports_regex_backreferencing = False

sql_server/pyodbc/schema.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
5050
sql_delete_table = "DROP TABLE %(table)s"
5151
sql_rename_column = "EXEC sp_rename '%(table)s.%(old_column)s', %(new_column)s, 'COLUMN'"
5252
sql_rename_table = "EXEC sp_rename %(old_table)s, %(new_table)s"
53+
sql_create_unique_null = "CREATE UNIQUE INDEX %(name)s ON %(table)s(%(columns)s) " \
54+
"WHERE %(columns)s IS NOT NULL"
5355

5456
def _alter_column_type_sql(self, table, old_field, new_field, new_type):
5557
new_type = self._set_field_new_type_null_status(old_field, new_type)
@@ -280,7 +282,14 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
280282
if (not old_field.unique and new_field.unique) or (
281283
old_field.primary_key and not new_field.primary_key and new_field.unique
282284
):
283-
self.execute(self._create_unique_sql(model, [new_field.column]))
285+
if not new_field.many_to_many and new_field.null:
286+
self.execute(
287+
self._create_index_sql(
288+
model, [new_field], sql=self.sql_create_unique_null, suffix="_uniq"
289+
)
290+
)
291+
else:
292+
self.execute(self._create_unique_sql(model, [new_field.column]))
284293
# Added an index?
285294
# constraint will no longer be used in lieu of an index. The following
286295
# lines from the truth table show all True cases; the rest are False:
@@ -473,6 +482,13 @@ def add_field(self, model, field):
473482
# It might not actually have a column behind it
474483
if definition is None:
475484
return
485+
486+
if not field.many_to_many and field.null and field.unique:
487+
definition = definition.replace(' UNIQUE', '')
488+
self.deferred_sql.append(self._create_index_sql(
489+
model, [field], sql=self.sql_create_unique_null, suffix="_uniq"
490+
))
491+
476492
# Check constraints can go on the column SQL here
477493
db_params = field.db_parameters(connection=self.connection)
478494
if db_params['check']:
@@ -527,6 +543,13 @@ def create_model(self, model):
527543
definition, extra_params = self.column_sql(model, field)
528544
if definition is None:
529545
continue
546+
547+
if not field.many_to_many and field.null and field.unique:
548+
definition = definition.replace(' UNIQUE', '')
549+
self.deferred_sql.append(self._create_index_sql(
550+
model, [field], sql=self.sql_create_unique_null, suffix="_uniq"
551+
))
552+
530553
# Check constraints can go on the column SQL here
531554
db_params = field.db_parameters(connection=self.connection)
532555
if db_params['check']:

0 commit comments

Comments
 (0)