fix: use dedicated connection in ApplySchema to preserve search_path#375
fix: use dedicated connection in ApplySchema to preserve search_path#375
Conversation
ApplySchema used separate ExecContext calls on *sql.DB (connection pool) for SET search_path and SQL execution. Go's database/sql does not guarantee the same connection across calls, so session-scoped settings like search_path could be lost, causing objects to be created in the wrong schema. Fix: acquire a single *sql.Conn from the pool and use it for all statements, guaranteeing SET search_path affects subsequent SQL. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Greptile SummaryThis PR fixes a real concurrency/session-state bug in Confidence Score: 5/5Safe to merge — the fix is minimal, targeted, and correctly addresses a real session-isolation bug with no regressions to the surrounding cleanup paths. The change is small (three files), logically sound, and covers both affected code paths (embedded and external) symmetrically. The interface generalisation in No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
participant Caller
participant ApplySchema
participant sql.DB (pool)
participant sql.Conn (dedicated)
participant PostgreSQL
Caller->>ApplySchema: ApplySchema(ctx, schema, sql)
ApplySchema->>sql.DB (pool): db.Conn(ctx)
sql.DB (pool)-->>ApplySchema: conn (*sql.Conn)
ApplySchema->>sql.Conn (dedicated): DROP SCHEMA IF EXISTS tempSchema CASCADE
sql.Conn (dedicated)->>PostgreSQL: execute
PostgreSQL-->>sql.Conn (dedicated): OK
ApplySchema->>sql.Conn (dedicated): CREATE SCHEMA tempSchema
sql.Conn (dedicated)->>PostgreSQL: execute
PostgreSQL-->>sql.Conn (dedicated): OK
ApplySchema->>sql.Conn (dedicated): SET search_path TO tempSchema, public
sql.Conn (dedicated)->>PostgreSQL: execute (session-scoped to this conn)
PostgreSQL-->>sql.Conn (dedicated): OK
ApplySchema->>sql.Conn (dedicated): [schema DDL — relies on search_path]
sql.Conn (dedicated)->>PostgreSQL: execute (same session, search_path active)
PostgreSQL-->>sql.Conn (dedicated): OK
ApplySchema->>sql.Conn (dedicated): conn.Close() (defer)
sql.Conn (dedicated)->>sql.DB (pool): return to pool
ApplySchema-->>Caller: nil
Reviews (1): Last reviewed commit: "fix: use dedicated connection in ApplySc..." | Re-trigger Greptile |
There was a problem hiding this comment.
Pull request overview
Ensures ApplySchema reliably applies desired-state SQL under the intended search_path by executing SET search_path and subsequent DDL on the same physical PostgreSQL connection, avoiding database/sql pool connection switching.
Changes:
- Updated embedded and external
ApplySchemaimplementations to use a dedicated*sql.Connfor all statements (includingSET search_path). - Generalized
ExecContextWithLoggingto accept anexecerinterface so it can execute via either*sql.DBor*sql.Conn.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| internal/postgres/external.go | Uses a dedicated *sql.Conn in ExternalDatabase.ApplySchema so search_path is preserved across statements. |
| internal/postgres/embedded.go | Uses a dedicated *sql.Conn in EmbeddedPostgres.ApplySchema so search_path is preserved across statements. |
| cmd/util/sql_logger.go | Refactors ExecContextWithLogging to accept an execer interface for compatibility with *sql.Conn. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
ApplySchema(both embedded and external) used separateExecContextcalls on*sql.DB(connection pool) forSET search_pathand SQL execution. Go'sdatabase/sqldoes not guarantee the same connection across calls, so session-scoped settings likesearch_pathcould silently be lost, causing objects to be created in the wrong schema.*sql.Connfrom the pool viadb.Conn(ctx)and use it for all statements, guaranteeingSET search_pathaffects subsequent SQL execution.ExecContextWithLoggingto accept anexecerinterface so it works with both*sql.DBand*sql.Conn.Test plan
TestPlanAndApply/create_table_add_column_cross_schema_custom_type— previously failing, now passesTestPlanAndApply/dependency_issue_300_function_table_composite_type— previously failing, now passesgo test ./...) passes🤖 Generated with Claude Code