Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ on drift, so CI catches a stale snapshot. `squirrel db schema` prints the DDL
of a database directly (opening it runs migrations first), for inspecting a
real index without a repo checkout.

Every **new** `CREATE TABLE` — whether in a migration or a future fresh
Every `CREATE TABLE` — whether in a migration or a future fresh
baseline — MUST be declared `STRICT` (`CREATE TABLE … (…) STRICT`). STRICT
rejects any value that can't be losslessly converted to the column's declared
type instead of silently coercing it via type affinity, so a non-numeric string
Expand All @@ -70,11 +70,15 @@ This is exactly the discipline the schema already follows — ns-integer
timestamps (`…_ns INTEGER`), `INTEGER … CHECK (x IN (0, 1))` booleans, and
fixed-length `BLOB` hashes (`CHECK (length(h) = 32)`).

The ~13 tables that predate this convention are **not** STRICT yet: STRICT
can't be added with `ALTER`, so each needs a full rebuild (create STRICT copy
→ `INSERT … SELECT` → drop → rename) recreating every index and trigger. That
bulk conversion is a dedicated, well-tested migration PR of its own — don't
ride it along with a feature, and don't convert tables ad hoc.
Every table is STRICT as of **v27**: the eighteen that predated the
convention were converted in bulk by `store/migrate_v27.go` (STRICT can't be
added with `ALTER`, so each was rebuilt — create STRICT copy → `INSERT …
SELECT` → drop → rename → recreate every index and trigger, all in one
transaction with foreign keys off and a `foreign_key_check` before commit).
`TestSchemaIsAllStrict` fails if any table at `SchemaVersion` is not STRICT,
so the convention is enforced, not just documented. Rebuilding a table for
some other reason must carry the keyword forward — dropping it is a
regression, not a neutral rewrite.

# Code quality

Expand Down
12 changes: 8 additions & 4 deletions cmd/squirrel/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,15 +243,19 @@ func TestCLIDBRestoreRejectsSchemaMismatch(t *testing.T) {

// TestCLIDBSchemaPrintsDDL confirms `squirrel db schema` dumps the
// opened database's DDL, including the invariants the schema enforces:
// the foundational volumes table, the content entity table, and the
// one-live-row-per-path partial unique index.
// the foundational volumes table, the content entity table, the
// one-live-row-per-path partial unique index, and STRICT typing. The
// table names come back quoted because the v27 STRICT conversion
// rebuilt them under a scratch name and renamed them into place —
// SQLite quotes the substituted name in the stored DDL.
func TestCLIDBSchemaPrintsDDL(t *testing.T) {
f := writeSyncFixture(t)
out := runCLI(t, "--config", f.configPath, "db", "schema")
for _, want := range []string{
"CREATE TABLE volumes",
"CREATE TABLE contents",
`CREATE TABLE "volumes"`,
`CREATE TABLE "contents"`,
"uniq_files_live_per_path",
") STRICT",
} {
if !strings.Contains(out, want) {
t.Fatalf("db schema output missing %q:\n%s", want, out)
Expand Down
Loading
Loading