77 "strings"
88
99 "github.com/jmoiron/sqlx"
10+ "github.com/rs/zerolog/log"
1011)
1112
1213type Migration struct {
@@ -80,6 +81,11 @@ var migrations = []Migration{
8081 Name : "add_webhook_use_proxy" ,
8182 UpSQL : addWebhookUseProxySQL ,
8283 },
84+ {
85+ ID : 12 ,
86+ Name : "repair_webhook_use_proxy" ,
87+ UpSQL : repairWebhookUseProxySQL ,
88+ },
8389}
8490
8591const changeIDToStringSQL = `
@@ -236,16 +242,19 @@ END $$;
236242
237243const addWebhookUseProxySQL = `
238244-- PostgreSQL version
239- DO $$
240- BEGIN
241- IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'users' AND column_name = 'webhook_use_proxy') THEN
242- ALTER TABLE users ADD COLUMN webhook_use_proxy BOOLEAN DEFAULT TRUE;
243- END IF;
244- END $$;
245+ ALTER TABLE users ADD COLUMN IF NOT EXISTS webhook_use_proxy BOOLEAN DEFAULT TRUE;
245246
246247-- SQLite version (handled in code)
247248`
248249
250+ // Migration 10 collided with migrations from another development branch and
251+ // also used an information_schema lookup that was not scoped to the active
252+ // schema. Keep this as a separate, globally unused migration ID so databases
253+ // that already recorded a different migration 10 or 11 are repaired.
254+ const repairWebhookUseProxySQL = `
255+ ALTER TABLE users ADD COLUMN IF NOT EXISTS webhook_use_proxy BOOLEAN DEFAULT TRUE;
256+ `
257+
249258// GenerateRandomID creates a random string ID
250259func GenerateRandomID () (string , error ) {
251260 bytes := make ([]byte , 16 ) // 128 bits
@@ -269,11 +278,51 @@ func initializeSchema(db *sqlx.DB) error {
269278 }
270279
271280 // Apply missing migrations
281+ sourceMigrations := make (map [int ]string , len (migrations ))
282+ for _ , migration := range migrations {
283+ sourceMigrations [migration .ID ] = migration .Name
284+ if appliedName , ok := applied [migration .ID ]; ok && appliedName != migration .Name {
285+ log .Warn ().
286+ Int ("migration_id" , migration .ID ).
287+ Str ("database_name" , appliedName ).
288+ Str ("binary_name" , migration .Name ).
289+ Msg ("Database migration ID has a different name in this binary" )
290+ }
291+ }
292+ for id , appliedName := range applied {
293+ if _ , ok := sourceMigrations [id ]; ! ok {
294+ log .Warn ().
295+ Int ("migration_id" , id ).
296+ Str ("database_name" , appliedName ).
297+ Msg ("Database contains a migration unknown to this binary" )
298+ }
299+ }
300+
301+ pending := 0
302+ for _ , migration := range migrations {
303+ if _ , ok := applied [migration .ID ]; ! ok {
304+ pending ++
305+ }
306+ }
307+ log .Info ().
308+ Str ("driver" , db .DriverName ()).
309+ Int ("applied" , len (applied )).
310+ Int ("pending" , pending ).
311+ Msg ("Database migration status" )
312+
272313 for _ , migration := range migrations {
273314 if _ , ok := applied [migration .ID ]; ! ok {
315+ log .Info ().
316+ Int ("migration_id" , migration .ID ).
317+ Str ("migration_name" , migration .Name ).
318+ Msg ("Applying database migration" )
274319 if err := applyMigration (db , migration ); err != nil {
275320 return fmt .Errorf ("failed to apply migration %d: %w" , migration .ID , err )
276321 }
322+ log .Info ().
323+ Int ("migration_id" , migration .ID ).
324+ Str ("migration_name" , migration .Name ).
325+ Msg ("Database migration applied" )
277326 }
278327 }
279328
@@ -322,8 +371,8 @@ func createMigrationsTable(db *sqlx.DB) error {
322371 return nil
323372}
324373
325- func getAppliedMigrations (db * sqlx.DB ) (map [int ]struct {} , error ) {
326- applied := make (map [int ]struct {} )
374+ func getAppliedMigrations (db * sqlx.DB ) (map [int ]string , error ) {
375+ applied := make (map [int ]string )
327376 var rows []struct {
328377 ID int `db:"id"`
329378 Name string `db:"name"`
@@ -335,7 +384,7 @@ func getAppliedMigrations(db *sqlx.DB) (map[int]struct{}, error) {
335384 }
336385
337386 for _ , row := range rows {
338- applied [row .ID ] = struct {}{}
387+ applied [row .ID ] = row . Name
339388 }
340389
341390 return applied , nil
@@ -473,7 +522,7 @@ func applyMigration(db *sqlx.DB, migration Migration) error {
473522 } else {
474523 _ , err = tx .Exec (migration .UpSQL )
475524 }
476- } else if migration .ID == 10 {
525+ } else if migration .ID == 10 || migration . ID == 12 {
477526 if db .DriverName () == "sqlite" {
478527 err = addColumnIfNotExistsSQLite (tx , "users" , "webhook_use_proxy" , "BOOLEAN DEFAULT 1" )
479528 } else {
0 commit comments