executor

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 13, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package executor applies migration SQL against the shadow database.

Per docs/DECISIONS.md the executor wraps the full migration in a single transaction by default, respects explicit BEGIN/COMMIT if present in the migration file, and stops on the first SQL error. Lock findings produced before the failure (from M3 onward) are preserved; post-migration plan capture (M4 onward) is skipped on a failed run.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func HasExplicitTransaction

func HasExplicitTransaction(stmts []string) bool

HasExplicitTransaction reports whether any statement in stmts is a transaction-control statement (BEGIN, START TRANSACTION, COMMIT, ROLLBACK, or END). When true, the executor MUST NOT wrap the migration in its own transaction — the file is already directing its own transaction boundaries and wrapping would produce nested-transaction errors.

The check uses the first SQL keyword of each statement after leading comments and whitespace have been stripped by SplitStatements. It is intentionally simple: the v1 rule is "any top-level tx-control statement makes the whole migration self-managed".

func SplitStatements

func SplitStatements(sql string) []string

SplitStatements splits a PostgreSQL script into individual statements. It is a small, purpose-built lexer — not a full SQL parser — that correctly handles the constructs that actually appear in real migration files and would break a naive "split on semicolon" approach:

  • single-line comments (-- ...)
  • block comments (/* ... */, including nested block comments)
  • single-quoted strings ('...' with ” as the embedded-quote escape)
  • double-quoted identifiers ("..." with "" escape)
  • dollar-quoted strings ($$...$$ and $tag$...$tag$, common in functions)

Semicolons inside any of the above are preserved as part of the current statement; only top-level semicolons terminate a statement. Empty statements are discarded. Returned statements have leading and trailing whitespace trimmed but are otherwise unchanged.

Types

type Result

type Result struct {
	// Statements is the per-statement log in execution order. On a
	// failed run the failing statement is the last entry.
	Statements []StatementResult

	// ExplicitTx is true when the migration file contained its own
	// transaction-control statements (BEGIN/COMMIT/etc.) and the
	// executor therefore did NOT wrap the migration in an implicit
	// transaction.
	ExplicitTx bool

	// Failed is true when the executor halted because a statement
	// returned an error or a commit/rollback failed. Downstream stages
	// use this flag to decide whether post-migration analysis runs.
	Failed bool

	// FailureIndex is the Index of the statement that caused the run to
	// halt. It is -1 when Failed is false, or when the failure happened
	// outside of statement execution (e.g. during commit).
	FailureIndex int

	// FailureErr carries the underlying error that caused the failure.
	// It is nil when Failed is false.
	FailureErr error

	// TotalDuration is the total wall-clock time from the start of
	// execution to the end (success or failure). It does not include
	// snapshot restore time.
	TotalDuration time.Duration
}

Result is the structured outcome of a single migration run.

Milestone 2 is the first milestone to populate a Result. Later milestones read it:

  • M3 (lock analyzer) attaches lock findings to whichever statements were executing when the locks were taken, using Statements[i].
  • M4 (plan regression analyzer) consults Failed to decide whether post-migration plan capture runs at all. Per DECISIONS.md it is skipped entirely when Failed is true.
  • M5 (report generator) surfaces the migration-failure finding in the "Migration Execution" section when Failed is true.

func Run

func Run(ctx context.Context, conn *pgx.Conn, migrationSQL string) (*Result, error)

Run executes the migration script against conn and returns a structured Result describing every statement's outcome.

By default the entire migration is wrapped in a single transaction. If the migration script contains its own BEGIN/COMMIT/ROLLBACK/END or START TRANSACTION statements, Run respects them instead of wrapping — this matches how Flyway and Alembic execute Postgres migrations and is the closed decision in docs/DECISIONS.md.

Run stops at the first statement error and returns a Result with Failed=true and FailureIndex / FailureErr populated. It does not return a Go error for SQL errors — those are product findings. A non-nil Go error return is reserved for unusual conditions like a failure to open the transaction or the context being cancelled before any statement runs.

func (*Result) OK

func (r *Result) OK() bool

OK reports whether the migration ran to completion.

type StatementResult

type StatementResult struct {
	// Index is the 0-based position of the statement in the original
	// migration script after splitting.
	Index int

	// SQL is the trimmed source text of the statement as it was
	// executed (without the trailing semicolon).
	SQL string

	// StartedAt is the wall-clock time immediately before Exec was
	// called for this statement. It is captured on every statement —
	// successful or failing — so the M3 lock analyzer can attribute
	// pg_locks samples to whichever statement window was executing
	// when each sample was taken.
	StartedAt time.Time

	// Duration is the wall-clock time spent in Exec for this statement.
	// It is captured even when the statement errored out.
	Duration time.Duration

	// Err is nil on success and non-nil on failure. The executor stops
	// after the first non-nil Err per docs/DECISIONS.md.
	Err error
}

StatementResult captures the outcome of a single migration statement. It is the atomic unit of per-statement timing that downstream M3+ lock analysis and M5+ reporting will consume.

func (StatementResult) EndAt

func (s StatementResult) EndAt() time.Time

EndAt reports the wall-clock time at which the statement finished executing. It is derived from StartedAt + Duration and is the right edge of the statement window used for lock-sample attribution.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL