constraints: CHECK-violation friendly error + typing-surface matrix (ADR-0029 §10)
Completes ADR-0029's implementation: the friendly-error layer now names the rule a CHECK violation broke, and the typing-surface matrix covers the whole constraint grammar. CHECK-violation friendly error (ADR-0029 §10): - enrich_dsl_failure gains a CHECK branch — it reads the column from the engine's `CHECK constraint failed: <column>` message, then resolves the table, the offending value, and the column's compiled CHECK expression. - FailureContext / TranslateContext carry the resolved check_rule; translate_check renders "the value <v> breaks the rule `<rule>`" when it is known, falling back to the plain hint otherwise. Typing-surface matrix: a new `constraints` submodule, 14 cells covering the create-table / add-column constraint suffix and the add-constraint / drop-constraint commands (174 → 188). 16 tests added (1 translate unit, 1 enrichment integration, 14 matrix cells).
This commit is contained in:
@@ -21,6 +21,7 @@ use rdbms_playground::db::{Database, DbError, SqliteErrorKind};
|
||||
use rdbms_playground::dsl::{
|
||||
action::ReferentialAction, ColumnSpec, Command, RowFilter, Type, Value,
|
||||
};
|
||||
use rdbms_playground::dsl::parser::parse_command;
|
||||
use rdbms_playground::runtime::enrich_dsl_failure;
|
||||
|
||||
fn rt() -> Runtime {
|
||||
@@ -475,6 +476,64 @@ fn enrich_fk_delete_resolves_child_table() {
|
||||
});
|
||||
}
|
||||
|
||||
// ---- CHECK (ADR-0029 §10) ---------------------------------------
|
||||
|
||||
#[test]
|
||||
fn enrich_check_insert_resolves_table_column_value_and_rule() {
|
||||
let db = db();
|
||||
rt().block_on(async {
|
||||
// `Scores(id serial pk)` plus a non-PK `score` column
|
||||
// carrying `CHECK (score >= 0)`.
|
||||
db.create_table(
|
||||
"Scores".to_string(),
|
||||
vec![ColumnSpec::new("id".to_string(), Type::Serial)],
|
||||
vec!["id".to_string()],
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let score_spec = match parse_command(
|
||||
"create table __probe with pk score(int) check (score >= 0)",
|
||||
)
|
||||
.expect("probe create parses")
|
||||
{
|
||||
Command::CreateTable { columns, .. } => {
|
||||
columns.into_iter().next().expect("one column")
|
||||
}
|
||||
other => panic!("expected CreateTable, got {other:?}"),
|
||||
};
|
||||
db.add_column("Scores".to_string(), score_spec, None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// An insert that violates the CHECK.
|
||||
let cmd = Command::Insert {
|
||||
table: "Scores".to_string(),
|
||||
columns: Some(vec!["score".to_string()]),
|
||||
values: vec![Value::Number("-5".to_string())],
|
||||
};
|
||||
let err = db
|
||||
.insert(
|
||||
"Scores".to_string(),
|
||||
Some(vec!["score".to_string()]),
|
||||
vec![Value::Number("-5".to_string())],
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
let facts = enrich_dsl_failure(&db, &cmd, &err).await;
|
||||
assert_eq!(facts.table.as_deref(), Some("Scores"));
|
||||
assert_eq!(facts.column.as_deref(), Some("score"));
|
||||
assert_eq!(facts.value.as_deref(), Some("-5"));
|
||||
let rule = facts.check_rule.expect("the CHECK rule is resolved");
|
||||
assert!(
|
||||
rule.contains("score"),
|
||||
"the resolved rule names the column: {rule}",
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// ---- non-engine error → empty enrichment ------------------------
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user