From f9cf54e67c60c87cb509ec26233c7bfee1dae907 Mon Sep 17 00:00:00 2001 From: filipriec Date: Mon, 3 Mar 2025 15:15:30 +0100 Subject: [PATCH] delete table --- common/proto/table_definition.proto | 11 ++ common/src/proto/descriptor.bin | Bin 12964 -> 13562 bytes .../src/proto/multieko2.table_definition.rs | 95 ++++++++++++++++++ .../services/table_definition_service.rs | 25 ++++- server/src/table_definition/handlers.rs | 2 + .../table_definition/handlers/delete_table.rs | 84 ++++++++++++++++ .../handlers/post_table_definition.rs | 9 -- 7 files changed, 212 insertions(+), 14 deletions(-) create mode 100644 server/src/table_definition/handlers/delete_table.rs diff --git a/common/proto/table_definition.proto b/common/proto/table_definition.proto index 9d3fc8c..01ef71c 100644 --- a/common/proto/table_definition.proto +++ b/common/proto/table_definition.proto @@ -7,6 +7,7 @@ import "common.proto"; service TableDefinition { rpc PostTableDefinition (PostTableDefinitionRequest) returns (TableDefinitionResponse); rpc GetProfileTree (multieko2.common.Empty) returns (ProfileTreeResponse); + rpc DeleteTable (DeleteTableRequest) returns (DeleteTableResponse); } message PostTableDefinitionRequest { @@ -40,3 +41,13 @@ message ProfileTreeResponse { repeated Profile profiles = 1; } + +message DeleteTableRequest { + string profile_name = 1; + string table_name = 2; +} + +message DeleteTableResponse { + bool success = 1; + string message = 2; +} diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index ba8766b98bc4c479f006bab5f66b43c024949589..59e840ce19bddabb4d311fb66bc7cefc6d3e5a47 100644 GIT binary patch delta 1651 zcmY+FOKuZE5QgVb2)Cys1~*f|0S!2*d0u|-KVL6PzTJ3FKiXYdu+ zus}j$&4M-J65IfmtoWQv3F6^v8+&uZ4UZT}I33 z)ye5)bR50Aixx}S6ZzZsH|H0Z(VOd&tEgdj?Y-=A&U|};PqHn}3y1s9 zMEQa4;`Zix5y^(wSlpeRMT>>fTs_K&Hh;Xrmy@?qL+d0s`|YfLjQ@u6TIBAj$?59j zc&l>ni}Bq&;6VF9Kp*`4b6#X5+1AnO`{bo9R>AZ`oD-C>97noYdMfh5W3WMfIU$Z! zcN20!vnN6bhb&2XOWOF`B?~oqzb_z1mSah`Kus=T@R&qGNG#;43B}MWG&W5gVny0F zF`Jn331vWqc0!!l86*Ue;@-%Jz;8fZ6LKIDq#_Qi1O;_77~^}!L8X456AChwa!XtEFWzA5b&%KoE5`jDu}B zk~Xa^ZG&62>FeG&Y@U*jNgxGdI6a|st7@O>(aYeDA5$ARN;+m1( z4a)KQ1J3N$>S;aVJ%2Y1^EUSU(wd>y3+rp<{6>Gxkn0VG#wtd-L%WGpzU)rE%tysA zr{+3Hz3RyhQm=ZpgP?4*!O$M0Rxvf{4)ON%Re`WNOs#_PbcSJV9q0UdTXD?CbGwJ# zo*I+OjT=#4ch#|=H8Sr9At;RsdI3Su8CCU@1VLm35ndgVG`0@(%y`kqep87Pi%-n6 z0(de8>YoN6GAHI;fuJ`r?~3?}Q?tSV^nRL_aW|)CgAqg7)NHUP>CigTHauhZ)R55V;ghQFCT!ixv>G_SWc?GflxND)=o3{cd@*g F{|A!Tkum@P delta 1067 zcmYL|O^(w*5QXjTn&7m5Vz=%5lElfxgEJB!!7X6L6%Z^~4MHrDe283u1B}Em5E46X zFneanidXI*FF(DiSFfw=f1kd8y#IL?e~KT!&VPk`^%!10JbzSg9;RxdDGbh+-!I;U z)64PYw_Yhhfu=YpLZy_XAk;FBjw)6}fe5rrXsr@;#c;)>9HB6YYey2aT-i2ADcxEr zB&NjC1e9(a^<27Z9l|B;&ela}nNedU0hi3X2t>)!(rGZss*NL)85#qIM_AaxTw6*>!h@!hNGQ0YCmK(XFK_<=5QyU)tv6+qv9r7 zw)nBkQ-O%u`lw+17;P~hb*^7*M@H1PIjHz8uay_q(a~&G1n}zY&jATeT{?VTTnL}8 UHa{E)E?skYKl%LS?(5y_|MTQM4gdfE diff --git a/common/src/proto/multieko2.table_definition.rs b/common/src/proto/multieko2.table_definition.rs index 30d6c05..bd5870d 100644 --- a/common/src/proto/multieko2.table_definition.rs +++ b/common/src/proto/multieko2.table_definition.rs @@ -48,6 +48,20 @@ pub mod profile_tree_response { pub tables: ::prost::alloc::vec::Vec, } } +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DeleteTableRequest { + #[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, ::prost::Message)] +pub struct DeleteTableResponse { + #[prost(bool, tag = "1")] + pub success: bool, + #[prost(string, tag = "2")] + pub message: ::prost::alloc::string::String, +} /// Generated client implementations. pub mod table_definition_client { #![allow( @@ -197,6 +211,35 @@ pub mod table_definition_client { ); self.inner.unary(req, path, codec).await } + pub async fn delete_table( + &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::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/multieko2.table_definition.TableDefinition/DeleteTable", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "multieko2.table_definition.TableDefinition", + "DeleteTable", + ), + ); + self.inner.unary(req, path, codec).await + } } } /// Generated server implementations. @@ -226,6 +269,13 @@ pub mod table_definition_server { tonic::Response, tonic::Status, >; + async fn delete_table( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; } #[derive(Debug)] pub struct TableDefinitionServer { @@ -398,6 +448,51 @@ pub mod table_definition_server { }; Box::pin(fut) } + "/multieko2.table_definition.TableDefinition/DeleteTable" => { + #[allow(non_camel_case_types)] + struct DeleteTableSvc(pub Arc); + impl< + T: TableDefinition, + > tonic::server::UnaryService + for DeleteTableSvc { + type Response = super::DeleteTableResponse; + 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 { + ::delete_table(&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 = DeleteTableSvc(inner); + let codec = tonic::codec::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(empty_body()); diff --git a/server/src/server/services/table_definition_service.rs b/server/src/server/services/table_definition_service.rs index a96cd6a..919e877 100644 --- a/server/src/server/services/table_definition_service.rs +++ b/server/src/server/services/table_definition_service.rs @@ -1,20 +1,27 @@ -// server/src/server/services/table_definition_service.rs +// src/server/services/table_definition_service.rs use tonic::{Request, Response, Status}; use common::proto::multieko2::{ common::Empty, table_definition::{ table_definition_server::TableDefinition, - PostTableDefinitionRequest, TableDefinitionResponse, ProfileTreeResponse - } + PostTableDefinitionRequest, TableDefinitionResponse, + ProfileTreeResponse, DeleteTableRequest, DeleteTableResponse, + }, }; use sqlx::PgPool; -use crate::table_definition::handlers::{post_table_definition, get_profile_tree}; +use crate::table_definition::handlers::{post_table_definition, get_profile_tree, delete_table}; #[derive(Debug)] pub struct TableDefinitionService { pub db_pool: PgPool, } +impl TableDefinitionService { + pub fn new(db_pool: PgPool) -> Self { + Self { db_pool } + } +} + #[tonic::async_trait] impl TableDefinition for TableDefinitionService { async fn post_table_definition( @@ -27,8 +34,16 @@ impl TableDefinition for TableDefinitionService { async fn get_profile_tree( &self, - request: Request, // Changed from Request<()> + request: Request, ) -> Result, Status> { get_profile_tree::get_profile_tree(&self.db_pool, request).await } + + async fn delete_table( + &self, + request: Request, + ) -> Result, Status> { + let response = delete_table(&self.db_pool, request.into_inner()).await?; + Ok(Response::new(response)) + } } diff --git a/server/src/table_definition/handlers.rs b/server/src/table_definition/handlers.rs index db5b807..d66aa88 100644 --- a/server/src/table_definition/handlers.rs +++ b/server/src/table_definition/handlers.rs @@ -1,6 +1,8 @@ // server/src/table_definition/handlers.rs pub mod post_table_definition; pub mod get_profile_tree; +pub mod delete_table; pub use post_table_definition::post_table_definition; pub use get_profile_tree::get_profile_tree; +pub use delete_table::delete_table; diff --git a/server/src/table_definition/handlers/delete_table.rs b/server/src/table_definition/handlers/delete_table.rs new file mode 100644 index 0000000..0af789a --- /dev/null +++ b/server/src/table_definition/handlers/delete_table.rs @@ -0,0 +1,84 @@ +// server/src/table_definition/handlers/delete_table.rs +use tonic::Status; +use sqlx::{PgPool, Postgres, Transaction}; +use common::proto::multieko2::table_definition::{DeleteTableRequest, DeleteTableResponse}; + +pub async fn delete_table( + db_pool: &PgPool, + request: DeleteTableRequest, +) -> Result { + let mut transaction = db_pool.begin().await + .map_err(|e| Status::internal(format!("Failed to start transaction: {}", e)))?; + + // Step 1: Get profile and validate existence + let profile = sqlx::query!( + "SELECT id FROM profiles WHERE name = $1", + request.profile_name + ) + .fetch_optional(&mut *transaction) + .await + .map_err(|e| Status::internal(format!("Profile lookup failed: {}", e)))?; + + let profile_id = match profile { + Some(p) => p.id, + None => return Err(Status::not_found("Profile not found")), + }; + + // Step 2: Get table definition and validate existence + let table_def = sqlx::query!( + "SELECT id FROM table_definitions + WHERE profile_id = $1 AND table_name = $2", + profile_id, + request.table_name + ) + .fetch_optional(&mut *transaction) + .await + .map_err(|e| Status::internal(format!("Table lookup failed: {}", e)))?; + + let table_def_id = match table_def { + Some(t) => t.id, + None => return Err(Status::not_found("Table not found in profile")), + }; + + // Step 3: Drop the actual PostgreSQL table with CASCADE + sqlx::query(&format!(r#"DROP TABLE IF EXISTS "{}" CASCADE"#, request.table_name)) + .execute(&mut *transaction) + .await + .map_err(|e| Status::internal(format!("Table drop failed: {}", e)))?; + + // Step 4: Delete from table_definitions + sqlx::query!( + "DELETE FROM table_definitions WHERE id = $1", + table_def_id + ) + .execute(&mut *transaction) + .await + .map_err(|e| Status::internal(format!("Definition deletion failed: {}", e)))?; + + // Step 5: Check and clean up profile if empty + let remaining = sqlx::query!( + "SELECT COUNT(*) as count FROM table_definitions WHERE profile_id = $1", + profile_id + ) + .fetch_one(&mut *transaction) + .await + .map_err(|e| Status::internal(format!("Count query failed: {}", e)))?; + + if remaining.count.unwrap_or(1) == 0 { + sqlx::query!( + "DELETE FROM profiles WHERE id = $1", + profile_id + ) + .execute(&mut *transaction) + .await + .map_err(|e| Status::internal(format!("Profile cleanup failed: {}", e)))?; + } + + transaction.commit().await + .map_err(|e| Status::internal(format!("Transaction commit failed: {}", e)))?; + + Ok(DeleteTableResponse { + success: true, + message: format!("Table '{}' and its definition were successfully removed", request.table_name), + }) +} diff --git a/server/src/table_definition/handlers/post_table_definition.rs b/server/src/table_definition/handlers/post_table_definition.rs index 3794ae9..b3fbf3a 100644 --- a/server/src/table_definition/handlers/post_table_definition.rs +++ b/server/src/table_definition/handlers/post_table_definition.rs @@ -101,15 +101,6 @@ pub async fn post_table_definition( } // Process indexes without year prefix - let mut indexes = Vec::new(); - for idx in request.indexes.drain(..) { - let idx_name = sanitize_identifier(&idx); // No year prefix - if !is_valid_identifier(&idx) { - return Err(Status::invalid_argument(format!("Invalid index name: {}", idx))); - } - indexes.push(idx_name); - } - let mut indexes = Vec::new(); for idx in request.indexes.drain(..) { let idx_name = sanitize_identifier(&idx);