ReviewOS

stacks/bench-review

db: 25 of 33 declared foreign keys are missing from the live schema

#86
Closed glennmichael123 opened this 9 days ago ·
9 days ago

Narrower and more current than the FK bullet in #52, and with a hazard that one does not mention.

Problem

./buddy doctor:

✗ Database FKs   25/33 declared FKs missing from live schema:
                 review_photos.judge_review_id → judge_reviews.id,
                 review_photos.user_id → users.id,
                 judges.court_house_id → court_houses.id,
                 social_posts.user_id → users.id,
                 email_list_subscribers.email_list_id → email_lists.id
                 (+20 more). Run `buddy migrate:fresh` (will reset data) or
                 `buddy migrate` against a clean DB.

The models declare 33 foreign keys; 25 are absent from the live SQLite schema. Note judges.court_house_id → court_houses.id is in that list — the single most load-bearing relationship in the app, across 2,740 judges and 202 courthouses.

One mitigating fact: PRAGMA foreign_key_check is clean, so there are no orphan rows today. The constraints are missing, not violated.

Why the suggested remedies are not available

  • buddy migrate:fresh resets data. The database holds 2,740 judges and 202 courthouses ingested from CourtListener + Wikidata. Not an option.
  • "buddy migrate against a clean DB" is the same thing by another route.

So this needs per-table rebuilds (SQLite cannot add a FK to an existing table), which is exactly what the framework's own model-migration phase attempts.

The hazard, learned the hard way

A rebuild applies every current model constraint, not just the FK. That already bit once this week: the generated court_houses rebuild carried "image" TEXT not null while all 202 rows held NULL, and failed with

NOT NULL constraint failed: _qb_tmp_court_houses.image

It rolled back cleanly, and the fix was to correct the model (CourtHouse.image is now optional) rather than the data — the live table had always allowed NULL. Expect more of these: each table's rebuild needs its model checked against the data it already holds before it will apply.

Secondary finding, same root cause

EmailSubscription declares useUuid: true, but the email_subscriptions table has no uuid column at all. It was skipped when the 30 missing UNIQUE indexes were added, because an index cannot be created on a column that does not exist. This one needs a column, not an index.

What to do

  • Enumerate the 25 from ./buddy doctor (it truncates at 5)
  • For each table, diff its model constraints against the rows it holds — identify the image-shaped mismatches before running anything
  • Fix the model where the model is wrong; migrate the data where the data is wrong. Decide per table, do not assume the model is right
  • Rebuild per table, one migration at a time, so a failure names one table
  • Add the missing email_subscriptions.uuid column
  • PRAGMA foreign_key_check clean afterwards, and ./buddy doctor FK check green

Already done in this area: 30 missing UNIQUE indexes added, 59 dead .ts migrations deleted, 33 orphaned scaffold tables dropped.

Sign in to comment on this issue.