Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions cmd/apply/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ func ApplyMigration(config *ApplyConfig, provider postgres.DesiredStateProvider)

// Set lock timeout before executing changes
if config.LockTimeout != "" {
_, err = conn.ExecContext(ctx, fmt.Sprintf("SET lock_timeout = '%s'", config.LockTimeout))
lockTimeoutSQL := fmt.Sprintf("SET lock_timeout = '%s'", config.LockTimeout)
_, err = util.ExecContextWithLogging(ctx, conn, lockTimeoutSQL, "set lock timeout")
if err != nil {
return fmt.Errorf("failed to set lock timeout: %w", err)
}
Expand All @@ -214,7 +215,8 @@ func ApplyMigration(config *ApplyConfig, provider postgres.DesiredStateProvider)
// Set search_path to target schema for unqualified table references
if config.Schema != "" && config.Schema != "public" {
quotedSchema := ir.QuoteIdentifier(config.Schema)
_, err = conn.ExecContext(ctx, fmt.Sprintf("SET search_path TO %s, public", quotedSchema))
searchPathSQL := fmt.Sprintf("SET search_path TO %s, public", quotedSchema)
_, err = util.ExecContextWithLogging(ctx, conn, searchPathSQL, "set search_path to target schema")
if err != nil {
return fmt.Errorf("failed to set search_path to target schema '%s': %w", config.Schema, err)
}
Expand Down Expand Up @@ -414,7 +416,7 @@ func executeGroupConcatenated(ctx context.Context, conn *sql.DB, group plan.Exec
}

// Execute all statements in a single call (implicit transaction)
_, err := conn.ExecContext(ctx, concatenatedSQL)
_, err := util.ExecContextWithLogging(ctx, conn, concatenatedSQL, fmt.Sprintf("execute %d statements in group %d", len(sqlStatements), groupNum))
if err != nil {
return fmt.Errorf("failed to execute concatenated statements in group %d: %w", groupNum, err)
}
Expand All @@ -437,7 +439,7 @@ func executeGroupIndividually(ctx context.Context, conn *sql.DB, group plan.Exec
fmt.Printf(" Executing: %s\n", truncateSQL(step.SQL, 80))
}

_, err := conn.ExecContext(ctx, step.SQL)
_, err := util.ExecContextWithLogging(ctx, conn, step.SQL, fmt.Sprintf("execute statement in group %d, step %d", groupNum, stepIdx+1))
if err != nil {
return fmt.Errorf("failed to execute statement in group %d, step %d: %w", groupNum, stepIdx+1, err)
}
Expand Down
29 changes: 29 additions & 0 deletions cmd/util/sql_logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package util

import (
"context"
"database/sql"

"github.com/pgschema/pgschema/internal/logger"
)

// ExecContextWithLogging executes SQL with debug logging if debug mode is enabled.
// It logs the SQL statement before execution and the result/error after execution.
func ExecContextWithLogging(ctx context.Context, db *sql.DB, sqlStmt string, description string) (sql.Result, error) {
isDebug := logger.IsDebug()
if isDebug {
logger.Get().Debug("Executing SQL", "description", description, "sql", sqlStmt)
}

result, err := db.ExecContext(ctx, sqlStmt)

if isDebug {
if err != nil {
logger.Get().Debug("SQL execution failed", "description", description, "error", err)
} else {
logger.Get().Debug("SQL execution succeeded", "description", description)
}
}

return result, err
}
13 changes: 10 additions & 3 deletions internal/logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,27 @@ func SetGlobal(logger *slog.Logger, debug bool) {
func Get() *slog.Logger {
mu.RLock()
defer mu.RUnlock()

if globalLogger != nil {
return globalLogger
}

// Fallback logger
level := slog.LevelInfo
if debugEnabled {
level = slog.LevelDebug
}

opts := &slog.HandlerOptions{
Level: level,
}
handler := slog.NewTextHandler(os.Stderr, opts)
return slog.New(handler)
}

// IsDebug returns whether debug mode is enabled
func IsDebug() bool {
mu.RLock()
defer mu.RUnlock()
return debugEnabled
}
8 changes: 4 additions & 4 deletions internal/postgres/embedded.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,19 +189,19 @@ func (ep *EmbeddedPostgres) GetSchemaName() string {
func (ep *EmbeddedPostgres) ApplySchema(ctx context.Context, schema string, sql string) error {
// Drop the temporary schema if it exists (CASCADE to drop all objects)
dropSchemaSQL := fmt.Sprintf("DROP SCHEMA IF EXISTS \"%s\" CASCADE", ep.tempSchema)
if _, err := ep.db.ExecContext(ctx, dropSchemaSQL); err != nil {
if _, err := util.ExecContextWithLogging(ctx, ep.db, dropSchemaSQL, "drop temporary schema"); err != nil {
return fmt.Errorf("failed to drop temporary schema %s: %w", ep.tempSchema, err)
}

// Create the temporary schema
createSchemaSQL := fmt.Sprintf("CREATE SCHEMA \"%s\"", ep.tempSchema)
if _, err := ep.db.ExecContext(ctx, createSchemaSQL); err != nil {
if _, err := util.ExecContextWithLogging(ctx, ep.db, createSchemaSQL, "create temporary schema"); err != nil {
return fmt.Errorf("failed to create temporary schema %s: %w", ep.tempSchema, err)
}

// Set search_path to the temporary schema
setSearchPathSQL := fmt.Sprintf("SET search_path TO \"%s\"", ep.tempSchema)
if _, err := ep.db.ExecContext(ctx, setSearchPathSQL); err != nil {
if _, err := util.ExecContextWithLogging(ctx, ep.db, setSearchPathSQL, "set search_path for desired state"); err != nil {
return fmt.Errorf("failed to set search_path: %w", err)
}

Expand All @@ -213,7 +213,7 @@ func (ep *EmbeddedPostgres) ApplySchema(ctx context.Context, schema string, sql
// Execute the SQL directly
// Note: Desired state SQL should never contain operations like CREATE INDEX CONCURRENTLY
// that cannot run in transactions. Those are migration details, not state declarations.
if _, err := ep.db.ExecContext(ctx, schemaAgnosticSQL); err != nil {
if _, err := util.ExecContextWithLogging(ctx, ep.db, schemaAgnosticSQL, "apply desired state SQL to temporary schema"); err != nil {
return fmt.Errorf("failed to apply schema SQL to temporary schema %s: %w", ep.tempSchema, err)
}

Expand Down
6 changes: 3 additions & 3 deletions internal/postgres/external.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,13 @@ func (ed *ExternalDatabase) ApplySchema(ctx context.Context, schema string, sql

// Create the temporary schema
createSchemaSQL := fmt.Sprintf("CREATE SCHEMA IF NOT EXISTS \"%s\"", ed.tempSchema)
if _, err := ed.db.ExecContext(ctx, createSchemaSQL); err != nil {
if _, err := util.ExecContextWithLogging(ctx, ed.db, createSchemaSQL, "create temporary schema"); err != nil {
return fmt.Errorf("failed to create temporary schema %s: %w", ed.tempSchema, err)
}

// Set search_path to the temporary schema
setSearchPathSQL := fmt.Sprintf("SET search_path TO \"%s\"", ed.tempSchema)
if _, err := ed.db.ExecContext(ctx, setSearchPathSQL); err != nil {
if _, err := util.ExecContextWithLogging(ctx, ed.db, setSearchPathSQL, "set search_path for desired state"); err != nil {
return fmt.Errorf("failed to set search_path: %w", err)
}

Expand All @@ -119,7 +119,7 @@ func (ed *ExternalDatabase) ApplySchema(ctx context.Context, schema string, sql
// Execute the SQL directly
// Note: Desired state SQL should never contain operations like CREATE INDEX CONCURRENTLY
// that cannot run in transactions. Those are migration details, not state declarations.
if _, err := ed.db.ExecContext(ctx, schemaAgnosticSQL); err != nil {
if _, err := util.ExecContextWithLogging(ctx, ed.db, schemaAgnosticSQL, "apply desired state SQL to temporary schema"); err != nil {
return fmt.Errorf("failed to apply schema SQL to temporary schema %s: %w", ed.tempSchema, err)
}

Expand Down
Loading