From 87a9b321cd45f8f303ff6985d9ca13ee47fb30da Mon Sep 17 00:00:00 2001 From: "Charles (via Bookendi session)" Date: Sun, 30 Aug 2026 09:30:33 +1000 Subject: [PATCH] fix(database): audit_logs.timeline_sequence idempotent on re-run ADD COLUMN IF NOT EXISTS timeline_sequence bigserial throws 'relation audit_logs_timeline_sequence_seq already exists' if push-schema.js runs a second time against a database that already has the column -- the IF NOT EXISTS existence check runs after Postgres has already expanded bigserial into its implicit CREATE SEQUENCE during statement transformation, so the sequence creation is attempted regardless. Replaced with an explicit CREATE SEQUENCE IF NOT EXISTS + a plain bigint column with an explicit nextval() default, which is safe to run any number of times. --- packages/database/src/push-schema.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/database/src/push-schema.ts b/packages/database/src/push-schema.ts index ff49134..d58252f 100644 --- a/packages/database/src/push-schema.ts +++ b/packages/database/src/push-schema.ts @@ -1462,7 +1462,17 @@ export async function pushSchema(databaseUrl: string = DATABASE_URL) { `ALTER TABLE reservations ADD COLUMN IF NOT EXISTS accepted_pricing_snapshot jsonb`, `ALTER TABLE payments ADD COLUMN IF NOT EXISTS booking_request_id uuid`, `ALTER TABLE payments ADD COLUMN IF NOT EXISTS idempotency_key varchar(255)`, - `ALTER TABLE audit_logs ADD COLUMN IF NOT EXISTS timeline_sequence bigserial`, + // NOT `bigserial` -- Postgres expands that pseudo-type into a CREATE + // SEQUENCE during statement transformation, BEFORE the ADD COLUMN IF NOT + // EXISTS existence check runs, so re-running push-schema.js throws + // 'relation "audit_logs_timeline_sequence_seq" already exists' even + // though the column itself is a no-op. Found running push-schema.js + // twice against the same database (once via run-migrations.js, once + // standalone, in the same session) -- explicit sequence + bigint + // default is unambiguously idempotent either way. + `CREATE SEQUENCE IF NOT EXISTS audit_logs_timeline_sequence_seq`, + `ALTER TABLE audit_logs ADD COLUMN IF NOT EXISTS timeline_sequence bigint NOT NULL DEFAULT nextval('audit_logs_timeline_sequence_seq')`, + `ALTER SEQUENCE audit_logs_timeline_sequence_seq OWNED BY audit_logs.timeline_sequence`, `CREATE UNIQUE INDEX IF NOT EXISTS audit_logs_timeline_sequence_unique ON audit_logs (timeline_sequence)`, // Commercial profile billing fields + links (KB 14.3 standing accounts) `ALTER TABLE group_profiles ADD COLUMN IF NOT EXISTS billing_address text`,