From 96f8d7bf39343f9be09010432986ea6e6ebef590 Mon Sep 17 00:00:00 2001 From: Filipriec Date: Sat, 29 Aug 2026 00:18:47 +0200 Subject: [PATCH] uniqueness only on nondeleted data4 import metadata --- common/proto/table_structure.proto | 55 ++++++ common/src/proto/descriptor.bin | Bin 196843 -> 200698 bytes common/src/proto/komp_ac.table_structure.rs | 202 ++++++++++++++++++++ server | 2 +- 4 files changed, 258 insertions(+), 1 deletion(-) diff --git a/common/proto/table_structure.proto b/common/proto/table_structure.proto index 15861a2c..8d44b06e 100644 --- a/common/proto/table_structure.proto +++ b/common/proto/table_structure.proto @@ -23,6 +23,12 @@ service TableStructureService { // - Returns an error if any validated table has no visible columns in // information_schema (e.g., physical table missing) rpc GetTableStructure(GetTableStructureRequest) returns (GetTableStructureResponse); + + // Describes the public values accepted by staged table-data import. Unlike + // GetTableStructure, this is an input contract: it includes logical types, + // omission/null behavior, stable identities and link targets. + rpc GetTableImportDescriptor(GetTableImportDescriptorRequest) + returns (GetTableImportDescriptorResponse); } // Request identifying the profile (schema) and tables to inspect. @@ -91,3 +97,52 @@ message TableColumn { // Stable public identity of a managed user column. Zero for system columns. int64 column_id = 10; } + +message GetTableImportDescriptorRequest { + string profile_name = 1; + string table_name = 2; +} + +enum ImportValueKind { + IMPORT_VALUE_KIND_UNSPECIFIED = 0; + IMPORT_VALUE_KIND_NULL = 1; + IMPORT_VALUE_KIND_STRING = 2; + IMPORT_VALUE_KIND_BOOLEAN = 3; + IMPORT_VALUE_KIND_NUMBER = 4; + IMPORT_VALUE_KIND_INTEGER_STRING = 5; + IMPORT_VALUE_KIND_LINK_DISPLAY = 6; +} + +message ImportLinkDescriptor { + int64 target_table_id = 1; + string target_profile_name = 2; + string target_table_name = 3; + repeated string target_row_display_columns = 4; +} + +message ImportFieldDescriptor { + // Stable identity of a managed user column. System columns have zero here + // and are identified by their server-owned name. + int64 column_id = 1; + string name = 2; + string logical_type = 3; + string storage_type = 4; + bool writable = 5; + bool required = 6; + bool may_omit = 7; + bool accepts_null = 8; + bool system = 9; + bool generated = 10; + string generated_from = 11; + optional ImportLinkDescriptor link = 12; + repeated ImportValueKind accepted_value_kinds = 13; +} + +message GetTableImportDescriptorResponse { + string requested_profile_name = 1; + string storage_profile_name = 2; + string table_name = 3; + int64 table_id = 4; + int64 table_revision = 5; + repeated ImportFieldDescriptor fields = 6; +} diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index e408177920e0ceb95d69ef942e0ca6fff5493a36..8a984bf5cb1b90dc2b76fcbe70593c16cc40fd00 100644 GIT binary patch delta 4375 zcmZ`*-)|Gy752R|_KdF+2oonbCM1&(euaeaW4jw*3mZ0pI0*?jgr-2ToOlw3IJVa_ zP8KRKu&{(`fx^VMa?&-t95a|67HJZfpDE+3nITTV5Y^y-G2E^TTqn zwp#LfpX#hlhIGbtiiORXg8w`uUBO>|Op^H?)>JGnyNmf^&c9i4QW`>wXA^4eB9<(; zcsOT%*^w@~bWv-Wyio$GW8#pN5BAs=sgPE)Oo4N9;>!%FiVF1qna{c_` zqEqp`T&Y$prc7Z@sI_S zA-7a5uci|4iD#P|q%&Bw&M>`*nx&dxOdJ^ux`%1UskhZ~Jdzcj8-!SwSyDy_>9u^Z z=Hyn~Qo&0#qb6I9>=Pn6DRPOMCt=N6A*He8oi;rSnsg* zs`F9JK{5)t;9tL@t=~^J7S318OK#B-y>f^p!>U!}a;}6PN>PykOWDo`_k7ZEMOHm7 z3MkkpSEE~by|t_%aHhbsDos^q&Gp=JNmN|4Guk_=7LnB067RYo_c2KY2Zt#ed226b zrJL9Pny{_^moK!&&dj;MSDAWdhXomv22TO^)XwJM^!L#-^}#Iv9n{@oZ2I5 z^|G!v_n68|Pv%B5v*)Kqe%KN_@*5ib>^S|r+j>S=>bpvA4f7uA>5w_uyu4Xi{`4_n z-u0$gcFM%7!_2UvB=R{4-aSDtknWcoC+Od_`d5VMu?QuWzLRzxPJc;kslDKMwtwBR zE4789yJ(A^_Uy1>?S-4R=VJyGY*GEeLf+5Yu9$@f?YUCXU2!(sch;}g7X4b)8Gxkg z+4$v4wp*&y{JOnZF8S5`qJP|WeXK1OYXxkrCXUUgkT<|=ObU;8*ASlx_JVUgzvh;! z13U<{ne7&wlJEL1nE6t{<`)Qo@iFTiWUPrr`4New?TmGWhyrz=>Vi1=C*i-~QDz~kGGrj%j?^!;`ZJBol%oWaPU;M52` zx@FMpL6EvmctAtJ-4Qy|KSXy67S^CKwCCLrL1XCHads6N)Y6Yejs@S0(5qXInqBM< z6e5(148JU#rYFfT?xf&hF^1Jddx)Spyj|pt6QDWV)5|8Hsag8_k%i#Zhje1g`(`V< z1WAoco(ztR()j}?Rcf#Xjg#?ap$G(xlP%lW&vik7ZT#gIqqK|mk2e@R8{=_HF?cq{ z+uB)15Ky|w;J;_+VQyx6QlSb%I;XeugyFB?CM>BzUmfr85X%I9JW(-*^cyJd~ zi`QDCx}{}SWL9s4l#wFHD4N2sqO!q1g-fd;t z8zMNCckfY>=(dWS5D1HRSEUh2433pueWHF*OZOrl%ldIS&lIQBqcl>tG@o{IagMOD zulfN9p1vuY4hZM;(R93kQJ#+)#rK?`@%q(N{b1s@cqqy1SL?PF4Kc0B6hMQ5wpMTO zxaL}1_7aTXt;vMI6=0nnR@-Sv`me)&I>mw>9w!n?u6e7HwwQWtb*3s^T= zLo>J!2!@ah*|hYBm=(%sf(saOF?0cq=Fkf2LxS2!YTVL4CA3``%+i(MDRi=E2xi1nqNXrO<4K{q5aC3K)C=}2$9AO zlIRX0ls1Un$Ldll0jUkLcfXQqvh>GzKUoR7R?~ULKO^Qov5A4}s6Qcf40Ij>AkO+EJS!lapOCl=7!a~g2qru~ ztAQ5RpAw8qNyTX5P^Jk+VU5cq0)qOK;8RRe@gemrYzl+cd2a%TT(10#;9-)=xIKWX zDGZK&N~pXaK%fE|$`?}!(3F*hRXF42m)jzT&k5cPsmpHQ$C}ehhQNiUf*+rC!_(2@k=$ zd3UD31RcnmHaNy0;_(V}pk6Em3FHU4JfHYH-qDt4Hq5r|h>sy}tINT7G34#l6z}>x z?sp97{GObhlQsEZ2=(o8B*sx3M`9esF^VH!R0VaQkiR3pos%8;dk7`NiB_hf=;+zt zMHNLSzaEI1pssxQgIMzM2)N-!uW2Z`>cu}uK|{$+rMc_BrVBci??x~wO+gJ}b>yd7 Q$m_^YQ<+6>0^dLU2k{PRW&i*H diff --git a/common/src/proto/komp_ac.table_structure.rs b/common/src/proto/komp_ac.table_structure.rs index 1da7b7a9..4a02457a 100644 --- a/common/src/proto/komp_ac.table_structure.rs +++ b/common/src/proto/komp_ac.table_structure.rs @@ -74,6 +74,113 @@ pub struct TableColumn { #[prost(int64, tag = "10")] pub column_id: i64, } +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct GetTableImportDescriptorRequest { + #[prost(string, tag = "1")] + pub profile_name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub table_name: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct ImportLinkDescriptor { + #[prost(int64, tag = "1")] + pub target_table_id: i64, + #[prost(string, tag = "2")] + pub target_profile_name: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub target_table_name: ::prost::alloc::string::String, + #[prost(string, repeated, tag = "4")] + pub target_row_display_columns: ::prost::alloc::vec::Vec< + ::prost::alloc::string::String, + >, +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct ImportFieldDescriptor { + /// Stable identity of a managed user column. System columns have zero here + /// and are identified by their server-owned name. + #[prost(int64, tag = "1")] + pub column_id: i64, + #[prost(string, tag = "2")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub logical_type: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub storage_type: ::prost::alloc::string::String, + #[prost(bool, tag = "5")] + pub writable: bool, + #[prost(bool, tag = "6")] + pub required: bool, + #[prost(bool, tag = "7")] + pub may_omit: bool, + #[prost(bool, tag = "8")] + pub accepts_null: bool, + #[prost(bool, tag = "9")] + pub system: bool, + #[prost(bool, tag = "10")] + pub generated: bool, + #[prost(string, tag = "11")] + pub generated_from: ::prost::alloc::string::String, + #[prost(message, optional, tag = "12")] + pub link: ::core::option::Option, + #[prost(enumeration = "ImportValueKind", repeated, tag = "13")] + pub accepted_value_kinds: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetTableImportDescriptorResponse { + #[prost(string, tag = "1")] + pub requested_profile_name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub storage_profile_name: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub table_name: ::prost::alloc::string::String, + #[prost(int64, tag = "4")] + pub table_id: i64, + #[prost(int64, tag = "5")] + pub table_revision: i64, + #[prost(message, repeated, tag = "6")] + pub fields: ::prost::alloc::vec::Vec, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ImportValueKind { + Unspecified = 0, + Null = 1, + String = 2, + Boolean = 3, + Number = 4, + IntegerString = 5, + LinkDisplay = 6, +} +impl ImportValueKind { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "IMPORT_VALUE_KIND_UNSPECIFIED", + Self::Null => "IMPORT_VALUE_KIND_NULL", + Self::String => "IMPORT_VALUE_KIND_STRING", + Self::Boolean => "IMPORT_VALUE_KIND_BOOLEAN", + Self::Number => "IMPORT_VALUE_KIND_NUMBER", + Self::IntegerString => "IMPORT_VALUE_KIND_INTEGER_STRING", + Self::LinkDisplay => "IMPORT_VALUE_KIND_LINK_DISPLAY", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "IMPORT_VALUE_KIND_UNSPECIFIED" => Some(Self::Unspecified), + "IMPORT_VALUE_KIND_NULL" => Some(Self::Null), + "IMPORT_VALUE_KIND_STRING" => Some(Self::String), + "IMPORT_VALUE_KIND_BOOLEAN" => Some(Self::Boolean), + "IMPORT_VALUE_KIND_NUMBER" => Some(Self::Number), + "IMPORT_VALUE_KIND_INTEGER_STRING" => Some(Self::IntegerString), + "IMPORT_VALUE_KIND_LINK_DISPLAY" => Some(Self::LinkDisplay), + _ => None, + } + } +} /// Generated client implementations. pub mod table_structure_service_client { #![allow( @@ -213,6 +320,38 @@ pub mod table_structure_service_client { ); self.inner.unary(req, path, codec).await } + /// Describes the public values accepted by staged table-data import. Unlike + /// GetTableStructure, this is an input contract: it includes logical types, + /// omission/null behavior, stable identities and link targets. + pub async fn get_table_import_descriptor( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic_prost::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/komp_ac.table_structure.TableStructureService/GetTableImportDescriptor", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "komp_ac.table_structure.TableStructureService", + "GetTableImportDescriptor", + ), + ); + self.inner.unary(req, path, codec).await + } } } /// Generated server implementations. @@ -246,6 +385,16 @@ pub mod table_structure_service_server { tonic::Response, tonic::Status, >; + /// Describes the public values accepted by staged table-data import. Unlike + /// GetTableStructure, this is an input contract: it includes logical types, + /// omission/null behavior, stable identities and link targets. + async fn get_table_import_descriptor( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; } /// Introspects the physical PostgreSQL tables for one or more logical tables /// (defined in table_definitions) and returns their column structures. @@ -381,6 +530,59 @@ pub mod table_structure_service_server { }; Box::pin(fut) } + "/komp_ac.table_structure.TableStructureService/GetTableImportDescriptor" => { + #[allow(non_camel_case_types)] + struct GetTableImportDescriptorSvc( + pub Arc, + ); + impl< + T: TableStructureService, + > tonic::server::UnaryService + for GetTableImportDescriptorSvc { + type Response = super::GetTableImportDescriptorResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + super::GetTableImportDescriptorRequest, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_table_import_descriptor( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = GetTableImportDescriptorSvc(inner); + let codec = tonic_prost::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } _ => { Box::pin(async move { let mut response = http::Response::new( diff --git a/server b/server index c958eb0f..e4392276 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit c958eb0f17080b86c97d487974ecba46467eaa71 +Subproject commit e4392276bcdf31be965f638e84eb8cde746d6cdb