Skip to content

Commit 88a765a

Browse files
authored
fix(db): paren-insensitive constraint-drift comparison (#113)
Found by the fresh-project e2e's final target assertion: `pg_get_constraintdef` prints AND-chains left-associated on PG 17.4 (where the pinned definitions were dumped) but flattened on 17.6 (fresh Supabase projects today) — semantically identical, textually different, so the drift check false-fired. Also future-proofs production's own eventual minor upgrade. Both sides of the comparison are now parenthesis-stripped; operator/literal/regex weakening still surfaces, which is what the check exists to catch. ## Verification notes - [x] `pnpm lint` / `pnpm typecheck` / `pnpm turbo build --force` / `pnpm deadcode` / `pnpm architecture:check` - Post-merge: wizard resume must clear the target assertion on the fresh 17.6 project (migrations 0000-0003 already ledgered there) and proceed to provisioning + probes.
1 parent 7bd5524 commit 88a765a

1 file changed

Lines changed: 13 additions & 1 deletion

File tree

scripts/supabase-target/invariants.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,15 @@ export async function validateIntegrityConstraints(client: PgClient): Promise<st
181181
CANONICAL_PROVIDER_KEY_CONSTRAINT,
182182
]) {
183183
const row = result.rows.find((candidate) => candidate["conname"] === contract.name);
184-
if (row && normalizedSqlDefinition(row["definition"]) !== contract.definition) {
184+
// pg_get_constraintdef's AND-chain grouping changed between Postgres
185+
// minors (17.4 prints left-associated parentheses, 17.6 flattens), so the
186+
// comparison ignores parentheses. Operator, literal, and regex changes
187+
// still surface, which is what this drift check exists to catch.
188+
if (
189+
row &&
190+
parenInsensitiveSqlDefinition(row["definition"]) !==
191+
parenInsensitiveSqlDefinition(contract.definition)
192+
) {
185193
issues.push(`Security check public.${contract.tableName}.${contract.name} has drifted.`);
186194
}
187195
}
@@ -261,3 +269,7 @@ function stringField(row: Record<string, unknown>, key: string): string | undefi
261269
function normalizedSqlDefinition(value: unknown): string {
262270
return typeof value === "string" ? value.replaceAll(/\s+/g, " ").trim() : "";
263271
}
272+
273+
function parenInsensitiveSqlDefinition(value: unknown): string {
274+
return normalizedSqlDefinition(value).replaceAll(/[()]/g, "").replaceAll(/\s+/g, " ").trim();
275+
}

0 commit comments

Comments
 (0)