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:
claude@clouddev1
2026-05-19 18:54:48 +00:00
parent abce1188f2
commit 5e97f6ac6a
22 changed files with 915 additions and 26 deletions
+37
View File
@@ -1248,6 +1248,8 @@ pub async fn enrich_dsl_failure(
enrich_unique_violation(database, command, message).await
} else if lower.contains("not null constraint failed") {
enrich_not_null_violation(command, message)
} else if lower.contains("check constraint failed") {
enrich_check_violation(database, command, message).await
} else if lower.contains("foreign key constraint failed") {
enrich_fk_violation(database, command).await
} else {
@@ -1255,6 +1257,41 @@ pub async fn enrich_dsl_failure(
}
}
/// Enrich a `CHECK` violation (ADR-0029 §10). The engine
/// reports `CHECK constraint failed: <column>` — the column the
/// constraint sits on, unqualified. We pair that with the
/// command's table, the value the user supplied for the column,
/// and the column's compiled `CHECK` expression so the friendly
/// error can name the rule that was broken.
async fn enrich_check_violation(
database: &Database,
command: &Command,
message: &str,
) -> crate::friendly::FailureContext {
let mut facts = crate::friendly::FailureContext::default();
let Some((_, after)) = message.split_once(':') else {
return facts;
};
let column = after.trim();
let table = command.target_table();
if column.is_empty() || table.is_empty() {
return facts;
}
facts.column = Some(column.to_string());
facts.table = Some(table.to_string());
// The value the user supplied for the constrained column.
facts.value = user_value_for_column_with_schema(database, command, table, column)
.await
.map(|v| v.to_string());
// The rule itself — the column's compiled CHECK expression.
if let Ok(desc) = database.describe_table(table.to_string(), None).await
&& let Some(col) = desc.columns.iter().find(|c| c.name == column)
{
facts.check_rule.clone_from(&col.check);
}
facts
}
async fn enrich_unique_violation(
database: &Database,
command: &Command,