From 9db1b41708cded53698fe38b50484a6cac0bf44a Mon Sep 17 00:00:00 2001 From: Trey t Date: Fri, 28 Nov 2025 08:54:30 -0600 Subject: [PATCH] Fix seed SQL execution to handle multiple statements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PostgreSQL prepared statements don't support multiple commands. Split SQL file into individual statements and execute each one, handling string literals and comment-only blocks properly. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- internal/admin/handlers/settings_handler.go | 78 ++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/internal/admin/handlers/settings_handler.go b/internal/admin/handlers/settings_handler.go index f48d141..eef41ce 100644 --- a/internal/admin/handlers/settings_handler.go +++ b/internal/admin/handlers/settings_handler.go @@ -4,6 +4,7 @@ import ( "net/http" "os" "path/filepath" + "strings" "github.com/gin-gonic/gin" "gorm.io/gorm" @@ -124,5 +125,80 @@ func (h *AdminSettingsHandler) runSeedFile(filename string) error { return err } - return h.db.Exec(string(sqlContent)).Error + // Split SQL into individual statements and execute each one + // This is needed because GORM/PostgreSQL prepared statements don't support multiple commands + statements := splitSQLStatements(string(sqlContent)) + + for _, stmt := range statements { + stmt = strings.TrimSpace(stmt) + if stmt == "" { + continue + } + if err := h.db.Exec(stmt).Error; err != nil { + return err + } + } + + return nil +} + +// splitSQLStatements splits SQL content into individual statements +func splitSQLStatements(sql string) []string { + var statements []string + var current strings.Builder + inString := false + stringChar := byte(0) + + for i := 0; i < len(sql); i++ { + c := sql[i] + + // Handle string literals (don't split on semicolons inside strings) + if (c == '\'' || c == '"') && (i == 0 || sql[i-1] != '\\') { + if !inString { + inString = true + stringChar = c + } else if c == stringChar { + // Check for escaped quote ('') + if c == '\'' && i+1 < len(sql) && sql[i+1] == '\'' { + current.WriteByte(c) + i++ + current.WriteByte(sql[i]) + continue + } + inString = false + } + } + + // Split on semicolon if not in string + if c == ';' && !inString { + current.WriteByte(c) + stmt := strings.TrimSpace(current.String()) + if stmt != "" && !isCommentOnly(stmt) { + statements = append(statements, stmt) + } + current.Reset() + continue + } + + current.WriteByte(c) + } + + // Don't forget the last statement if it doesn't end with semicolon + if stmt := strings.TrimSpace(current.String()); stmt != "" && !isCommentOnly(stmt) { + statements = append(statements, stmt) + } + + return statements +} + +// isCommentOnly checks if the statement is only comments +func isCommentOnly(stmt string) bool { + lines := strings.Split(stmt, "\n") + for _, line := range lines { + line = strings.TrimSpace(line) + if line != "" && !strings.HasPrefix(line, "--") { + return false + } + } + return true }