From ebb24f8783979c385535733b4acb05e9a62fc0ba Mon Sep 17 00:00:00 2001 From: Priec Date: Fri, 4 Sep 2026 23:18:39 +0200 Subject: [PATCH] reference a value from other linked table2 --- client | 2 +- client-gui2 | 2 +- common/proto/tables_data.proto | 5 +++++ common/src/proto/descriptor.bin | Bin 247554 -> 248429 bytes common/src/proto/komp_ac.tables_data.rs | 5 +++++ komp-app/src/value.rs | 24 ++++++++++++++++++++++++ server | 2 +- 7 files changed, 37 insertions(+), 3 deletions(-) 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 30145289d992346c18451e2ce6f84ea503dbce16..7059c7ffd83d8817d5f0327e5a2db859d063c3e3 100644 GIT binary patch delta 8471 zcmZ{qe~?sFcE`K#ebe+je$339VSe-s@ECu9>o6#?QHfU6HDnQ25?r#bRoR_p<^dgN zx+mQ|n2AZXpr|DHYh``9RY)x^RjaUyDG&wm2cYGuv6d=gc8x(wjq4AyJ+Nap?lUK)Ph@H@dA{;efp>0dK|1%87cPy4%TXmNRx4pXyzI&)SA?bIe;f5zdwoB= zxEPKUOXY#691VomM5A+7=Lbika40|A6Bdfqs5nq4t_-WCa5yTjz-MqY?9UHZN6JxH zT@{6uXeeK-7Wyk;X+@a#P4t=zAAWyTp%O;L{PMx55)S9f6^WHEj)wX2@6ic)zfZ}K*wSCj?^OV?Jyr3=3k zo;UB?=Z7mt3IkDXuux<^Y)u8_5~)iHX08Qcu~Y?(l0L~*M~9iIKVRf)Sav*xgZc8x zC|p#m{>l8%5JG+BuvldVp{V|CQ zm&CkWa}@T#X8%Z~S{kw;2i!ndSydW=+vQP+GFC)oS^Pq=QjJ&y7IMTa)EGge;``c5 zr2~C#c8Y(#?|CVe3iSCZ%=LuJ#^i%?}7C4Jgu=c^Wfl3yp5_?F(O4*-8Fv`6|gS^rq!HPclniab$3?k64l{mKiaf>=3aRPHA_D)E9Pk`Jbju zP{uCzx^nNNPZCvDvKEzl!|%brSv|Nm`soS3A)r#(5Buhf394sIN1}~9d+ZyEF-v4L z+-o;hey~zXr0btdyR|X#y13uiLno0YDjw5jG$b2uT$4_cmZWr@lB|P({!s1pS^N9TGC>Vn#}P``%f`y_2)c}NW|?xB{VK$nJ4reDaydUbmS-K>ylPCFb%eZX zOq+Ftyb5h@)V``?6GW+1*Q9ed>yrc>Q#^u1`+qGBzb~X2=4t0N^I|mEle7SsjF578 zZK1jwqz{cXOh2C7k#4JneMvBBtDGx^Qjz4ti<4GPZ!iPjoYq8^Bf;$U zu*9KKrZ+Ni-&rQ9{!+0*Q>R1uZu_&VO6;ofW1$k23-pEBKhTWHb#O3@h6+{cG3nUs z^P#Ex$7Y^BDy{nVyfK@?aza5X^W_0fD|4R70Vhp&B`Fy<8fuZ|Q?hfEOCumeJPa2E z3%ey{V@TC>%sT0OQQZ_j=X-UgW3G(`w@gNhHl=QOY=Xq zdv8wv%?0Ye-q@Y{fxf8Qzw5rVKOv|~r`GKr7}eLEn!auK(M@`3y6NBqCY!{z^VTcX zo7**E_jfkyKT|c<^72JjEarYNDyN4ujKKn(iIe-f?!rL#g0Or3yzZW`do@eJ^2>*L z-Tx$FiT)nb{sK1$xa#kZhO4H(ROP6x&Z=-yaSRo!VdB{nMysP@c19Ik?Tx@L6>OyIx!sfEmBng=*Trht!r_&^Lw&tS8ikzBYdKK`rGaNMQ)icMx z(yX2rcpKt9KiA7@8@?@!dC z%MxF9#a-j`a7YNuB(|IqzzK~G;(C{AmWzjE4BDvF+5Y4j?g)_ec;3<2MM)N z1w9TL=ecJQN_kU)OsrBrOL&=rWvsHNnpvg@FZVcJK-31bya2JiN7WKyz=XZW5i>>7 zzt?0)f*=wE+5~|lc&|$kXpEBtfke7jkzh!I4MAp;N^Mu^To5iZx<%%dVOpo$k|wsY znhm&bQrRwlA>1@b)|=y*ztG?9-R$V(BQ~4WgeistAX98sO@TS58YI~*DxQ6}?mh=V za$A(jCZ>|y7FBN-f>AuT$U=O}OvV~+jgS38_srYsgxH`Y-d2?jk_fU|Th-K7|6Jn& z`lV7GxijL|?$%xLimkeR4j-(F8)nXuLn>><+$hXFtW?hUfrtvUJOHtM zSk>F*Z&Wp*@L|>7VYJdHCGSwGJ@FL|fzDSr+@ZL6NP^hnx93Zpo zatZ|Ex=RJ8IAdnnrB0t^C<}tjV=A>TnIypSF_oQBGqVIT%j1qS5MhC~ZXm{wJI;WF zecVwNNM(B*WguLE&Q&Pe<0xYk*FBE10Lp6KUdI^_U!dg-i0@v<84%yS!kN$=2zo-P z>84Z=u0ZE1>U+ZVP)3b$(nFhr%u_1$oF$^>HDmge6Hl`c@w8HQi9v(~+HMBK_G!2M zfP{Towx49wEa^U@)HIV32v?xZ2xvyLR#`w?pHZD%mj7lE&wh4O;wwY|Er|@`yI13zftY6JNJo&u=)QvYn=sRta=8esVjd9Cg+6kw8ZsVL$>Mb%eEwu-=TP{Yo#G z_vW~Z5cZ}c3>#eD6vECX-BwZR+icm~De=+!==)oLrGpwD>{2s~^V@FI0&#xZZd&EF z1)29$>Vu>OwPE?5%61z8wMq8xyAA|`SD@ViKy2T4RRD?lzUx43N^UR5TnlP5I}hl5 zrJQ4~97gdy=2}pjlyls5AQ006Z81PxkGo=kxE^;Ms7=rb#g8bH&qF0Ze8J9Fq;NuI z?Rmhcu@0L3=hEi=m-$$w{x?4UkbbuIv8wA563h$4`$Q>oybB@(uwG37@IPro@;`KOav*>HxYVG`62h z5*zuAg?VWiU&iGf`tI77ibs>pR9;G7+S!%Y9%TNYQa5OOXKg3kA6&a_msS7Ta0rDU zC4`7-tmW?Z6`I!TQ{+0Yyj+mkpi_4yH07||pm{4zk{`2j z!qcrfxr2cS3$#1|F}_tNcQ7DfZ`H|tA}873<`M)!6VS>BXhu72MnGI|vl+>(&~&@5 zCB*^pyxB`-ZgJ}cG)~D50TObHTlr35=YF$8 z&@PCT2f9ciqWi}q$@KTT4T3R|?w1X+Uin$c4*bCG?Ys5F^qdE@vXt@_uLtzZxkfKQ zvC{L-@&C$H#Dv@9w;t11_HK7t=roUu?K*4kc|csYYtFd4EDL0{BaR-|KfQ3rc&>7( z*`bs7NvD!a%?_QsPk@Bjp?RO&Z9}k3_d$`qm>m9pPfSfEEcqj z3%X7{8Gp4$pWgVG<~bublrCAuJvyHIpguGH(O#XIvqvXK8$*QKqm#!BkZ^l+avSOr zF89TE?$rx=_l>*ATJ6)x8wML(_DSIvkioPd^NdbC8~@K<{in5OT=$tK8SHm^0R#;| zd%OY(xZmXfB;bC@K^oUI$>D%b-cul4fzDNOIpA_(6xRcii@07gO@wn$CodlmgaYk~ z0r5SkTkKN@i0?toMV76>!epM;seg+%@Vu^T^{?Wrl0PzdFT@w^(^pS_A<0QT zGR+H`A7+;s8^XR6KekW*V8%<@zAmtl)AS`hv)lg*SehPWexp5TC=E zCp*R9LQ1d1fA^HWdeJN6E)wV!U2lIMzy_CBw0WQ?Z$^;$olYH1I_C^5e|cvpp4Kox@lSDB)Gb(t%w-DKAdXpEElQYcOPnA?gVT!B{G zz?t|Sb9)ht?=jhnKQhyjd7jYqc0Yusd7hB{AiU2EG9T*HCkgKo z{6o!;dGe>FnZo-=@!kV^N$n%Y10S*Z$cYX}0v|ci%@k5Tc6EbD9%w}eG^0sJ2IBg$ zxGpA2m;7 z{e>$R$ShwtJph^I3)iT-CBd(xQO}Qm{H)H*{c7A#vir)30Ym)05;3fiP1`MMSmVVT zpVe!7*LcZ2hp#xT@v?ShfjF)4lD^R`=msyo^f~>*=!S70S^gWmL^&84t3)~7vivuC hNhb!uAkZ#95Z4>Mq!R;iz0o^;*7D4-{8s_z{2vAvaE$-} delta 7520 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 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