[Phase 2] CRUD composite primary keys (trait default methods)
Part of the SQL Server Phase 2 epic #150.
The driver isn't on main. Code lives on feat/sql-server and isn't in any release. Check that branch out, branch off it, target your PR at it — not at main. The whole thing squashes into main when Phase 2 closes (#149).
Phase 1 CRUD commands in src-tauri/src/commands.rs (update_record, delete_record around lines 1788–1855) assume a single PK column. SQL Server tables routinely have composite PKs — the driver needs a way to target rows by (col1, col2, ...) without breaking MySQL / Postgres / SQLite.
Task
1. Trait evolution (backward-compatible). In src-tauri/src/drivers/driver_trait.rs, add two default methods to DatabaseDriver:
async fn delete_record_composite(
&self,
params: &ConnectionParams,
table: &str,
pk_cols: &[String],
pk_vals: Vec<serde_json::Value>,
schema: Option<&str>,
) -> Result<u64, String> {
if pk_cols.len() == 1 {
let v = pk_vals.into_iter().next().unwrap_or(serde_json::Value::Null);
return self.delete_record(params, table, &pk_cols[0], v, schema).await;
}
Err("Composite primary keys not supported by this driver".into())
}
// Same pattern for update_record_composite (adds col_name / new_val / max_blob_size).
The default forwards to the legacy single-key method when pk_cols.len() == 1, which keeps MySQL / Postgres / SQLite drivers working without any change.
2. SQL Server override. In src-tauri/src/drivers/sqlserver/mod.rs, override both methods to build WHERE [c1]=@P1 AND [c2]=@P2 AND ... using tiberius parameter binding (ordinal markers).
3. Tauri command extension. In src-tauri/src/commands.rs, extend update_record / delete_record with optional parallel parameters:
pk_cols: Option<Vec<String>>,
pk_vals: Option<Vec<serde_json::Value>>,
If present → call the *_composite method. Otherwise → legacy path. Tauri deserializes missing JSON keys as None, so existing frontend callers are unaffected.
Rules
- Existing frontend CRUD flows (MySQL / Postgres / SQLite) keep working byte-identically — verify with regression test
- Unit tests for the
*_composite methods: one covering the single-key fallback, one covering a real composite WHERE clause (can use mock params for the sql_statement_for_... builders)
- No breaking changes to existing trait signatures — only new default methods
Reference
- Plan:
docs/sql-server-implementation-plan.md § Phase 2 — 2.3 + 2.4
- Existing assumption in MySQL:
src-tauri/src/drivers/mysql/mod.rs::update_record uses a single pk_col: &str
[Phase 2] CRUD composite primary keys (trait default methods)
Part of the SQL Server Phase 2 epic #150.
The driver isn't on
main. Code lives onfeat/sql-serverand isn't in any release. Check that branch out, branch off it, target your PR at it — not atmain. The whole thing squashes intomainwhen Phase 2 closes (#149).Phase 1 CRUD commands in
src-tauri/src/commands.rs(update_record,delete_recordaround lines 1788–1855) assume a single PK column. SQL Server tables routinely have composite PKs — the driver needs a way to target rows by(col1, col2, ...)without breaking MySQL / Postgres / SQLite.Task
1. Trait evolution (backward-compatible). In
src-tauri/src/drivers/driver_trait.rs, add two default methods toDatabaseDriver:The default forwards to the legacy single-key method when
pk_cols.len() == 1, which keeps MySQL / Postgres / SQLite drivers working without any change.2. SQL Server override. In
src-tauri/src/drivers/sqlserver/mod.rs, override both methods to buildWHERE [c1]=@P1 AND [c2]=@P2 AND ...using tiberius parameter binding (ordinal markers).3. Tauri command extension. In
src-tauri/src/commands.rs, extendupdate_record/delete_recordwith optional parallel parameters:If present → call the
*_compositemethod. Otherwise → legacy path. Tauri deserializes missing JSON keys asNone, so existing frontend callers are unaffected.Rules
*_compositemethods: one covering the single-key fallback, one covering a real compositeWHEREclause (can use mock params for thesql_statement_for_...builders)Reference
docs/sql-server-implementation-plan.md§ Phase 2 — 2.3 + 2.4src-tauri/src/drivers/mysql/mod.rs::update_recorduses a singlepk_col: &str