Skip to content

Commit 454c529

Browse files
fix(codegen): add self-FK on simulation_branches + four enum CHECKs (#79)
Closes #43. The overlay DDL had no structural enforcement of: - `simulation_branches.parent_branch` referencing `branch_id` (self-referencing tree of branches) - the four enum-shaped TEXT columns: * `simulation_branches.status` ∈ {active, merged, abandoned} * `provenance_log.operation` ∈ {insert, update, delete, transform} * `access_policies.access_level` ∈ {read, write, admin, deny} * `lineage_graph.derivation_type` ∈ {copy, transform, aggregate, join, filter} Anything could be written to those columns; downstream consumers had to re-validate every row. Add the FK (NULL still allowed for root branches) and the four CHECK constraints. Test `test_overlay_has_enum_checks_and_fk` asserts each by substring against the generated DDL. `cargo clippy --all-targets -- -D warnings` clean; 36 unit tests pass. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent edbfa90 commit 454c529

1 file changed

Lines changed: 51 additions & 5 deletions

File tree

src/codegen/overlay.rs

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ fn generate_provenance_table() -> String {
126126
\x20 actor TEXT NOT NULL,\n\
127127
\x20 timestamp TEXT NOT NULL, -- ISO 8601\n\
128128
\x20 before_snapshot TEXT, -- JSON of entity state before operation\n\
129-
\x20 transformation TEXT -- description of transformation applied\n\
129+
\x20 transformation TEXT, -- description of transformation applied\n\
130+
\x20 CHECK (operation IN ('insert','update','delete','transform'))\n\
130131
);\n\
131132
CREATE INDEX IF NOT EXISTS idx_provenance_entity ON verisimdb_provenance_log(entity_id);\n\
132133
CREATE INDEX IF NOT EXISTS idx_provenance_table ON verisimdb_provenance_log(table_name);\n\n"
@@ -153,7 +154,8 @@ fn generate_lineage_table() -> String {
153154
\x20 derivation_type TEXT NOT NULL, -- copy, transform, aggregate, join, filter\n\
154155
\x20 description TEXT,\n\
155156
\x20 created_at TEXT NOT NULL, -- ISO 8601\n\
156-
\x20 CHECK (source_entity <> target_entity OR source_table <> target_table)\n\
157+
\x20 CHECK (source_entity <> target_entity OR source_table <> target_table),\n\
158+
\x20 CHECK (derivation_type IN ('copy','transform','aggregate','join','filter'))\n\
157159
);\n\
158160
CREATE INDEX IF NOT EXISTS idx_lineage_source ON verisimdb_lineage_graph(source_entity);\n\
159161
CREATE INDEX IF NOT EXISTS idx_lineage_target ON verisimdb_lineage_graph(target_entity);\n\n"
@@ -196,7 +198,8 @@ fn generate_access_policy_table() -> String {
196198
\x20 access_level TEXT NOT NULL, -- read, write, admin, deny\n\
197199
\x20 condition TEXT, -- SQL-like filter condition\n\
198200
\x20 created_at TEXT NOT NULL, -- ISO 8601\n\
199-
\x20 active INTEGER NOT NULL DEFAULT 1\n\
201+
\x20 active INTEGER NOT NULL DEFAULT 1,\n\
202+
\x20 CHECK (access_level IN ('read','write','admin','deny'))\n\
200203
);\n\
201204
CREATE INDEX IF NOT EXISTS idx_access_table ON verisimdb_access_policies(target_table);\n\
202205
CREATE INDEX IF NOT EXISTS idx_access_principal ON verisimdb_access_policies(principal);\n\n"
@@ -211,12 +214,13 @@ fn generate_simulation_table() -> String {
211214
"-- Simulation: what-if branching and sandbox queries\n\
212215
CREATE TABLE IF NOT EXISTS verisimdb_simulation_branches (\n\
213216
\x20 branch_id TEXT PRIMARY KEY,\n\
214-
\x20 parent_branch TEXT, -- NULL for root branch\n\
217+
\x20 parent_branch TEXT REFERENCES verisimdb_simulation_branches(branch_id), -- NULL for root\n\
215218
\x20 name TEXT NOT NULL,\n\
216219
\x20 description TEXT,\n\
217220
\x20 created_at TEXT NOT NULL, -- ISO 8601\n\
218221
\x20 merged_at TEXT, -- ISO 8601, NULL if not merged\n\
219-
\x20 status TEXT NOT NULL DEFAULT 'active' -- active, merged, abandoned\n\
222+
\x20 status TEXT NOT NULL DEFAULT 'active', -- active, merged, abandoned\n\
223+
\x20 CHECK (status IN ('active','merged','abandoned'))\n\
220224
);\n\n\
221225
CREATE TABLE IF NOT EXISTS verisimdb_simulation_deltas (\n\
222226
\x20 delta_id TEXT PRIMARY KEY,\n\
@@ -285,6 +289,48 @@ mod tests {
285289
assert!(ddl.contains("verisimdb_simulation_branches"));
286290
}
287291

292+
/// All four enum-shape columns must be CHECK-constrained, and
293+
/// simulation_branches.parent_branch must be a self-referencing FK
294+
/// (closes #43).
295+
#[test]
296+
fn test_overlay_has_enum_checks_and_fk() {
297+
let schema = test_schema();
298+
let octad = OctadConfig {
299+
enable_provenance: true,
300+
enable_lineage: true,
301+
enable_temporal: true,
302+
enable_access_control: true,
303+
enable_constraints: true,
304+
enable_simulation: true,
305+
};
306+
let ddl = generate_sidecar_schema(&schema, &octad);
307+
308+
// Self-referencing FK on parent_branch.
309+
assert!(
310+
ddl.contains("parent_branch TEXT REFERENCES verisimdb_simulation_branches(branch_id)"),
311+
"simulation_branches.parent_branch is missing the self-referencing FK"
312+
);
313+
// Enum CHECKs.
314+
assert!(
315+
ddl.contains("CHECK (status IN ('active','merged','abandoned'))"),
316+
"simulation_branches.status enum CHECK missing"
317+
);
318+
assert!(
319+
ddl.contains("CHECK (operation IN ('insert','update','delete','transform'))"),
320+
"provenance_log.operation enum CHECK missing"
321+
);
322+
assert!(
323+
ddl.contains("CHECK (access_level IN ('read','write','admin','deny'))"),
324+
"access_policies.access_level enum CHECK missing"
325+
);
326+
assert!(
327+
ddl.contains(
328+
"CHECK (derivation_type IN ('copy','transform','aggregate','join','filter'))"
329+
),
330+
"lineage_graph.derivation_type enum CHECK missing"
331+
);
332+
}
333+
288334
/// Lineage edges must refuse self-loops at the storage layer
289335
/// (closes #42). The DAG claim in the README would be unenforced
290336
/// without this check.

0 commit comments

Comments
 (0)