Skip to content

sandboxes.parent_snapshot_id has no foreign key, only an index #157

Description

@haasonsaas

Problem

sandboxes.parent_snapshot_id is a bare text column with no foreign-key constraint to snapshots.id. Referential integrity is enforced only by an index, not by the database. A forked sandbox row can point at a snapshot id that no longer exists (e.g. after out-of-band deletion), and nothing in the schema catches it.

Evidence

  • crates/sandboxwich-api/migrations/20260704000100_initial.sql:9parent_snapshot_id text with no references snapshots(id), unlike the sibling commands.sandbox_id column two lines earlier which does declare references sandboxes(id) on delete cascade.
  • crates/sandboxwich-api/migrations/20260704000500_snapshots.sql:20-21 — only create index if not exists idx_sandboxes_parent_snapshot_id on sandboxes(parent_snapshot_id); is added later; no constraint accompanies it.

Suggested approach

Add the foreign key in both backends:

  • SQLite: SQLite can't ALTER TABLE ... ADD CONSTRAINT, so this needs the standard rebuild pattern (create a new sandboxes table with the FK, copy rows, drop old table, rename) in a new migration.
  • PostgreSQL: a straightforward ALTER TABLE sandboxes ADD CONSTRAINT fk_sandboxes_parent_snapshot_id FOREIGN KEY (parent_snapshot_id) REFERENCES snapshots(id) ... with an explicit decision on ON DELETE behavior (likely SET NULL, since snapshot expiry/cleanup should not cascade-delete the sandbox that forked from it).

Both SQLite and PostgreSQL contract-test suites should assert the constraint is enforced.

Status

A PR addressing this is in progress today (parallel effort) — not yet numbered.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions