fix(migrations): auto-create required extensions, prevent dirty state#1442
Open
bclermont wants to merge 2 commits into
Open
fix(migrations): auto-create required extensions, prevent dirty state#1442bclermont wants to merge 2 commits into
bclermont wants to merge 2 commits into
Conversation
added 2 commits
July 15, 2026 13:10
…ation Move pgcrypto and pgvector extension creation out of the migration to a prerequisite step. These extensions require superuser privileges, and when goclaw connects with a least-privilege role, the migration fails midway and leaves schema_migrations.dirty=true, blocking all subsequent boots. Extensions must now be created by a database administrator before the first goclaw connection. This is documented in the migration header. Fixes: Migration fails with least-privilege goclaw role Resolves: schema_migrations.dirty blocking boot with no recovery path
PROBLEM ======= goclaw's initial migration required superuser to CREATE EXTENSION pgcrypto and pgvector. When goclaw connects with a least-privilege role, the migration fails midway, marking schema_migrations.dirty=true, which blocks all subsequent boots with no recovery path. SOLUTION ======== 1. Remove CREATE EXTENSION from migrations/000001_init_schema.up.sql (superuser requirement becomes explicit in header doc) 2. Add EnsureExtensions() function that: - Checks if pgcrypto and pgvector exist before migrations run - Automatically creates them if missing (works with privileged roles) - Returns clear DBA-actionable error if creation fails (permission denied) - Prevents dirty state by ensuring extensions upfront 3. Call EnsureExtensions() in 'migrate up' and 'upgrade' before running migrations BEHAVIOR ======== Before: Migration fails → dirty state → boot blocked forever After: Extensions checked → auto-created if possible → migration succeeds If auto-creation fails (insufficient privilege): Clear error message with SQL for DBA to run manually MIGRATION PATH ============== - Existing deployments: no change if extensions already created - Fresh deployments: extensions auto-created on first 'goclaw upgrade' - Test environments: extensions auto-created with test role (usually sufficient) Fixes schema_migrations.dirty blocker in least-privilege deployments
clark-cant
requested changes
Jul 17, 2026
clark-cant
left a comment
Contributor
There was a problem hiding this comment.
Review: fix(migrations) auto-create required extensions
Summary: Moves CREATE EXTENSION out of migration 000001 into a pre-flight EnsureExtensions() check to prevent dirty schema state when running as non-superuser.
Risk level: Medium — touches migration path, upgrade path, and CLI entry points.
Mandatory gates:
- Duplicate/prior implementation: clear — no prior PR addresses this exact migration-dirty-state path
- Project standards: inferred from codebase patterns
- Strategic necessity: clear value — prevents unrecoverable dirty state for least-privilege deployments
Findings
Critical
- go CI unit tests FAILING — run #29438786446 job go fails at "Unit tests" step. Must be green before merge.
Important
- cmd/migrate.go: redundant DB connection —
connectDB(dsn)already opensdbabove. This PR opens a secondsql.Open("pgx", dsn)just forEnsureExtensions, then closes it. Reuse the existingdbhandle instead of creating a second connection that leaks ifnewMigratorfails between the close and the open. - cmd/upgrade.go runUpgradeStatus: error swallowed —
fmt.Print(err)writes to stdout (not stderr) and thenreturn nilsilently discards the error. Thestatussubcommand will print an extension error as if everything is fine. Either return the error or print to stderr and set a non-zero exit indicator.
Suggestion
- cmd/upgrade.go runUpgrade: same
fmt.Print(err)pattern — considerfmt.Fprintln(os.Stderr, err)for consistency. - internal/upgrade/checker.go createExtension: extension names are hardcoded so no real injection risk, but consider using a constant slice instead of
fmt.Sprintffor the SQL to make the intent clearer.
Verdict: Request changes
Fix the CI failure and the redundant connection / error-swallowing issues, then this is a solid improvement.
Posted by github-maintain cron at 2026-07-17T13:30:00Z
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
goclaw's initial migration (
migrations/000001_init_schema.up.sql) executed:Both extensions require superuser privileges. When goclaw connects with a least-privilege database role (the recommended security posture), the migration fails midway and marks the schema as dirty (
schema_migrations.dirty = true). This blocks all subsequent boots with no automated recovery — the only workaround is manual intervention to clean the schema.Root Cause
PostgreSQL does not allow non-superuser roles to create extensions (unless explicitly granted CREATE privilege). Once a migration fails, the dirty flag prevents the app from starting again until the flag is manually cleared.
Solution
Move extension creation out of the migration into a pre-flight check that auto-creates them. This prevents the dirty state entirely.
Changes
migrations/000001_init_schema.up.sql
CREATE EXTENSIONstatementsinternal/upgrade/checker.go - Add EnsureExtensions()
cmd/migrate.go & cmd/upgrade.go
EnsureExtensions(db)before running migrationsBehavior
Error Message Example
If auto-creation fails due to insufficient privilege:
Migration Path
Existing deployments:
EnsureExtensionswill try to fix on next upgradeFresh deployments:
goclaw upgrade(if role has privilege)Test environments:
Testing