Nykst

SQL and migrations

How SQL column and migration impact works

What Nykst can prove about a SQL change from migrations and literal client calls, what it cannot prove, and how to review the result before running it.

Reviewed 31 October 2026Nykst example repository: flawsom/nykst

Nykst reads code, not your database

The SQL view is a static reading of two things: the DDL in migration files, and the literal query calls in application code. It never connects to a database, never reads live rows, and never runs a migration. That limit is the point. An impact proof should be inspectable before anyone touches production, and it should fail closed when a query is built from a runtime string.

The parser collects column references in src/lib/analysis/parse.server.ts:284 and appends them to the file result at line 644. It caps the stored list at 500 entries on line 714. The stated limitation is in src/lib/analysis/engine.server.ts:540: drift is computed from literal calls and DDL, and dynamic table or column names are not guessed. A missing result is not proof that the column is unused. It is proof that the scanner did not see a literal use.

A real migration in the example repository

The public Nykst repository contains supabase/migrations/20261030130000_razorpay_subscriptions.sql. Line 6 starts an alter table that adds seven columns without dropping or rewriting existing ones. The additions include provider, trial_ends_at, checkout_started_at and cancel_at_period_end. Line 17 creates a unique index on one of the new identifiers, guarded by a partial predicate so existing rows with a null value remain valid.

The consolidated supabase/schema.sql repeats the same columns at lines 186 to 189 and the same index at lines 193 to 195. That duplication is deliberate: a fresh database can be built from one idempotent file, while an existing database applies the timestamped migrations in order. The header at supabase/schema.sql:3 states that it is every migration merged in order. If you review a column change, review both paths. A migration that is correct in isolation can still be missing from the consolidated schema.

Add, rename, drop: three different proofs

An additive column is the safest case. Nykst can list the writers and readers that already name the table, but it cannot tell you that an old application version will ignore the new column. Check the deploy order and the rollback path yourself.

A rename is a sequence, not a single edit. Add the new column, write to both columns, backfill, move readers, then remove the old column in a later migration. Nykst can show the files that mention the old column and the files that mention the new one. It cannot run the backfill or tell you whether every writer was deployed. The proof gives you the call sites to inspect; the deployment plan is still yours.

A drop is the case where a missing reader matters most. If the caller is assembled from a variable, the static graph will not show it. Treat an empty dependent list as a prompt to search the runtime path, not as permission to delete. This is why the guide recommends a two-step removal even when the map looks clean.

Migration order is part of the proof

Migration files are applied by their timestamp prefix. The consolidated schema is built from the same order, so a column added in a later file cannot be used by an earlier one. When you read a SQL impact result, write down the file order and the first file that introduces the column. If a query in an earlier migration needs the column, the migration itself is wrong, regardless of what the graph says.

The example migration also calls notify pgrst, 'reload schema' at line 22. That is a real operational step: PostgREST caches the schema, so a column can exist in the database and still be invisible to the API until the cache reloads. A static map cannot observe that delay. It is a reminder that "the migration ran" and "the API serves the new column" are two different checks.

What not to claim

  • Do not claim a change is safe because Nykst found no callers.
  • Do not claim the production database matches the migrations. Nykst did not connect to it.
  • Do not claim row level security, grants or policies were verified. Read them in the migration and test them with the real role.
  • Do not claim a backfill is complete. The proof can name the writers, not the rows.

Open the system map and use the data section on a file to see the columns it reads. If a result is empty, read the limitation above before you treat it as a clean bill of health.

Every example links to the file it comes from. If a claim here cannot be checked in the repository, treat it as a bug and tell us.