Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
30ba92c
fix(cli): wrap pipelined migration batches in an explicit transaction…
7ttp Aug 26, 2026
7a71865
fix: run transaction-prohibited ddl standalone
7ttp Aug 27, 2026
57f3ebb
docs: align push side effects with transactional batches
7ttp Aug 27, 2026
ce99a40
test: assert batch errors via instanceof
7ttp Aug 27, 2026
32335b4
fix: route subscription ddl standalone
7ttp Aug 27, 2026
5afa6f7
fix: roll failed batches back while interruptible
7ttp Aug 27, 2026
8335bff
fix: run discard all standalone with role restore
7ttp Aug 27, 2026
7f55c99
fix: route reindex schema standalone
7ttp Aug 27, 2026
35acc1b
fix: route alter database set tablespace standalone
7ttp Aug 27, 2026
605f369
fix: route subscription refresh forms standalone
7ttp Aug 27, 2026
a6bd732
docs: align migration up standalone list
7ttp Aug 27, 2026
9aece2f
fix: allocate batch state per execution
7ttp Aug 27, 2026
9cea8df
fix: address batch review nits
7ttp Aug 29, 2026
f2f254d
fix: tighten detach partition matching
7ttp Aug 29, 2026
0a7172a
fix: route all-in-tablespace moves standalone
7ttp Aug 29, 2026
5b85ecc
fix: match qualified quoted detach names
7ttp Aug 29, 2026
eae412a
fix: treat database-dropped as session-ending
7ttp Aug 29, 2026
1ad16ee
fix: keep pipeline classifiers loose and uniform
7ttp Aug 29, 2026
b1b3405
fix: name commit failures in batch errors
7ttp Aug 29, 2026
79cf215
fix: polish batch classifier and rollback nits
7ttp Aug 29, 2026
d582b79
fix: report commit failures without a statement tail
7ttp Aug 29, 2026
3e9d1e5
fix: omit statement context for begin failures
7ttp Aug 29, 2026
c838da3
fix: match parenthesized reindex concurrently
7ttp Aug 29, 2026
02db922
fix: keep detach partition finalize batched
7ttp Aug 29, 2026
7ddeddc
docs: note wrapper failures in reset contract
7ttp Aug 29, 2026
abf4aff
fix: type transaction phase without assertions
7ttp Aug 29, 2026
f83f672
docs: note roles batch splits in push contract
7ttp Aug 29, 2026
d3beb8d
fix: mark wrapper phase on connection loss
7ttp Aug 30, 2026
35fc933
fix: route refresh materialized view concurrently standalone
7ttp Aug 30, 2026
b0f6cba
test: assert connection-loss error via instanceof
7ttp Aug 30, 2026
221515c
docs: note standalone concurrent matview refresh in contracts
7ttp Aug 30, 2026
3224cb5
chore: fix test import formatting
7ttp Aug 30, 2026
a270457
Merge branch 'develop' into 7ttp/cli-2261-db-reset-pipelined-migratio…
7ttp Aug 31, 2026
4917f5f
fix: keep rollback failures in the effect boundary
7ttp Aug 31, 2026
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
28 changes: 18 additions & 10 deletions apps/cli/src/legacy/commands/db/push/SIDE_EFFECTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ before migrations unless `--skip-vault` is set.

## Database Mutations

