From 8ebe74484c4505ec3be1ea45aa166936b0b73623 Mon Sep 17 00:00:00 2001 From: filipriec Date: Tue, 17 Jun 2025 11:45:55 +0200 Subject: [PATCH] now not creating tables with the year_ prefix and living in the gen schema by default --- server/src/shared/schema_qualifier.rs | 66 +++++++++++-------- .../handlers/post_table_definition.rs | 6 +- .../tables_data/handlers/delete_table_data.rs | 7 +- .../tables_data/handlers/get_table_data.rs | 7 +- .../handlers/get_table_data_by_position.rs | 7 +- .../handlers/get_table_data_count.rs | 13 ++-- .../tables_data/handlers/post_table_data.rs | 10 ++- .../tables_data/handlers/put_table_data.rs | 7 +- 8 files changed, 82 insertions(+), 41 deletions(-) diff --git a/server/src/shared/schema_qualifier.rs b/server/src/shared/schema_qualifier.rs index dc61740..7977a79 100644 --- a/server/src/shared/schema_qualifier.rs +++ b/server/src/shared/schema_qualifier.rs @@ -1,34 +1,48 @@ -// src/shared/schema_qualifier.rs + // src/shared/schema_qualifier.rs +use sqlx::PgPool; use tonic::Status; - -/// Qualifies table names with the appropriate schema -/// + +/// Qualifies a table name by checking for its existence in the table_definitions table. +/// This is the robust, "source of truth" approach. +/// /// Rules: -/// - Tables created via PostTableDefinition (dynamically created tables) are in 'gen' schema -/// - System tables (like users, profiles) remain in 'public' schema -pub fn qualify_table_name(table_name: &str) -> String { - // Check if table matches the pattern of dynamically created tables (e.g., 2025_something) - if table_name.starts_with(|c: char| c.is_ascii_digit()) && table_name.contains('_') { - format!("gen.\"{}\"", table_name) +/// - If a table is found in `table_definitions`, it is qualified with the 'gen' schema. +/// - Otherwise, it is assumed to be a system table in the 'public' schema. +pub async fn qualify_table_name( + db_pool: &PgPool, + profile_name: &str, + table_name: &str, +) -> Result { + // Check if a definition exists for this table in the given profile. + let definition_exists = sqlx::query!( + r#"SELECT EXISTS ( + SELECT 1 FROM table_definitions td + JOIN profiles p ON td.profile_id = p.id + WHERE p.name = $1 AND td.table_name = $2 + )"#, + profile_name, + table_name + ) + .fetch_one(db_pool) + .await + .map_err(|e| Status::internal(format!("Schema lookup failed: {}", e)))? + .exists + .unwrap_or(false); + + if definition_exists { + // It's a user-defined table, so it lives in 'gen'. + Ok(format!("gen.\"{}\"", table_name)) } else { - format!("\"{}\"", table_name) + // It's not a user-defined table, so it must be a system table in 'public'. + Ok(format!("\"{}\"", table_name)) } } /// Qualifies table names for data operations -pub fn qualify_table_name_for_data(table_name: &str) -> Result { - Ok(qualify_table_name(table_name)) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_qualify_table_name() { - assert_eq!(qualify_table_name("2025_test_schema3"), "gen.\"2025_test_schema3\""); - assert_eq!(qualify_table_name("users"), "\"users\""); - assert_eq!(qualify_table_name("profiles"), "\"profiles\""); - assert_eq!(qualify_table_name("adresar"), "\"adresar\""); - } +pub async fn qualify_table_name_for_data( + db_pool: &PgPool, + profile_name: &str, + table_name: &str, +) -> Result { + qualify_table_name(db_pool, profile_name, table_name).await } diff --git a/server/src/table_definition/handlers/post_table_definition.rs b/server/src/table_definition/handlers/post_table_definition.rs index 22083be..3c629a9 100644 --- a/server/src/table_definition/handlers/post_table_definition.rs +++ b/server/src/table_definition/handlers/post_table_definition.rs @@ -24,11 +24,9 @@ fn is_valid_identifier(s: &str) -> bool { } fn sanitize_table_name(s: &str) -> String { - let year = OffsetDateTime::now_utc().year(); - let cleaned = s.replace(|c: char| !c.is_ascii_alphanumeric() && c != '_', "") + s.replace(|c: char| !c.is_ascii_alphanumeric() && c != '_', "") .trim() - .to_lowercase(); - format!("{}_{}", year, cleaned) + .to_lowercase() } fn sanitize_identifier(s: &str) -> String { diff --git a/server/src/tables_data/handlers/delete_table_data.rs b/server/src/tables_data/handlers/delete_table_data.rs index 7a2a00c..24cfec1 100644 --- a/server/src/tables_data/handlers/delete_table_data.rs +++ b/server/src/tables_data/handlers/delete_table_data.rs @@ -38,7 +38,12 @@ pub async fn delete_table_data( } // Qualify table name with schema - let qualified_table = qualify_table_name_for_data(&request.table_name)?; + let qualified_table = qualify_table_name_for_data( + db_pool, + &request.profile_name, + &request.table_name, + ) + .await?; // Perform soft delete using qualified table name let query = format!( diff --git a/server/src/tables_data/handlers/get_table_data.rs b/server/src/tables_data/handlers/get_table_data.rs index ab9d0c3..dc0eccf 100644 --- a/server/src/tables_data/handlers/get_table_data.rs +++ b/server/src/tables_data/handlers/get_table_data.rs @@ -88,7 +88,12 @@ pub async fn get_table_data( // --- END OF FIX --- // Qualify table name with schema - let qualified_table = qualify_table_name_for_data(&table_name)?; + let qualified_table = qualify_table_name_for_data( + db_pool, + &profile_name, + &table_name, + ) + .await?; let sql = format!( "SELECT {} FROM {} WHERE id = $1 AND deleted = false", diff --git a/server/src/tables_data/handlers/get_table_data_by_position.rs b/server/src/tables_data/handlers/get_table_data_by_position.rs index a8679bd..18c78f3 100644 --- a/server/src/tables_data/handlers/get_table_data_by_position.rs +++ b/server/src/tables_data/handlers/get_table_data_by_position.rs @@ -45,7 +45,12 @@ pub async fn get_table_data_by_position( } // Qualify table name with schema - let qualified_table = qualify_table_name_for_data(&table_name)?; + let qualified_table = qualify_table_name_for_data( + db_pool, + &profile_name, + &table_name, + ) + .await?; let id_result = sqlx::query_scalar( &format!( diff --git a/server/src/tables_data/handlers/get_table_data_count.rs b/server/src/tables_data/handlers/get_table_data_count.rs index 2bd0f17..d1d6e2b 100644 --- a/server/src/tables_data/handlers/get_table_data_count.rs +++ b/server/src/tables_data/handlers/get_table_data_count.rs @@ -47,7 +47,12 @@ pub async fn get_table_data_count( } // 2. QUALIFY THE TABLE NAME using the imported function - let qualified_table_name = qualify_table_name_for_data(&request.table_name)?; + let qualified_table = qualify_table_name_for_data( + db_pool, + &request.profile_name, + &request.table_name, + ) + .await?; // 3. USE THE QUALIFIED NAME in the SQL query let query_sql = format!( @@ -56,7 +61,7 @@ pub async fn get_table_data_count( FROM {} WHERE deleted = FALSE "#, - qualified_table_name // Use the schema-qualified name here + qualified_table ); // The rest of the logic remains largely the same, but error messages can be more specific. @@ -81,14 +86,14 @@ pub async fn get_table_data_count( // even though it was defined in table_definitions. This is an inconsistency. return Err(Status::internal(format!( "Table '{}' is defined but does not physically exist in the database as {}.", - request.table_name, qualified_table_name + request.table_name, qualified_table ))); } } // For other errors, provide a general message. Err(Status::internal(format!( "Count query failed for table {}: {}", - qualified_table_name, e + qualified_table, e ))) } } diff --git a/server/src/tables_data/handlers/post_table_data.rs b/server/src/tables_data/handlers/post_table_data.rs index 11c299f..fd299ae 100644 --- a/server/src/tables_data/handlers/post_table_data.rs +++ b/server/src/tables_data/handlers/post_table_data.rs @@ -7,8 +7,7 @@ use chrono::{DateTime, Utc}; use common::proto::multieko2::tables_data::{PostTableDataRequest, PostTableDataResponse}; use std::collections::HashMap; use std::sync::Arc; -use crate::shared::schema_qualifier::qualify_table_name_for_data; -use prost_types::value::Kind; // NEW: Import the Kind enum +use prost_types::value::Kind; use crate::steel::server::execution::{self, Value}; use crate::steel::server::functions::SteelContext; @@ -261,7 +260,12 @@ pub async fn post_table_data( } // Qualify table name with schema - let qualified_table = qualify_table_name_for_data(&table_name)?; + let qualified_table = crate::shared::schema_qualifier::qualify_table_name_for_data( + db_pool, + &profile_name, + &table_name, + ) + .await?; let sql = format!( "INSERT INTO {} ({}) VALUES ({}) RETURNING id", diff --git a/server/src/tables_data/handlers/put_table_data.rs b/server/src/tables_data/handlers/put_table_data.rs index 8f61338..391800a 100644 --- a/server/src/tables_data/handlers/put_table_data.rs +++ b/server/src/tables_data/handlers/put_table_data.rs @@ -161,7 +161,12 @@ pub async fn put_table_data( params.add(record_id) .map_err(|e| Status::internal(format!("Failed to add record_id parameter: {}", e)))?; - let qualified_table = qualify_table_name_for_data(&table_name)?; + let qualified_table = qualify_table_name_for_data( + db_pool, + &profile_name, + &table_name, + ) + .await?; let set_clause = set_clauses.join(", "); let sql = format!( "UPDATE {} SET {} WHERE id = ${} AND deleted = FALSE RETURNING id",