From 08750add83d1617c882bcaafe4672ffb665a27a8 Mon Sep 17 00:00:00 2001 From: evilfox Date: Wed, 29 Jul 2026 09:25:35 +0300 Subject: [PATCH] Fix MySQL import abort on setval for tables without id. --- internal/mysqlimport/import.go | 44 ++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/internal/mysqlimport/import.go b/internal/mysqlimport/import.go index fd01a3d..1d3d14d 100644 --- a/internal/mysqlimport/import.go +++ b/internal/mysqlimport/import.go @@ -62,9 +62,14 @@ func ImportFile(ctx context.Context, pool *pgxpool.Pool, dumpPath string) error } defer tx.Rollback(ctx) - // Temporarily disable FK checks via deferred constraints where possible - if _, err := tx.Exec(ctx, `SET session_replication_role = replica`); err != nil { - log.Printf("warn: cannot set session_replication_role: %v", err) + // Temporarily disable FK checks (requires superuser). Use savepoint so failure does not abort TX. + if _, err := tx.Exec(ctx, `SAVEPOINT sp_repl_role`); err == nil { + if _, err := tx.Exec(ctx, `SET LOCAL session_replication_role = replica`); err != nil { + log.Printf("warn: cannot set session_replication_role: %v", err) + _, _ = tx.Exec(ctx, `ROLLBACK TO SAVEPOINT sp_repl_role`) + } else { + _, _ = tx.Exec(ctx, `RELEASE SAVEPOINT sp_repl_role`) + } } inserts := map[string][]string{} @@ -89,11 +94,25 @@ func ImportFile(ctx context.Context, pool *pgxpool.Pool, dumpPath string) error } } - // Fix sequences - for _, table := range tablesOrder { - _, _ = tx.Exec(ctx, fmt.Sprintf(` - SELECT setval(pg_get_serial_sequence('%s','id'), COALESCE((SELECT MAX(id) FROM %s), 1), true) + // Fix sequences only for tables with BIGSERIAL id. + // Do not run setval on wg_config_settings — it has no id column. + // In PostgreSQL any failed statement aborts the whole transaction even if Go ignores the error. + serialTables := []string{ + "users", "site_status", "servers", "servers_simple", "countries", + "wg_configs", "demo_keys", "awg_configs", "amnezia_configs", + "vless_configs", "admin_uploads", "user_servers", "license_keys", "rate_limits", + } + for _, table := range serialTables { + _, err := tx.Exec(ctx, fmt.Sprintf(` + SELECT setval( + pg_get_serial_sequence('%s', 'id'), + GREATEST(COALESCE((SELECT MAX(id) FROM %s), 1), 1), + true + ) `, table, table)) + if err != nil { + return fmt.Errorf("fix sequence %s: %w", table, err) + } } _, err = tx.Exec(ctx, ` @@ -101,11 +120,16 @@ func ImportFile(ctx context.Context, pool *pgxpool.Pool, dumpPath string) error VALUES ('mysql_import_done', '1', NOW()) ON CONFLICT (key) DO UPDATE SET value = '1', updated_at = NOW()`) if err != nil { - return err + return fmt.Errorf("mark import done: %w", err) } - if _, err := tx.Exec(ctx, `SET session_replication_role = DEFAULT`); err != nil { - log.Printf("warn: restore session_replication_role: %v", err) + if _, err := tx.Exec(ctx, `SAVEPOINT sp_repl_role_end`); err == nil { + if _, err := tx.Exec(ctx, `SET LOCAL session_replication_role = DEFAULT`); err != nil { + log.Printf("warn: restore session_replication_role: %v", err) + _, _ = tx.Exec(ctx, `ROLLBACK TO SAVEPOINT sp_repl_role_end`) + } else { + _, _ = tx.Exec(ctx, `RELEASE SAVEPOINT sp_repl_role_end`) + } } if err := tx.Commit(ctx); err != nil {