ADR-0017 implementation: per-cell type-change with override flags
Replaces the placeholder "trust STRICT" body of do_change_column_type
with the per-cell transformer matrix from ADR-0017. Adds:
- src/type_change.rs: CellOutcome { Clean / Lossy / Incompatible }
+ transform_cell + static_refusal covering every matrix pair
from §3 (54 unit tests).
- --force-conversion and --dont-convert flags on `change column`
(mutually exclusive at parse time per §5).
- Refined PK rule (§4.1): refused only when the column has an
inbound FK and fk_target_type would change. Outbound-FK columns
still refused outright (§4.2). PK / shortid uniqueness checked
post-transformation (§4.3).
- Bordered diagnostic tables (lossy / incompatible / collision)
via the pretty-table renderer (§7) — uses ADR-0016's primitives.
- [client-side] success note (§6) when any cell was rewritten.
- Friendly wrapper for engine-level errors under --dont-convert
so no engine vocabulary leaks (ADR-0002 user-facing posture).
ADR-0017 §3 + §7 amended in place (with user sign-off): serial->int
added explicitly to the always-clean matrix, and diagnostic rows
identify themselves by PK value(s) rather than positional indices
(SQLite returns rows unordered without ORDER BY, so positional
"row 5" is unaddressable).
Tests: 449 -> 517 (+68). Clippy clean with nursery lints.
This commit is contained in:
+43
-1
@@ -13,7 +13,8 @@ use tracing::{trace, warn};
|
||||
|
||||
use crate::action::Action;
|
||||
use crate::db::{
|
||||
CascadeEffect, DataResult, DeleteResult, InsertResult, TableDescription, UpdateResult,
|
||||
CascadeEffect, ChangeColumnTypeResult, DataResult, DeleteResult, InsertResult,
|
||||
TableDescription, UpdateResult,
|
||||
};
|
||||
use crate::dsl::{Command, ParseError, parse_command};
|
||||
use crate::event::AppEvent;
|
||||
@@ -312,6 +313,10 @@ impl App {
|
||||
self.handle_dsl_delete_success(&command, &result);
|
||||
Vec::new()
|
||||
}
|
||||
AppEvent::DslChangeColumnSucceeded { command, result } => {
|
||||
self.handle_dsl_change_column_success(&command, result);
|
||||
Vec::new()
|
||||
}
|
||||
AppEvent::DslFailed { command, error } => {
|
||||
self.handle_dsl_failure(&command, &error);
|
||||
Vec::new()
|
||||
@@ -761,6 +766,43 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_dsl_change_column_success(
|
||||
&mut self,
|
||||
command: &Command,
|
||||
result: ChangeColumnTypeResult,
|
||||
) {
|
||||
let summary = format!("[ok] {} {}", command.verb(), command.display_subject());
|
||||
self.note_system(summary);
|
||||
if let Some(note) = result.client_side {
|
||||
// ADR-0017 §6: pedagogical hook telling the learner
|
||||
// "the tool did this for you; raw SQL would need a
|
||||
// CAST or application-level code." Lossy variant
|
||||
// adds the lossy count when --force-conversion was
|
||||
// used.
|
||||
let line = if note.lossy > 0 {
|
||||
format!(
|
||||
"[client-side] {n} row(s) transformed; {l} of those discarded \
|
||||
information (lossy). In raw SQL this would need an explicit \
|
||||
`CAST` or application-level code.",
|
||||
n = note.transformed,
|
||||
l = note.lossy
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"[client-side] {n} row(s) were transformed before being stored. \
|
||||
In raw SQL this would need an explicit `CAST` or \
|
||||
application-level code.",
|
||||
n = note.transformed
|
||||
)
|
||||
};
|
||||
self.note_system(line);
|
||||
}
|
||||
for line in crate::output_render::render_structure(&result.description) {
|
||||
self.note_system(line);
|
||||
}
|
||||
self.current_table = Some(result.description);
|
||||
}
|
||||
|
||||
fn handle_dsl_delete_success(&mut self, command: &Command, result: &DeleteResult) {
|
||||
self.note_system(format!(
|
||||
"[ok] {} {}",
|
||||
|
||||
Reference in New Issue
Block a user