ADR-0018 implementation: auto-fill contracts for serial and shortid
Generalises serial and shortid beyond their previous restricted forms: - `serial` is no longer restricted to single-column PK. Non-PK serial columns get an emitted UNIQUE constraint and use application-side MAX(col)+1 at INSERT time (rowid alias still drives the PK case for free; per ADR-0010 worker-thread serialisation, the read-then-insert sequence is safe). - `shortid` columns auto-fill existing null cells when the column is materialised — `add column T: x (shortid)` on a non-empty table no longer leaves rows in a not-really-valid NULL state. - `int -> serial` joins the type-change matrix as always-clean identity (closes the asymmetry vs `text -> shortid`); other sources are refused with a route-via-int hint. - `change column T: x (serial|shortid)` fills null source cells with sequence / generated values in the same rebuild transaction. Internal infrastructure: - ReadColumn gains `unique: bool`; read_schema detects single- column UNIQUE indexes via pragma_index_list / pragma_index_info; schema_to_ddl emits inline UNIQUE for non-PK columns. - ColumnSchema (persistence) gains `unique: bool` so the flag survives YAML round-trip and rebuild-from-text reconstructs it faithfully — preserves the "serial -> int leaves UNIQUE in place" promise across save/load cycles. - ChangeColumnTypeResult.client_side now carries `auto_filled` + `auto_fill_kind` alongside `transformed` + `lossy`; the app handler renders separate note lines when both apply. - AddColumnResult is a new return type carrying pre-rendered [client-side] note lines for the auto-fill paths. Tests: 519 -> 534 (+15). Clippy clean.
This commit is contained in:
+28
-12
@@ -71,12 +71,21 @@ fn write_table(out: &mut String, table: &TableSchema) {
|
||||
}
|
||||
|
||||
fn write_column(out: &mut String, col: &ColumnSchema) {
|
||||
let _ = writeln!(
|
||||
out,
|
||||
" - {{ name: {}, type: {} }}",
|
||||
quote_if_needed(&col.name),
|
||||
col.user_type.keyword(),
|
||||
);
|
||||
if col.unique {
|
||||
let _ = writeln!(
|
||||
out,
|
||||
" - {{ name: {}, type: {}, unique: true }}",
|
||||
quote_if_needed(&col.name),
|
||||
col.user_type.keyword(),
|
||||
);
|
||||
} else {
|
||||
let _ = writeln!(
|
||||
out,
|
||||
" - {{ name: {}, type: {} }}",
|
||||
quote_if_needed(&col.name),
|
||||
col.user_type.keyword(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn write_relationship(out: &mut String, rel: &RelationshipSchema) {
|
||||
@@ -181,6 +190,7 @@ pub(crate) fn parse_schema(body: &str) -> Result<SchemaSnapshot, YamlError> {
|
||||
columns.push(ColumnSchema {
|
||||
name: c.name,
|
||||
user_type,
|
||||
unique: c.unique,
|
||||
});
|
||||
}
|
||||
tables.push(TableSchema {
|
||||
@@ -265,6 +275,11 @@ struct RawColumn {
|
||||
name: String,
|
||||
#[serde(rename = "type")]
|
||||
user_type: String,
|
||||
/// Optional flag introduced in ADR-0018 for single-column
|
||||
/// UNIQUE constraints. Older project files without this
|
||||
/// field default to `false`.
|
||||
#[serde(default)]
|
||||
unique: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -295,16 +310,16 @@ mod tests {
|
||||
name: "Customers".to_string(),
|
||||
primary_key: vec!["id".to_string()],
|
||||
columns: vec![
|
||||
ColumnSchema { name: "id".to_string(), user_type: Type::Serial },
|
||||
ColumnSchema { name: "Name".to_string(), user_type: Type::Text },
|
||||
ColumnSchema { name: "id".to_string(), user_type: Type::Serial, unique: false },
|
||||
ColumnSchema { name: "Name".to_string(), user_type: Type::Text, unique: false },
|
||||
],
|
||||
},
|
||||
TableSchema {
|
||||
name: "Orders".to_string(),
|
||||
primary_key: vec!["id".to_string()],
|
||||
columns: vec![
|
||||
ColumnSchema { name: "id".to_string(), user_type: Type::Serial },
|
||||
ColumnSchema { name: "CustId".to_string(), user_type: Type::Int },
|
||||
ColumnSchema { name: "id".to_string(), user_type: Type::Serial, unique: false },
|
||||
ColumnSchema { name: "CustId".to_string(), user_type: Type::Int, unique: false },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -359,6 +374,7 @@ mod tests {
|
||||
columns: vec![ColumnSchema {
|
||||
name: "yes".to_string(),
|
||||
user_type: Type::Bool,
|
||||
unique: false,
|
||||
}],
|
||||
}],
|
||||
relationships: vec![],
|
||||
@@ -452,8 +468,8 @@ relationships:
|
||||
name: "Items".to_string(),
|
||||
primary_key: vec!["a".to_string(), "b".to_string()],
|
||||
columns: vec![
|
||||
ColumnSchema { name: "a".to_string(), user_type: Type::Int },
|
||||
ColumnSchema { name: "b".to_string(), user_type: Type::Int },
|
||||
ColumnSchema { name: "a".to_string(), user_type: Type::Int, unique: false },
|
||||
ColumnSchema { name: "b".to_string(), user_type: Type::Int, unique: false },
|
||||
],
|
||||
}],
|
||||
relationships: vec![],
|
||||
|
||||
Reference in New Issue
Block a user