From 7fc69dcc5523347d6ff3e2b82a2bd386e07c5816 Mon Sep 17 00:00:00 2001 From: Lakhan Samani Date: Fri, 21 Oct 2022 21:58:56 +0530 Subject: [PATCH] fix(server): sql server not allow multiple null multiple null values for unique constrained column is not allowed on sqlserver Resolves #278 --- server/db/providers/sql/provider.go | 37 +++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/server/db/providers/sql/provider.go b/server/db/providers/sql/provider.go index 712f3d1..cfe83eb 100644 --- a/server/db/providers/sql/provider.go +++ b/server/db/providers/sql/provider.go @@ -1,6 +1,7 @@ package sql import ( + "fmt" "log" "os" "time" @@ -21,6 +22,16 @@ type provider struct { db *gorm.DB } +const ( + phoneNumberIndexName = "UQ_phone_number" + phoneNumberColumnName = "phone_number" +) + +type indexInfo struct { + IndexName string `json:"index_name"` + ColumnName string `json:"column_name"` +} + // NewProvider returns a new SQL provider func NewProvider() (*provider, error) { var sqlDB *gorm.DB @@ -65,6 +76,32 @@ func NewProvider() (*provider, error) { if err != nil { return nil, err } + + // unique constraint on phone number does not work with multiple null values for sqlserver + // for more information check https://stackoverflow.com/a/767702 + if dbType == constants.DbTypeSqlserver { + var indexInfos []indexInfo + // remove index on phone number if present with different name + res := sqlDB.Raw("SELECT i.name AS index_name, i.type_desc AS index_algorithm, CASE i.is_unique WHEN 1 THEN 'TRUE' ELSE 'FALSE' END AS is_unique, ac.Name AS column_name FROM sys.tables AS t INNER JOIN sys.indexes AS i ON t.object_id = i.object_id INNER JOIN sys.index_columns AS ic ON ic.object_id = i.object_id AND ic.index_id = i.index_id INNER JOIN sys.all_columns AS ac ON ic.object_id = ac.object_id AND ic.column_id = ac.column_id WHERE t.name = 'authorizer_users' AND SCHEMA_NAME(t.schema_id) = 'dbo';").Scan(&indexInfos) + if res.Error != nil { + return nil, res.Error + } + + for _, val := range indexInfos { + if val.ColumnName == phoneNumberColumnName && val.IndexName != phoneNumberIndexName { + // drop index & create new + if res := sqlDB.Exec(fmt.Sprintf(`ALTER TABLE authorizer_users DROP CONSTRAINT "%s";`, val.IndexName)); res.Error != nil { + return nil, res.Error + } + + // create index + if res := sqlDB.Exec(fmt.Sprintf("CREATE UNIQUE NONCLUSTERED INDEX %s ON authorizer_users(phone_number) WHERE phone_number IS NOT NULL;", phoneNumberIndexName)); res.Error != nil { + return nil, res.Error + } + } + } + } + return &provider{ db: sqlDB, }, nil