diff --git a/client b/client index 4df0322e..e104825e 160000 --- a/client +++ b/client @@ -1 +1 @@ -Subproject commit 4df0322e126d7deecaffd07c2ffe3a837c880cfa +Subproject commit e104825edc850e8706ad85c2639c3cf6b432791e diff --git a/client-gui2 b/client-gui2 index 0461f0f6..f3ae8177 160000 --- a/client-gui2 +++ b/client-gui2 @@ -1 +1 @@ -Subproject commit 0461f0f6021109bd0aa97426686b41f835a83a9d +Subproject commit f3ae817797640b4b89a135b1241d16379fd9c570 diff --git a/common/proto/tables_data.proto b/common/proto/tables_data.proto index fdeffebd..49919fda 100644 --- a/common/proto/tables_data.proto +++ b/common/proto/tables_data.proto @@ -258,6 +258,11 @@ message PutTableDataRequest { // FAILED_PRECONDITION, instructing the caller to include X explicitly. // // Passing an empty map results in a no-op success response. + // A LINK may be supplied as {"id": "42", "version": "2"} to explicitly + // accept its target's current version, even when the row id is unchanged. + // Both values must be positive integers. The server rejects a stale version + // and atomically updates the saved FK version and all its stored linked values. + // A plain unchanged row id preserves the previously saved version and values. map data = 4; // Required. Revision returned when this row was loaded or last saved. diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index 30145289..7059c7ff 100644 Binary files a/common/src/proto/descriptor.bin and b/common/src/proto/descriptor.bin differ diff --git a/common/src/proto/komp_ac.tables_data.rs b/common/src/proto/komp_ac.tables_data.rs index ae3aa9a6..4ecd6908 100644 --- a/common/src/proto/komp_ac.tables_data.rs +++ b/common/src/proto/komp_ac.tables_data.rs @@ -168,6 +168,11 @@ pub struct PutTableDataRequest { /// FAILED_PRECONDITION, instructing the caller to include X explicitly. /// /// Passing an empty map results in a no-op success response. + /// A LINK may be supplied as {"id": "42", "version": "2"} to explicitly + /// accept its target's current version, even when the row id is unchanged. + /// Both values must be positive integers. The server rejects a stale version + /// and atomically updates the saved FK version and all its stored linked values. + /// A plain unchanged row id preserves the previously saved version and values. #[prost(map = "string, message", tag = "4")] pub data: ::std::collections::HashMap< ::prost::alloc::string::String, diff --git a/komp-app/src/value.rs b/komp-app/src/value.rs index 18cf358c..3ac8d369 100644 --- a/komp-app/src/value.rs +++ b/komp-app/src/value.rs @@ -59,10 +59,34 @@ pub fn convert_and_validate_data( .collect() } +pub fn accepted_link_value(id: i64, version: i64) -> Result { + if id <= 0 || version <= 0 { + return Err("Selected link id and version must be positive".into()); + } + Ok(Value { + kind: Some(Kind::StructValue(prost_types::Struct { + fields: [ + ("id".into(), Value { kind: Some(Kind::StringValue(id.to_string())) }), + ("version".into(), Value { kind: Some(Kind::StringValue(version.to_string())) }), + ].into(), + })), + }) +} + #[cfg(test)] mod tests { use super::*; + #[test] + fn accepted_link_selection_keeps_full_precision() { + let selected = accepted_link_value(i64::MAX, 3).unwrap(); + let Some(Kind::StructValue(fields)) = selected.kind else { panic!("Expected a link selection") }; + assert_eq!(fields.fields["id"].kind, Some(Kind::StringValue(i64::MAX.to_string()))); + assert_eq!(fields.fields["version"].kind, Some(Kind::StringValue("3".into()))); + assert!(accepted_link_value(0, 3).is_err()); + assert!(accepted_link_value(42, -1).is_err()); + } + #[test] fn preserves_large_integers_and_decimals_as_strings() { assert_eq!( diff --git a/server b/server index 41a34de4..81a49d66 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit 41a34de46f7c206e80f723167fef70789638c62a +Subproject commit 81a49d66d6c1e39655f06982e9c1ed87a69f0804