big change in the schema, its profile names now and not gen
This commit is contained in:
@@ -5,8 +5,6 @@ use sqlx::{PgPool, Transaction, Postgres};
|
|||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use common::proto::multieko2::table_definition::{PostTableDefinitionRequest, TableDefinitionResponse};
|
use common::proto::multieko2::table_definition::{PostTableDefinitionRequest, TableDefinitionResponse};
|
||||||
|
|
||||||
const GENERATED_SCHEMA_NAME: &str = "gen";
|
|
||||||
|
|
||||||
const PREDEFINED_FIELD_TYPES: &[(&str, &str)] = &[
|
const PREDEFINED_FIELD_TYPES: &[(&str, &str)] = &[
|
||||||
("text", "TEXT"),
|
("text", "TEXT"),
|
||||||
("string", "TEXT"),
|
("string", "TEXT"),
|
||||||
@@ -100,6 +98,13 @@ fn is_invalid_table_name(table_name: &str) -> bool {
|
|||||||
table_name == "created_at"
|
table_name == "created_at"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_reserved_schema(schema_name: &str) -> bool {
|
||||||
|
let lower = schema_name.to_lowercase();
|
||||||
|
lower == "public" ||
|
||||||
|
lower == "information_schema" ||
|
||||||
|
lower.starts_with("pg_")
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn post_table_definition(
|
pub async fn post_table_definition(
|
||||||
db_pool: &PgPool,
|
db_pool: &PgPool,
|
||||||
request: PostTableDefinitionRequest,
|
request: PostTableDefinitionRequest,
|
||||||
@@ -108,8 +113,28 @@ pub async fn post_table_definition(
|
|||||||
return Err(Status::invalid_argument("Profile name cannot be empty"));
|
return Err(Status::invalid_argument("Profile name cannot be empty"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply same sanitization rules as table names
|
||||||
|
let sanitized_profile_name = sanitize_identifier(&request.profile_name);
|
||||||
|
|
||||||
|
// Add validation to prevent reserved schemas
|
||||||
|
if is_reserved_schema(&sanitized_profile_name) {
|
||||||
|
return Err(Status::invalid_argument("Profile name is reserved and cannot be used"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_valid_identifier(&sanitized_profile_name) {
|
||||||
|
return Err(Status::invalid_argument("Invalid profile name"));
|
||||||
|
}
|
||||||
|
|
||||||
const MAX_IDENTIFIER_LENGTH: usize = 63;
|
const MAX_IDENTIFIER_LENGTH: usize = 63;
|
||||||
|
|
||||||
|
if sanitized_profile_name.len() > MAX_IDENTIFIER_LENGTH {
|
||||||
|
return Err(Status::invalid_argument(format!(
|
||||||
|
"Profile name '{}' exceeds the {} character limit.",
|
||||||
|
sanitized_profile_name,
|
||||||
|
MAX_IDENTIFIER_LENGTH
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
let base_name = sanitize_table_name(&request.table_name);
|
let base_name = sanitize_table_name(&request.table_name);
|
||||||
if base_name.len() > MAX_IDENTIFIER_LENGTH {
|
if base_name.len() > MAX_IDENTIFIER_LENGTH {
|
||||||
return Err(Status::invalid_argument(format!(
|
return Err(Status::invalid_argument(format!(
|
||||||
@@ -140,7 +165,7 @@ pub async fn post_table_definition(
|
|||||||
let mut tx = db_pool.begin().await
|
let mut tx = db_pool.begin().await
|
||||||
.map_err(|e| Status::internal(format!("Failed to start transaction: {}", e)))?;
|
.map_err(|e| Status::internal(format!("Failed to start transaction: {}", e)))?;
|
||||||
|
|
||||||
match execute_table_definition(&mut tx, request, base_name).await {
|
match execute_table_definition(&mut tx, request, base_name, sanitized_profile_name).await {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
tx.commit().await
|
tx.commit().await
|
||||||
.map_err(|e| Status::internal(format!("Failed to commit transaction: {}", e)))?;
|
.map_err(|e| Status::internal(format!("Failed to commit transaction: {}", e)))?;
|
||||||
@@ -157,6 +182,7 @@ async fn execute_table_definition(
|
|||||||
tx: &mut Transaction<'_, Postgres>,
|
tx: &mut Transaction<'_, Postgres>,
|
||||||
mut request: PostTableDefinitionRequest,
|
mut request: PostTableDefinitionRequest,
|
||||||
table_name: String,
|
table_name: String,
|
||||||
|
profile_name: String,
|
||||||
) -> Result<TableDefinitionResponse, Status> {
|
) -> Result<TableDefinitionResponse, Status> {
|
||||||
let profile = sqlx::query!(
|
let profile = sqlx::query!(
|
||||||
"INSERT INTO profiles (name) VALUES ($1)
|
"INSERT INTO profiles (name) VALUES ($1)
|
||||||
@@ -168,6 +194,12 @@ async fn execute_table_definition(
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| Status::internal(format!("Profile error: {}", e)))?;
|
.map_err(|e| Status::internal(format!("Profile error: {}", e)))?;
|
||||||
|
|
||||||
|
// Create schema if it doesn't exist
|
||||||
|
sqlx::query(&format!("CREATE SCHEMA IF NOT EXISTS \"{}\"", profile_name))
|
||||||
|
.execute(&mut **tx)
|
||||||
|
.await
|
||||||
|
.map_err(|e| Status::internal(format!("Schema creation failed: {}", e)))?;
|
||||||
|
|
||||||
let mut links = Vec::new();
|
let mut links = Vec::new();
|
||||||
for link in request.links.drain(..) {
|
for link in request.links.drain(..) {
|
||||||
let linked_table = sqlx::query!(
|
let linked_table = sqlx::query!(
|
||||||
@@ -212,7 +244,7 @@ async fn execute_table_definition(
|
|||||||
indexes.push(idx_name);
|
indexes.push(idx_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (create_sql, index_sql) = generate_table_sql(tx, &table_name, &columns, &indexes, &links).await?;
|
let (create_sql, index_sql) = generate_table_sql(tx, &profile_name, &table_name, &columns, &indexes, &links).await?;
|
||||||
|
|
||||||
let table_def = sqlx::query!(
|
let table_def = sqlx::query!(
|
||||||
r#"INSERT INTO table_definitions
|
r#"INSERT INTO table_definitions
|
||||||
@@ -269,12 +301,13 @@ async fn execute_table_definition(
|
|||||||
|
|
||||||
async fn generate_table_sql(
|
async fn generate_table_sql(
|
||||||
tx: &mut Transaction<'_, Postgres>,
|
tx: &mut Transaction<'_, Postgres>,
|
||||||
|
profile_name: &str,
|
||||||
table_name: &str,
|
table_name: &str,
|
||||||
columns: &[String],
|
columns: &[String],
|
||||||
indexes: &[String],
|
indexes: &[String],
|
||||||
links: &[(i64, bool)],
|
links: &[(i64, bool)],
|
||||||
) -> Result<(String, Vec<String>), Status> {
|
) -> Result<(String, Vec<String>), Status> {
|
||||||
let qualified_table = format!("{}.\"{}\"", GENERATED_SCHEMA_NAME, table_name);
|
let qualified_table = format!("{}.\"{}\"", profile_name, table_name);
|
||||||
|
|
||||||
let mut system_columns = vec![
|
let mut system_columns = vec![
|
||||||
"id BIGSERIAL PRIMARY KEY".to_string(),
|
"id BIGSERIAL PRIMARY KEY".to_string(),
|
||||||
@@ -283,7 +316,7 @@ async fn generate_table_sql(
|
|||||||
|
|
||||||
for (linked_id, required) in links {
|
for (linked_id, required) in links {
|
||||||
let linked_table = get_table_name_by_id(tx, *linked_id).await?;
|
let linked_table = get_table_name_by_id(tx, *linked_id).await?;
|
||||||
let qualified_linked_table = format!("{}.\"{}\"", GENERATED_SCHEMA_NAME, linked_table);
|
let qualified_linked_table = format!("{}.\"{}\"", profile_name, linked_table);
|
||||||
let base_name = linked_table.split_once('_')
|
let base_name = linked_table.split_once('_')
|
||||||
.map(|(_, rest)| rest)
|
.map(|(_, rest)| rest)
|
||||||
.unwrap_or(&linked_table)
|
.unwrap_or(&linked_table)
|
||||||
|
|||||||
Reference in New Issue
Block a user