package sqlexec import ( "context" "fmt" "sort" "github.com/jackc/pgx/v5" "git.warky.dev/wdevs/relspecgo/pkg/models" "git.warky.dev/wdevs/relspecgo/pkg/writers" ) // Writer implements the writers.Writer interface for executing SQL scripts type Writer struct { options *writers.WriterOptions } // NewWriter creates a new SQL executor writer func NewWriter(options *writers.WriterOptions) *Writer { return &Writer{ options: options, } } // WriteDatabase executes all scripts from all schemas in the database func (w *Writer) WriteDatabase(db *models.Database) error { if db == nil { return fmt.Errorf("database is nil") } // Get connection string from metadata connString, ok := w.options.Metadata["connection_string"].(string) if !ok || connString == "" { return fmt.Errorf("connection_string is required in writer metadata") } // Connect to database ctx := context.Background() conn, err := pgx.Connect(ctx, connString) if err != nil { return fmt.Errorf("failed to connect to database: %w", err) } defer conn.Close(ctx) // Execute scripts from all schemas for _, schema := range db.Schemas { if err := w.executeScripts(ctx, conn, schema.Scripts); err != nil { return fmt.Errorf("failed to execute scripts from schema %s: %w", schema.Name, err) } } return nil } // WriteSchema executes all scripts from a single schema func (w *Writer) WriteSchema(schema *models.Schema) error { if schema == nil { return fmt.Errorf("schema is nil") } // Get connection string from metadata connString, ok := w.options.Metadata["connection_string"].(string) if !ok || connString == "" { return fmt.Errorf("connection_string is required in writer metadata") } // Connect to database ctx := context.Background() conn, err := pgx.Connect(ctx, connString) if err != nil { return fmt.Errorf("failed to connect to database: %w", err) } defer conn.Close(ctx) // Execute scripts if err := w.executeScripts(ctx, conn, schema.Scripts); err != nil { return fmt.Errorf("failed to execute scripts: %w", err) } return nil } // WriteTable is not applicable for SQL script execution func (w *Writer) WriteTable(table *models.Table) error { return fmt.Errorf("WriteTable is not supported for SQL script execution") } // executeScripts executes scripts in Priority then Sequence order func (w *Writer) executeScripts(ctx context.Context, conn *pgx.Conn, scripts []*models.Script) error { if len(scripts) == 0 { return nil } // Sort scripts by Priority (ascending) then Sequence (ascending) sortedScripts := make([]*models.Script, len(scripts)) copy(sortedScripts, scripts) sort.Slice(sortedScripts, func(i, j int) bool { if sortedScripts[i].Priority != sortedScripts[j].Priority { return sortedScripts[i].Priority < sortedScripts[j].Priority } return sortedScripts[i].Sequence < sortedScripts[j].Sequence }) // Execute each script in order for _, script := range sortedScripts { if script.SQL == "" { continue } fmt.Printf("Executing script: %s (Priority=%d, Sequence=%d)\n", script.Name, script.Priority, script.Sequence) // Execute the SQL script _, err := conn.Exec(ctx, script.SQL) if err != nil { return fmt.Errorf("failed to execute script %s (Priority=%d, Sequence=%d): %w", script.Name, script.Priority, script.Sequence, err) } fmt.Printf("✓ Successfully executed: %s\n", script.Name) } return nil }