MCPcopy Create free account
hub / github.com/authorizerdev/authorizer / clearLegacyColumnUniqueness

Function clearLegacyColumnUniqueness

internal/storage/db/sql/provider.go:150–226  ·  view source on GitHub ↗

clearLegacyColumnUniqueness removes any single-column UNIQUE constraint or index on the email / phone_number columns of authorizer_users and authorizer_otps, regardless of its name. See the call site in NewProvider for why this must run before AutoMigrate. Best-effort and non-fatal: fresh installs,

(db *gorm.DB, log *zerolog.Logger)

Source from the content-addressed store, hash-verified

148// installs, and catalogs without information_schema (sqlite), simply find
149// nothing to drop.
150func clearLegacyColumnUniqueness(db *gorm.DB, log *zerolog.Logger) {
151 // Run through a discarding GORM logger. The information_schema probe below
152 // intentionally errors on databases without that catalog (sqlite); GORM's
153 // default logger writes such errors to os.Stdout, which corrupts the MCP
154 // server's stdio JSON-RPC stream. We already surface anything actionable via
155 // the zerolog `log` (stderr), so GORM's own logging here is redundant.
156 db = db.Session(&gorm.Session{Logger: logger.Discard})
157
158 targets := map[string]bool{"email": true, "phone_number": true}
159 tables := []struct {
160 model any
161 name string
162 }{
163 {&schemas.User{}, "authorizer_users"},
164 {&schemas.OTP{}, "authorizer_otps"},
165 }
166
167 for _, t := range tables {
168 // 1) Drop single-column UNIQUE *constraints* by their real names, read
169 // from the same catalog GORM uses (information_schema). This matches
170 // ANY name — ..._key, uni_..., idx_..., or custom — which is what
171 // actually prevents the DROP CONSTRAINT "uni_..." 42704 abort. A
172 // constraint's backing index cannot be removed with DROP INDEX, so it
173 // must go via DropConstraint.
174 var rows []struct {
175 ConstraintName string `gorm:"column:constraint_name"`
176 ColumnName string `gorm:"column:column_name"`
177 }
178 const q = `SELECT tc.constraint_name AS constraint_name, kcu.column_name AS column_name
179 FROM information_schema.table_constraints tc
180 JOIN information_schema.key_column_usage kcu
181 ON tc.constraint_name = kcu.constraint_name
182 AND tc.table_schema = kcu.table_schema
183 AND tc.table_name = kcu.table_name
184 WHERE tc.constraint_type = 'UNIQUE' AND tc.table_name = ?`
185 if err := db.Raw(q, t.name).Scan(&rows).Error; err != nil {
186 // sqlite and other catalogs without information_schema land here; they
187 // are not affected by the GORM unique-reconciliation behaviour.
188 log.Debug().Err(err).Str("table", t.name).Msg("skip legacy unique-constraint scan")
189 } else {
190 columnsByConstraint := map[string][]string{}
191 for _, r := range rows {
192 columnsByConstraint[r.ConstraintName] = append(columnsByConstraint[r.ConstraintName], r.ColumnName)
193 }
194 for name, cols := range columnsByConstraint {
195 if len(cols) == 1 && targets[cols[0]] {
196 if err := db.Migrator().DropConstraint(t.model, name); err != nil {
197 log.Debug().Err(err).Str("constraint", name).Msg("failed to drop legacy unique constraint")
198 }
199 }
200 }
201 }
202
203 // 2) Drop any standalone single-column UNIQUE *index* (CREATE UNIQUE
204 // INDEX, not a constraint) on the same columns. These do not cause the
205 // abort — GORM reads column-uniqueness from table_constraints only —
206 // but removing them makes the column genuinely non-unique, matching the
207 // v2 application-layer model. GetIndexes already excludes

Callers 1

NewProviderFunction · 0.85

Calls 6

ScanMethod · 0.80
DropConstraintMethod · 0.80
MigratorMethod · 0.80
DropIndexMethod · 0.80
NameMethod · 0.80
SessionMethod · 0.65

Tested by

no test coverage detected