Repository navigation
fix: locked-down tables stay locked, quoted names, views, --json, data fidelity - #124
Merged
Merged
Conversation
…efaults A table the source keeps away from the Data API (REVOKE ALL ... FROM anon, authenticated) and created on a Supabase target by the schema fix got everything the target's default privileges grant. Nothing took that back until a second sync, so one sync left it open to the anon key. The roles check now works out what the target's defaults will grant on each table it lacks, and revokes whatever the source does not grant. The revoke runs with the grants, after the table is created. An extra grant to anon, authenticated or PUBLIC is critical, and one to any other role a warning; both were info. Role, schema and table names in the generated SQL are now quoted with their own quotes doubled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A policy named say "hi" there was written between plain quotes, a
syntax error: a restore holding one rolled back entirely, and diff --apply
applied nothing from that run. Policy SQL is now built in one place for
the RLS, storage and Realtime checks and the snapshot, with the policy,
schema, table and every role in TO quoted. Webhook, RLS coverage,
extension, publication, grant and database names are quoted the same way.
A role list read as an array literal ({"Admins \"A\"",anon}) is parsed
properly, so such a role is not quoted twice, and titles read a name with
doubled quotes as one name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A changed view arrives from dbdiff as DROP VIEW + CREATE VIEW. Read by its DROP it was a critical 'Extra view' for a view both sides have. It is now one 'View modified' warning, materialized views alike, as triggers, policies and routines already are. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…'pending' Under --json every line a command prints besides its result now goes to stderr: migrate list and clone printed their preflight checks, and clone its transfer meter, on stdout before the JSON. Each command's JSON goes through one helper, and a clone preview prints a JSON result too. migrate list, connected as a role that cannot read the migration history, warned and then listed every migration as pending, exiting 0. It now shows the status as unknown (applied: null under --json) and exits 1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rows were captured as JSON values, so a json column came back re-rendered (pretty-printed, keys reordered, a duplicate key collapsed) and a jsonb holding JSON null came back as SQL NULL. Each column is now captured as PostgreSQL's own text for it, which its type reads back exactly, and restored with a cast per column. Snapshots in the earlier format still restore. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… reports - A comment on a new function folds into its 'Function missing' finding, matched by name when only one new routine has it: dbdiff spells the signature with argument types in the comment and with the parameter list in the create, so the two never matched. - A new table's ENABLE/FORCE ROW LEVEL SECURITY folds into its creation instead of being a 'Table altered' finding for a table not there yet. - A restore skip names only the missing extension or schema its error is about, and the snapshot groups a role's privileges on an object into one GRANT, so a skipped relation is one line per role, not one per privilege. - A restore that rolled back no longer also reports incomplete. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t on its server PostgreSQL prints the same objects differently from one major version to the next (15 table-qualifies a view's columns, 16+ does not), so a migrated target never matched a source on another version however right it was. Across major versions the runner now builds the source's SQL on the target's server and compares the target with that, as --prove does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ads them A policy name with a double quote in it is written with it doubled. Matched as text, the schema check's CREATE POLICY and the RLS check's for the same policy got different keys, so both were applied and the second failed with 'policy ... already exists'. Created and dropped policies and created triggers are now keyed from the parse tree, and the text fallback reads doubled quotes as part of one name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| import { BRANCHES_FILE, PG_PIPELINE_TIMEOUT_MS, CLONE_PROGRESS_INTERVAL_MS } from './constants' | ||
| import { CLONE_STUBS_SQL } from './stubs' | ||
| import { quoteIdent } from './utils/sql' | ||
| import { quoteIdent, quoteName } from './utils/sql' |
| * therefore readable and writable with the anon key until a second sync took | ||
| * the grants back. Both servers here carry the same defaults Supabase does. | ||
| */ | ||
| import { describe, it, expect, beforeAll, afterAll } from 'vitest' |
| * role that cannot read the history table, warned and then listed every | ||
| * applied migration as pending, exiting 0. | ||
| */ | ||
| import { describe, it, expect, beforeAll, afterAll } from 'vitest' |
| * error: the restore holding it rolled back entirely, and `diff --apply` | ||
| * applied nothing from that run, unrelated fixes included. | ||
| */ | ||
| import { describe, it, expect, beforeAll, afterAll } from 'vitest' |
dbdiff sets the comments of a view, function, policy or trigger it recreates again. Apart from the 'modified' finding they read as 'Comment changed' on a comment that had not. The grants e2e now covers a new locked-down view as well as tables. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scripts/test-release-candidates.sh builds DBDiff's PHAR and static binary from a checkout the way a release does, packs @dbdiff/cli, @dbdiff/cli-linux-x64 and pg-conformance, builds and packs SupaForge against them and installs it as a user would. It then runs the unit, integration, database e2e and scenario suites (twin servers and 17→15) against that installed package. SUPAFORGE_E2E_CLI points the harness at it. A fix spanning two repos can then be tested together before either is released. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Found by running the scenario suites against packed release candidates: - The RLS check's ENABLE/DISABLE ROW LEVEL SECURITY and the extensions check's CREATE, DROP and ALTER EXTENSION wrote names between plain quotes, and an extension version unescaped. - Under NODE_ENV=development or test, as in many projects running npx supaforge, the published package warned on every command that it could not find TypeScript. The compiled bin turns oclif's TypeScript lookup off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Grouping a role's privileges into one GRANT put PostgreSQL 17's MAINTAIN alongside the rest, so restoring a 17 snapshot into 15 failed on the whole statement and rolled back, where before only the MAINTAIN line was skipped. Found by the release smoke against the packed candidate. A new scenario restores a snapshot across every pair of servers the scenario suites run on, 17 → 15 included. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| * a 17 privilege: grouped into one GRANT with the others, the 15 target | ||
| * rejected the whole statement and the restore rolled back. | ||
| */ | ||
| import { describe, it, expect, beforeAll, afterAll } from 'vitest' |
… only Off everywhere, a checkout could no longer run its e2e suites before building dist, which CI does. The published package ships no src/, so that is what the bin checks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Fixes from retesting 0.0.29 against Supabase, plain PostgreSQL and a PostgreSQL 15/17 pair. Every fix has a test that fails on the previous code and passes with the fix.
High
A new table the source keeps away from the Data API stays that way after one sync.
REVOKE ALL … FROM anon, authenticated(RLS off) and created on a Supabase target got everything the target's default privileges grant. It stayed readable and writable with the anon key until a second sync.anon,authenticatedorPUBLICare now critical, and extra grants to any other role a warning. Both wereinfo.test/e2e/grants-on-new-tables.test.tsruns one sync against Supabase-style default privileges and checks the grants against the source's.Medium
Names holding a double quote.
say "hi" therewas written between plain quotes, a syntax error. A restore holding one rolled back entirely, anddiff --applyapplied nothing.TO.test/quoted-names.test.tsreads every builder's SQL back with PostgreSQL's parser.test/e2e/quoted-names.test.tscovers a sync and a restore.CREATE POLICY) and is covered by the pg-conformance casequoted_identifiers(feat(corpus): names holding double quotes pg-conformance#16) once both are released and bumped.A view dropped and recreated is one "View modified" warning, not a critical "Extra view". Materialized views are handled the same way.
--provedoes.known-gaps.jsonin the bump.Low
--jsonis JSON alone:migrate listandcloneused to print their preflight checks on stdout;migrate listwithout read access to the history now shows the status as unknown (applied: null) and exits 1, instead of calling every migration pending.jsonkeeps its spacing and duplicate keys, and a JSONnullstays distinct from SQLNULL;ENABLE ROW LEVEL SECURITYfolds into "Table missing".incomplete.Also
ENABLE/DISABLE ROW LEVEL SECURITYand the extensions check's SQL quote names properly.NODE_ENV=developmentortest, as in many projects runningnpx supaforge, the published package printed oclif's "Could not find typescript" warning on every command. The compiled bin now turns that lookup off. The release dry run installs the packed package and fails on any output to stderr.scripts/test-release-candidates.shbuilds DBDiff's PHAR and static binary from a checkout as a release does, packs@dbdiff/cli,@dbdiff/cli-linux-x64and pg-conformance, and installs SupaForge against them as a user would. It then runs every suite against that install.SUPAFORGE_E2E_CLIpoints the harness at the installed CLI. Building from DBDiff needs fix(build): build-local.sh copies the PHAR out of its build copy DBDiff/DBDiff#255, a fix to itsbuild-local.sh.Testing
Lint is clean.
🤖 Generated with Claude Code