fix: ADR-0035 4g — reconstruct table-CHECK metadata on rebuild

do_rebuild_from_text re-emitted table-level CHECKs into the recreated
DDL (so they stayed enforced) but never repopulated __rdbms_playground_
table_checks. A fresh rebuild (missing .db, reconstructed from
project.yaml) therefore left the CHECK metadata empty: DROP CONSTRAINT,
describe, and a later save would lose it — including a named CHECK's
name. In-place rebuilds only worked because the wipe never touched the
table. (Latent since 4a.3 for unnamed checks; exposed by 4g's named
round-trip claim.)

Rebuild now wipes and repopulates CHECK_TABLE from the yaml snapshot
(name + seq + expr), like META/REL, and adds the 4g `name` column if a
pre-4g table predates it (the rebuild-only migration). Regression test:
a named CHECK's metadata survives a fresh rebuild (DROP CONSTRAINT by
name resolves).
This commit is contained in:
claude@clouddev1
2026-05-25 22:16:26 +00:00
parent 6ff97f6e20
commit 50a889e599
2 changed files with 96 additions and 0 deletions
+40
View File
@@ -8643,6 +8643,23 @@ fn do_rebuild_from_text(
))
.map_err(DbError::from_rusqlite)?;
// 0b. The table-level CHECK metadata is the source of truth that
// the engine cannot report (ADR-0035 §4a.3), so — like
// META/REL — it is wiped and repopulated from the YAML
// snapshot here (step 3b). Without this a fresh rebuild
// (missing `.db`) would enforce the CHECK via the recreated
// DDL but leave `CHECK_TABLE` empty, so `describe` / `DROP
// CONSTRAINT` / a later save would lose it. The rebuild also
// **migrates** a pre-§4g table that predates the `name`
// column (the rebuild-only migration, ADR-0035 §4g): add it
// if absent before repopulating with names.
if !check_table_has_name_column(&tx)? {
tx.execute_batch(&format!("ALTER TABLE {CHECK_TABLE} ADD COLUMN name TEXT;"))
.map_err(DbError::from_rusqlite)?;
}
tx.execute_batch(&format!("DELETE FROM {CHECK_TABLE};"))
.map_err(DbError::from_rusqlite)?;
// 1. Recreate user tables with FK constraints inline.
for table in &snapshot.tables {
let read_schema = build_read_schema(table, &snapshot.relationships);
@@ -8694,6 +8711,29 @@ fn do_rebuild_from_text(
}
}
// 3b. Table-level CHECK metadata (ADR-0035 §4a.3 / §4g) — in
// declaration order (`seq`), carrying the optional name so a
// named CHECK round-trips through the rebuild.
{
let mut stmt = tx
.prepare(&format!(
"INSERT INTO {CHECK_TABLE} (table_name, seq, check_expr, name) \
VALUES (?1, ?2, ?3, ?4);"
))
.map_err(DbError::from_rusqlite)?;
for table in &snapshot.tables {
for (seq, check) in table.check_constraints.iter().enumerate() {
stmt.execute(rusqlite::params![
table.name,
seq as i64,
check.expr,
check.name,
])
.map_err(DbError::from_rusqlite)?;
}
}
}
// 4. Project metadata: overwrite the configure-time
// `created_at` with the YAML's authoritative value.
tx.execute(