| Statement | When |
| ------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `RESET ALL` + migration statements + `INSERT INTO supabase_migrations.schema_migrations(version, name, statements)` | per pending migration (after confirmation); compatible statements use an implicit extended-protocol batch with one final `Sync`, while pipeline-incompatible statements run standalone — see Notes |
| `CREATE SCHEMA/TABLE … supabase_migrations.schema_migrations`, `ALTER TABLE … ADD COLUMN …` | once before applying migrations (idempotent) |
| `roles.sql` statements (no history row) | per `--include-roles` globals file (after confirmation); statements use an implicit extended-protocol batch with one final `Sync` |
| `SELECT id, name FROM vault.secrets …`, `SELECT vault.update_secret(...)`, `SELECT vault.create_secret(...)` | when `[db.vault]` has syncable secrets, migrations are applied, and `--skip-vault` is not set |
| `CREATE TABLE … supabase_migrations.seed_files`, seed statements, `INSERT … seed_files(path, hash) … ON CONFLICT …` | per pending seed file with `--include-seed` (after confirmation); a dirty seed only refreshes the hash |
| `SET SESSION ROLE postgres` | stepped-down sessions only (`cli_login_*`/`supabase_admin`): after each top-level role-reverting statement, at the end of each migration/globals/seed file, and before the history insert and the `seed_files` upsert (CLI-2205, #6236) |
| Statement | When |
| ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `RESET ALL` + migration statements + `INSERT INTO supabase_migrations.schema_migrations(version, name, statements)` | per pending migration (after confirmation); compatible statements use one explicitly transactional extended-protocol batch (`BEGIN` … `COMMIT`) with one final `Sync`, while pipeline-incompatible statements run standalone — see Notes |
| `CREATE SCHEMA/TABLE … supabase_migrations.schema_migrations`, `ALTER TABLE … ADD COLUMN …` | once before applying migrations (idempotent) |
| `roles.sql` statements (no history row) | per `--include-roles` globals file (after confirmation); compatible statements use one explicitly transactional extended-protocol batch (`BEGIN` … `COMMIT`) with one final `Sync`, with the same standalone/sequential exceptions as migrations — see Notes |
| `SELECT id, name FROM vault.secrets …`, `SELECT vault.update_secret(...)`, `SELECT vault.create_secret(...)` | when `[db.vault]` has syncable secrets, migrations are applied, and `--skip-vault` is not set |
| `CREATE TABLE … supabase_migrations.seed_files`, seed statements, `INSERT … seed_files(path, hash) … ON CONFLICT …` | per pending seed file with `--include-seed` (after confirmation); a dirty seed only refreshes the hash |
| `SET SESSION ROLE postgres` | stepped-down sessions only (`cli_login_*`/`supabase_admin`): after each top-level role-reverting statement, at the end of each migration/globals/seed file, and before the history insert and the `seed_files` upsert (CLI-2205, #6236) |

## API Routes

Expand Down Expand Up @@ -118,11 +118,19 @@ stdout is payload-only. A single `result` object is emitted:
load, including decrypted `encrypted:` values. `--skip-vault` leaves them unchanged
and does not resolve or decrypt their configured values.
- **Pipeline-incompatible statements**: `CREATE [UNIQUE] INDEX CONCURRENTLY`,
`REINDEX … CONCURRENTLY`, `VACUUM`, `ALTER SYSTEM`, and `CLUSTER` cannot run inside a
`DROP INDEX CONCURRENTLY`, `REINDEX … CONCURRENTLY`, `VACUUM`, `ALTER SYSTEM`, `CLUSTER`,
`CREATE`/`DROP DATABASE`, `CREATE`/`DROP TABLESPACE`, `REINDEX DATABASE`/`SYSTEM`/`SCHEMA`,
`CREATE`/`DROP SUBSCRIPTION`, `DISCARD ALL`, `ALTER DATABASE … SET TABLESPACE`, and
`ALTER SUBSCRIPTION … REFRESH`/`SET`/`ADD`/`DROP PUBLICATION`,
`ALTER TABLE … DETACH PARTITION … CONCURRENTLY`,
`ALTER TABLE`/`INDEX`/`MATERIALIZED VIEW ALL IN TABLESPACE`, and
`REFRESH MATERIALIZED VIEW CONCURRENTLY`
cannot run inside a
transaction block (SQLSTATE 25001). The apply flushes (commits) the open batch, runs
the statement standalone outside any transaction, then resumes batching; the history
insert stays in the final batch so the migration is recorded only after every
statement succeeds. Atomicity is therefore lost at each flush boundary: statements
statement succeeds. A failed batch's transaction is rolled back (bounded) before its
connection is reused; a rollback that fails or times out discards the connection. Atomicity is therefore lost at each flush boundary: statements
committed in an earlier batch are **not** rolled back if a later statement fails,
leaving the database partially migrated with **no history row** — a re-run replays
the whole file from the top (which may then fail on already-applied statements).
Expand Down
5 changes: 4 additions & 1 deletion apps/cli/src/legacy/commands/db/reset/SIDE_EFFECTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,10 @@ reported as a lost connection (with the driver's own reason and, locally, the hi
stack) rather than against a statement that never ran. Once any part of the batch has been written,
and for the pipeline-incompatible statements the same loop runs on their own (`CREATE INDEX
CONCURRENTLY`, `VACUUM`, ...), a failure still reports as `At statement: N` with the statement
echoed, because those may genuinely have reached the server.
echoed, because those may genuinely have reached the server. The one exception is a failure of the
batch's own transaction wrapper — a rejected `BEGIN`, or a deferred constraint surfacing at
`COMMIT` — which reports the phase-labeled driver message with no statement context, since no
caller statement is to blame.

## Exit Codes

Expand Down
12 changes: 10 additions & 2 deletions apps/cli/src/legacy/commands/migration/up/SIDE_EFFECTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,18 @@ Same structured `applied` result delivered as an NDJSON `result` event.
a non-linked target).
- `--include-all` applies all migrations not found on the remote history table.
- Pipeline-incompatible statements (`CREATE [UNIQUE] INDEX CONCURRENTLY`,
`REINDEX … CONCURRENTLY`, `VACUUM`, `ALTER SYSTEM`, `CLUSTER`) run standalone outside
`DROP INDEX CONCURRENTLY`, `REINDEX … CONCURRENTLY`, `VACUUM`, `ALTER SYSTEM`,
`CLUSTER`, `CREATE`/`DROP DATABASE`, `CREATE`/`DROP TABLESPACE`,
`REINDEX DATABASE`/`SYSTEM`/`SCHEMA`, `CREATE`/`DROP SUBSCRIPTION`, `DISCARD ALL`,
`ALTER DATABASE … SET TABLESPACE`,
`ALTER SUBSCRIPTION … REFRESH`/`SET`/`ADD`/`DROP PUBLICATION`,
`ALTER TABLE … DETACH PARTITION … CONCURRENTLY`,
`ALTER TABLE`/`INDEX`/`MATERIALIZED VIEW ALL IN TABLESPACE`, and
`REFRESH MATERIALIZED VIEW CONCURRENTLY`) run standalone outside
the migration's transaction batch — they fail with SQLSTATE 25001 inside one. The
history insert stays in the final batch, so a mid-file failure leaves earlier,
already-committed batches applied with **no history row**; a re-run replays the file
from the top. Prefer idempotent forms (`… IF NOT EXISTS`) for such statements.
from the top. A failed batch's transaction is rolled back (bounded) before its
connection is reused. Prefer idempotent forms (`… IF NOT EXISTS`) for such statements.
Intentional fix for supabase/cli#5139, adopted into TS in PR supabase/cli#5671
(landed on develop as `b48fad60`).
6 changes: 6 additions & 0 deletions apps/cli/src/legacy/shared/legacy-db-connection.errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ export class LegacyDbExecError extends Data.TaggedError("LegacyDbExecError")<{
* the batch length for a deferred Sync failure. Absent for `exec`/`query`.
*/
readonly statementIndex?: number;
/**
* Which CLI-injected transaction wrapper was in flight when the batch failed —
* whether the server rejected BEGIN/COMMIT or the connection was lost while one
* was pending — rather than a caller statement. Absent otherwise.
*/
readonly transactionPhase?: "begin" | "commit";
/**
* Postgres SQLSTATE (e.g. `42P01` undefined_table), extracted from the driver
* error's `cause` chain when present. Lets callers match Go's error-code checks
Expand Down
9 changes: 6 additions & 3 deletions apps/cli/src/legacy/shared/legacy-db-connection.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,12 @@ export interface LegacyDbSession {
/** Run a single SQL statement, ignoring any returned rows. */
readonly exec: (sql: string) => Effect.Effect<void, LegacyDbExecError>;
/**
* Run statements as one extended-protocol batch with a single final Sync.
* On failure, {@link LegacyDbExecError.statementIndex} is the number of
* statements that completed before the error.
* Run statements as one extended-protocol batch inside a single explicit
* transaction, with a single final Sync — a bare pipeline is not a transaction
* block (supabase/cli#6347). On failure a bounded, best-effort rollback runs
* before the connection can be reused (a rollback that does not succeed
* discards the connection), and {@link LegacyDbExecError.statementIndex} is
* the number of the caller's statements that completed before the error.
*
* A batch runs on its own pooled connection, which the driver checks out per
* call. Failing to acquire it, or losing it before any of the batch reaches the
Expand Down
Loading
Loading