From 011fafc0ff7a466d2ec275ad8a7d7f91ecbc0cf5 Mon Sep 17 00:00:00 2001 From: filipriec Date: Tue, 17 Jun 2025 17:31:11 +0200 Subject: [PATCH] now working proper types --- server/src/shared/schema_qualifier.rs | 3 + .../handlers/post_table_definition.rs | 66 ++++++++++++++++--- 2 files changed, 60 insertions(+), 9 deletions(-) diff --git a/server/src/shared/schema_qualifier.rs b/server/src/shared/schema_qualifier.rs index 7977a79..ed82cc3 100644 --- a/server/src/shared/schema_qualifier.rs +++ b/server/src/shared/schema_qualifier.rs @@ -1,6 +1,9 @@ // src/shared/schema_qualifier.rs use sqlx::PgPool; use tonic::Status; + +// TODO in the future, remove database query on every request and implement caching for scalable +// solution with many data and requests /// Qualifies a table name by checking for its existence in the table_definitions table. /// This is the robust, "source of truth" approach. diff --git a/server/src/table_definition/handlers/post_table_definition.rs b/server/src/table_definition/handlers/post_table_definition.rs index 3c629a9..7b42182 100644 --- a/server/src/table_definition/handlers/post_table_definition.rs +++ b/server/src/table_definition/handlers/post_table_definition.rs @@ -1,19 +1,19 @@ use tonic::Status; use sqlx::{PgPool, Transaction, Postgres}; use serde_json::json; -use time::OffsetDateTime; use common::proto::multieko2::table_definition::{PostTableDefinitionRequest, TableDefinitionResponse}; const GENERATED_SCHEMA_NAME: &str = "gen"; const PREDEFINED_FIELD_TYPES: &[(&str, &str)] = &[ ("text", "TEXT"), - ("psc", "TEXT"), - ("phone", "VARCHAR(15)"), - ("address", "TEXT"), - ("email", "VARCHAR(255)"), + ("string", "TEXT"), ("boolean", "BOOLEAN"), ("timestamp", "TIMESTAMPTZ"), + ("time", "TIMESTAMPTZ"), + ("money", "NUMERIC(14, 4)"), + ("integer", "INTEGER"), + ("date", "DATE"), ]; fn is_valid_identifier(s: &str) -> bool { @@ -35,12 +35,60 @@ fn sanitize_identifier(s: &str) -> String { .to_lowercase() } -fn map_field_type(field_type: &str) -> Result<&str, Status> { +fn map_field_type(field_type: &str) -> Result { + let lower_field_type = field_type.to_lowercase(); + + // Special handling for "decimal(precision, scale)" + if lower_field_type.starts_with("decimal(") && lower_field_type.ends_with(')') { + // Extract the part inside the parentheses, e.g., "10, 2" + let args = lower_field_type + .strip_prefix("decimal(") + .and_then(|s| s.strip_suffix(')')) + .unwrap_or(""); // Should always succeed due to the checks above + + // Split into precision and scale parts + if let Some((p_str, s_str)) = args.split_once(',') { + // Parse precision, returning an error if it's not a valid number + let precision = p_str.trim().parse::().map_err(|_| { + Status::invalid_argument("Invalid precision in decimal type") + })?; + + // Parse scale, returning an error if it's not a valid number + let scale = s_str.trim().parse::().map_err(|_| { + Status::invalid_argument("Invalid scale in decimal type") + })?; + + // Add validation based on PostgreSQL rules + if precision < 1 { + return Err(Status::invalid_argument("Precision must be at least 1")); + } + if scale > precision { + return Err(Status::invalid_argument( + "Scale cannot be greater than precision", + )); + } + + // If everything is valid, build and return the NUMERIC type string + return Ok(format!("NUMERIC({}, {})", precision, scale)); + } else { + // The format was wrong, e.g., "decimal(10)" or "decimal()" + return Err(Status::invalid_argument( + "Invalid decimal format. Expected: decimal(precision, scale)", + )); + } + } + + // If not a decimal, fall back to the predefined list PREDEFINED_FIELD_TYPES .iter() - .find(|(key, _)| *key == field_type.to_lowercase().as_str()) - .map(|(_, sql_type)| *sql_type) - .ok_or_else(|| Status::invalid_argument(format!("Invalid field type: {}", field_type))) + .find(|(key, _)| *key == lower_field_type.as_str()) + .map(|(_, sql_type)| sql_type.to_string()) // Convert to an owned String + .ok_or_else(|| { + Status::invalid_argument(format!( + "Invalid field type: {}", + field_type + )) + }) } fn is_invalid_table_name(table_name: &str) -> bool {