diff --git a/client b/client index 9dbcd82f..fc712732 160000 --- a/client +++ b/client @@ -1 +1 @@ -Subproject commit 9dbcd82f37a22f94fb7dce29dba49f13eaa8e530 +Subproject commit fc712732df6521160302fd6ace06261fd8336f53 diff --git a/common/proto/table_definition.proto b/common/proto/table_definition.proto index f0c79d1a..076a1873 100644 --- a/common/proto/table_definition.proto +++ b/common/proto/table_definition.proto @@ -27,6 +27,11 @@ service TableDefinition { // Existing columns, links, and table logic are never changed by this call. rpc AddTableColumns(AddTableColumnsRequest) returns (TableDefinitionResponse); + // Atomically removes selected columns and appends new columns to an empty + // dynamic table. Column identities come from GetProfileDetails. The call is + // rejected after any row, including a soft-deleted row, has been stored. + rpc PutTableDefinition(PutTableDefinitionRequest) returns (PutTableDefinitionResponse); + // Lists all profiles (schemas) and their tables with declared dependencies. // This provides a tree-like overview of table relationships. rpc GetProfileTree(komp_ac.common.Empty) returns (ProfileTreeResponse); @@ -203,6 +208,31 @@ message AddTableColumnsRequest { } +// One atomic structural edit to an existing empty table. +// +// Removals use stable column IDs rather than aliases. Generated companion +// columns must be removed together with their generating column. New columns +// follow the same contract as AddTableColumns. +message PutTableDefinitionRequest { + string profile_name = 1; + string table_name = 2; + repeated int64 remove_column_ids = 3; + repeated ColumnDefinition add_columns = 4; + repeated string add_indexes = 5; + repeated GeneratedColumnAlias generated_aliases = 6; + + // Revision returned by GetProfileDetails. Prevents a stale editor from + // overwriting a newer structural or presentation change. + int64 expected_row_version = 7; +} + +message PutTableDefinitionResponse { + bool success = 1; + string sql = 2; + int64 row_version = 3; + repeated int64 removed_column_ids = 4; +} + enum MoneyRounding { MONEY_ROUNDING_NONE = 0; MONEY_ROUNDING_HALF_UP = 1; diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index 9fe5cba2..0e67ea93 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.table_definition.rs b/common/src/proto/komp_ac.table_definition.rs index 59af8e83..2d84a993 100644 --- a/common/src/proto/komp_ac.table_definition.rs +++ b/common/src/proto/komp_ac.table_definition.rs @@ -165,6 +165,41 @@ pub struct AddTableColumnsRequest { #[prost(message, repeated, tag = "5")] pub generated_aliases: ::prost::alloc::vec::Vec, } +/// One atomic structural edit to an existing empty table. +/// +/// Removals use stable column IDs rather than aliases. Generated companion +/// columns must be removed together with their generating column. New columns +/// follow the same contract as AddTableColumns. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PutTableDefinitionRequest { + #[prost(string, tag = "1")] + pub profile_name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub table_name: ::prost::alloc::string::String, + #[prost(int64, repeated, tag = "3")] + pub remove_column_ids: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub add_columns: ::prost::alloc::vec::Vec, + #[prost(string, repeated, tag = "5")] + pub add_indexes: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "6")] + pub generated_aliases: ::prost::alloc::vec::Vec, + /// Revision returned by GetProfileDetails. Prevents a stale editor from + /// overwriting a newer structural or presentation change. + #[prost(int64, tag = "7")] + pub expected_row_version: i64, +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct PutTableDefinitionResponse { + #[prost(bool, tag = "1")] + pub success: bool, + #[prost(string, tag = "2")] + pub sql: ::prost::alloc::string::String, + #[prost(int64, tag = "3")] + pub row_version: i64, + #[prost(int64, repeated, tag = "4")] + pub removed_column_ids: ::prost::alloc::vec::Vec, +} /// Describes one user-defined column for a table. #[derive(serde::Serialize, serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] @@ -893,6 +928,38 @@ pub mod table_definition_client { ); self.inner.unary(req, path, codec).await } + /// Atomically removes selected columns and appends new columns to an empty + /// dynamic table. Column identities come from GetProfileDetails. The call is + /// rejected after any row, including a soft-deleted row, has been stored. + pub async fn put_table_definition( + &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_definition.TableDefinition/PutTableDefinition", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "komp_ac.table_definition.TableDefinition", + "PutTableDefinition", + ), + ); + self.inner.unary(req, path, codec).await + } /// Lists all profiles (schemas) and their tables with declared dependencies. /// This provides a tree-like overview of table relationships. pub async fn get_profile_tree( @@ -1191,6 +1258,16 @@ pub mod table_definition_server { tonic::Response, tonic::Status, >; + /// Atomically removes selected columns and appends new columns to an empty + /// dynamic table. Column identities come from GetProfileDetails. The call is + /// rejected after any row, including a soft-deleted row, has been stored. + async fn put_table_definition( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; /// Lists all profiles (schemas) and their tables with declared dependencies. /// This provides a tree-like overview of table relationships. async fn get_profile_tree( @@ -1542,6 +1619,55 @@ pub mod table_definition_server { }; Box::pin(fut) } + "/komp_ac.table_definition.TableDefinition/PutTableDefinition" => { + #[allow(non_camel_case_types)] + struct PutTableDefinitionSvc(pub Arc); + impl< + T: TableDefinition, + > tonic::server::UnaryService + for PutTableDefinitionSvc { + type Response = super::PutTableDefinitionResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::put_table_definition( + &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 = PutTableDefinitionSvc(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) + } "/komp_ac.table_definition.TableDefinition/GetProfileTree" => { #[allow(non_camel_case_types)] struct GetProfileTreeSvc(pub Arc); diff --git a/server b/server index 0a8cf005..f559f57d 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit 0a8cf005d853b760eb295f57c223ea4f27cd42fc +Subproject commit f559f57d40b9b0b8821ff9a924b2392903302813