From 87e465a36e7858f75f0c2137324c087b224f20dd Mon Sep 17 00:00:00 2001 From: filipriec Date: Sat, 1 Mar 2025 18:30:19 +0100 Subject: [PATCH] working properly well creation of profiles and creation of tables by user --- common/src/proto/descriptor.bin | Bin 11622 -> 12102 bytes .../src/proto/multieko2.table_definition.rs | 15 +++- ...0250301152039_create_table_definitions.sql | 2 +- .../handlers/post_table_definition.rs | 64 ++++++++++-------- 4 files changed, 51 insertions(+), 30 deletions(-) diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index eca1a7808aeb7e93d1d708bb6990c4b46e66613f..a97f56581de14cdaacc3f32d347678e1dfb82d2c 100644 GIT binary patch delta 686 zcmZXSPfo%>6vjK#2g0;an3m8YDq>6uG|}kF#FZH0${6Cx7zrvQjZlo@x=Y>91w4YM zaACZJ8xP^a_@;E+u>8I6&wNe#eer(v_R*q0OnkPw^qGoM`;hn8%FS%@G>slz+R%Mt zFAzbeUg{>d@)^gVma!4z!J-EF~(?&3UIAL5Og4D<2g5JCsgCQ4NlU4fPHp4 z3JHz2VJQv!Y;LD9lpOF4a*ELC=1YpO&$XR2hLQ)1@26DG@X{GbQZtG&#sLv?UYTEK@o$>X_WuAdn_AQW delta 270 zcmX>W_bh6|DNUwX?32%EIy2@^7Sys4kl639wY);^AUtVPIlp5n|?H;eqj3fjnMe7>^Oi6Oo4Tn1MVw z`N@U4ZuMMTOe~B-ECLL|j9gq?%q)yRl>!{{T-;nNER0MHLd=3JqF`-64$w$J77iE> zs8Eno0LB9vDJU!g)drLjVu9Gr$ixWLCd9%8RR}U)h?@__;{)mw5)?{eD=5k@$u|Z7 DvdteV diff --git a/common/src/proto/multieko2.table_definition.rs b/common/src/proto/multieko2.table_definition.rs index 7bc9182..567dc83 100644 --- a/common/src/proto/multieko2.table_definition.rs +++ b/common/src/proto/multieko2.table_definition.rs @@ -3,10 +3,21 @@ pub struct PostTableDefinitionRequest { #[prost(string, tag = "1")] pub table_name: ::prost::alloc::string::String, - #[prost(string, repeated, tag = "2")] - pub columns: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "2")] + pub columns: ::prost::alloc::vec::Vec, #[prost(string, repeated, tag = "3")] pub indexes: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, tag = "4")] + pub profile_name: ::prost::alloc::string::String, + #[prost(string, optional, tag = "5")] + pub linked_table_name: ::core::option::Option<::prost::alloc::string::String>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ColumnDefinition { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub data_type: ::prost::alloc::string::String, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct TableDefinitionResponse { diff --git a/server/migrations/20250301152039_create_table_definitions.sql b/server/migrations/20250301152039_create_table_definitions.sql index 591718e..25136e5 100644 --- a/server/migrations/20250301152039_create_table_definitions.sql +++ b/server/migrations/20250301152039_create_table_definitions.sql @@ -2,7 +2,7 @@ CREATE TABLE table_definitions ( id BIGSERIAL PRIMARY KEY, deleted BOOLEAN NOT NULL DEFAULT FALSE, - table_name TEXT NOT NULL UNIQUE, + table_name TEXT NOT NULL, columns JSONB NOT NULL, indexes JSONB NOT NULL, created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, diff --git a/server/src/table_definition/handlers/post_table_definition.rs b/server/src/table_definition/handlers/post_table_definition.rs index 6cd9daa..3083c45 100644 --- a/server/src/table_definition/handlers/post_table_definition.rs +++ b/server/src/table_definition/handlers/post_table_definition.rs @@ -1,17 +1,15 @@ // src/table_definition/handlers/post_table_definition.rs use tonic::Status; -use sqlx::{PgPool, Row}; +use sqlx::PgPool; use serde_json::json; use common::proto::multieko2::table_definition::{PostTableDefinitionRequest, TableDefinitionResponse}; const VALID_DATA_TYPES: &[&str] = &["TEXT", "INTEGER", "BIGINT", "BOOLEAN", "TIMESTAMPTZ", "NUMERIC"]; -// Add to validation section fn is_valid_data_type(dt: &str) -> bool { VALID_DATA_TYPES.contains(&dt.to_uppercase().as_str()) } -// Validate SQL identifiers fn is_valid_identifier(s: &str) -> bool { !s.is_empty() && s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') && @@ -19,7 +17,6 @@ fn is_valid_identifier(s: &str) -> bool { !s.chars().next().unwrap().is_ascii_digit() } -// Sanitize SQL identifiers fn sanitize_identifier(s: &str) -> String { s.replace(|c: char| !c.is_ascii_alphanumeric() && c != '_', "") .trim() @@ -48,29 +45,31 @@ pub async fn post_table_definition( .map_err(|e| Status::internal(format!("Profile error: {}", e)))?; // Validate linked table if provided - let linked_table_id = if let Some(linked_table) = &request.linked_table_name { - let lt = sqlx::query!( - "SELECT id FROM table_definitions - WHERE profile_id = $1 AND table_name = $2", + let linked_table_id; + let linked_table_name = request.linked_table_name + .as_ref() + .map(|lt| sanitize_identifier(lt)); + + if let Some(lt_name) = &linked_table_name { + let lt_record = sqlx::query!( + "SELECT id FROM table_definitions + WHERE profile_id = $1 AND table_name = $2", profile.id, - sanitize_identifier(linked_table) + lt_name ) .fetch_optional(db_pool) .await .map_err(|e| Status::internal(format!("Linked table lookup failed: {}", e)))?; - lt.map(|r| r.id) - .ok_or_else(|| Status::not_found("Linked table not found in profile"))? + linked_table_id = match lt_record { + Some(r) => Some(r.id), + None => return Err(Status::not_found("Linked table not found in profile")), + }; } else { - None - }; - - fn is_reserved_column(name: &str, linked_table: Option<&str>) -> bool { - let reserved = vec!["id", "deleted", "firma", "created_at"]; - reserved.contains(&name) || linked_table.map(|lt| format!("{}_id", lt)) == Some(name.to_string()) + linked_table_id = None; } - // Validate columns and indexes (add data type support) + // Validate columns and indexes let mut columns = Vec::new(); for col_def in request.columns.drain(..) { let col_name = sanitize_identifier(&col_def.name); @@ -93,7 +92,12 @@ pub async fn post_table_definition( } // Generate SQL - let (create_sql, index_sql) = generate_table_sql(&table_name, &columns, &indexes); + let (create_sql, index_sql) = generate_table_sql( + &table_name, + &columns, + &indexes, + linked_table_name.as_deref() + ); // Store definition sqlx::query!( @@ -136,8 +140,6 @@ pub async fn post_table_definition( }) } - -// Add system columns to SQL generation fn generate_table_sql( table_name: &str, columns: &[String], @@ -145,9 +147,9 @@ fn generate_table_sql( linked_table: Option<&str>, ) -> (String, Vec) { let mut system_columns = vec![ - "id BIGSERIAL PRIMARY KEY", - "deleted BOOLEAN NOT NULL DEFAULT FALSE", - "firma TEXT NOT NULL", + "id BIGSERIAL PRIMARY KEY".to_string(), + "deleted BOOLEAN NOT NULL DEFAULT FALSE".to_string(), + "firma TEXT NOT NULL".to_string(), ]; if let Some(linked) = linked_table { @@ -158,8 +160,8 @@ fn generate_table_sql( let all_columns = system_columns .iter() - .map(|s| s.to_string()) - .chain(columns.iter().cloned()) + .chain(columns.iter()) + .cloned() .collect::>(); let create_sql = format!( @@ -179,5 +181,13 @@ fn generate_table_sql( )); } - (create_sql, [system_indexes, indexes.to_vec()].concat()) + let all_indexes = system_indexes + .into_iter() + .chain(indexes.iter().map(|idx| { + format!("CREATE INDEX idx_{}_{} ON \"{}\" (\"{}\")", + table_name, idx, table_name, idx) + })) + .collect::>(); + + (create_sql, all_indexes) }