From b731e4ef2c0e7cb5b373d7291fefba40bc6ac1c6 Mon Sep 17 00:00:00 2001 From: filipriec Date: Sun, 16 Mar 2025 13:39:46 +0100 Subject: [PATCH] fixing the row issue, missing sql stuff --- server/src/steel/server/execution.rs | 109 +++++++++++---------------- server/src/steel/server/functions.rs | 94 +++++++++++++++++++++++ 2 files changed, 138 insertions(+), 65 deletions(-) diff --git a/server/src/steel/server/execution.rs b/server/src/steel/server/execution.rs index 713eb8d..6a45b45 100644 --- a/server/src/steel/server/execution.rs +++ b/server/src/steel/server/execution.rs @@ -1,13 +1,19 @@ // src/steel/server/execution.rs -// src/steel/server/execution.rs use steel::steel_vm::engine::Engine; use steel::steel_vm::register_fn::RegisterFn; use steel::rvals::SteelVal; -use thiserror::Error; +use super::functions::{SteelContext, FunctionError}; use sqlx::PgPool; use std::sync::Arc; use std::collections::HashMap; -use tokio::runtime::Handle; +use thiserror::Error; + +#[derive(Debug)] +pub enum Value { + Strings(Vec), + Numbers(Vec), + Mixed(Vec), +} #[derive(Debug, Error)] pub enum ExecutionError { @@ -19,81 +25,54 @@ pub enum ExecutionError { UnsupportedType(String), } -#[derive(Debug)] -pub enum Value { - Strings(Vec), - Numbers(Vec), - Mixed(Vec), -} - pub fn execute_script( script: String, target_type: &str, - db_pool: Option>, - current_data: HashMap, + db_pool: Arc, + context: SteelContext, // Contains row_data, profile_id, etc. ) -> Result { let mut vm = Engine::new(); - let handle = Handle::current(); + let context = Arc::new(context); - // Clone data for closures - let data = current_data.clone(); - - // Register steel_get_column (current table access) - vm.register_fn("steel_get_column", move |column: String| { - data.get(&column) - .map(|s| SteelVal::StringV(s.clone().into())) - .ok_or_else(|| SteelVal::StringV(format!("Column {} not found", column).into())) + // Register steel_get_column with row context + vm.register_fn("steel_get_column", { + let ctx = context.clone(); + move |table: String, column: String| { + ctx.steel_get_column(&table, &column) + .map_err(|e| e.to_string()) + } }); - // Register steel_get_column_with_index (related table indexed access) - let indexed_data = current_data.clone(); - vm.register_fn("steel_get_column_with_index", + // Register steel_get_column_with_index + vm.register_fn("steel_get_column_with_index", { + let ctx = context.clone(); move |table: String, index: i64, column: String| { - let key = format!("{}.{}", table, column); - indexed_data.get(&key) - .and_then(|s| s.split(',').nth(index as usize)) - .map(|v| SteelVal::StringV(v.trim().to_string().into())) - .ok_or_else(|| SteelVal::StringV( - format!("Index {} not found for {}.{}", index, table, column).into() - )) - }); + ctx.steel_get_column_with_index(&table, index, &column) + .map_err(|e| e.to_string()) + } + }); - // Register SQL function if pool exists - if let Some(pool) = db_pool { - let pool_clone = pool.clone(); - vm.register_fn("steel_query_sql", move |query: String| { - let pool = pool_clone.clone(); - let handle = handle.clone(); - - handle.block_on(async move { - sqlx::query_scalar::<_, i64>(&query) - .fetch_one(&*pool) - .await - .map(|v| SteelVal::IntV(v as isize)) - .map_err(|e| SteelVal::StringV(format!("SQL error: {}", e).into())) - }) - }); - } - - // Execute all expressions in script + // Execute script and process results let results = vm.compile_and_run_raw_program(script) .map_err(|e| ExecutionError::RuntimeError(e.to_string()))?; - // Process results based on target type + // Convert results to target type match target_type { - "STRINGS" => { - let mut strings = Vec::new(); - for result in results { - if let SteelVal::StringV(s) = result { - strings.push(s.to_string()); - } else { - return Err(ExecutionError::TypeConversionError( - format!("Expected string, got {:?}", result) - )); - } - } - Ok(Value::Strings(strings)) - }, - _ => Err(ExecutionError::UnsupportedType(target_type.to_string())), + "STRINGS" => process_string_results(results), + _ => Err(ExecutionError::UnsupportedType(target_type.into())) } } + +fn process_string_results(results: Vec) -> Result { + let mut strings = Vec::new(); + for result in results { + if let SteelVal::StringV(s) = result { + strings.push(s.to_string()); + } else { + return Err(ExecutionError::TypeConversionError( + format!("Expected string, got {:?}", result) + )); + } + } + Ok(Value::Strings(strings)) +} diff --git a/server/src/steel/server/functions.rs b/server/src/steel/server/functions.rs index e69de29..adceef1 100644 --- a/server/src/steel/server/functions.rs +++ b/server/src/steel/server/functions.rs @@ -0,0 +1,94 @@ +// src/steel/server/functions.rs +use steel::rvals::SteelVal; +use sqlx::{PgPool, FromRow}; +use std::collections::HashMap; +use std::sync::Arc; +use thiserror::Error; +use serde_json::Value; +use sqlx::postgres::PgRow; + +#[derive(Debug, Error)] +pub enum FunctionError { + #[error("Column not found: {0}")] + ColumnNotFound(String), + #[error("Foreign key not found: {0}")] + ForeignKeyNotFound(String), + #[error("Table not found: {0}")] + TableNotFound(String), + #[error("Database error: {0}")] + DatabaseError(String), +} + +pub struct SteelContext { + pub current_table: String, + pub profile_id: i64, + pub row_data: HashMap, + pub db_pool: Arc, +} + +impl SteelContext { + pub async fn get_related_table_name(&self, base_name: &str) -> Result { + let table_def = sqlx::query!( + r#"SELECT table_name FROM table_definitions + WHERE profile_id = $1 AND table_name LIKE $2"#, + self.profile_id, + format!("%_{}", base_name) + ) + .fetch_optional(&*self.db_pool) + .await + .map_err(|e| FunctionError::DatabaseError(e.to_string()))? + .ok_or_else(|| FunctionError::TableNotFound(base_name.to_string()))?; + + Ok(table_def.table_name) + } + + pub fn steel_get_column(&self, table: &str, column: &str) -> Result { + if table == self.current_table { + return self.row_data.get(column) + .map(|v| SteelVal::StringV(v.clone().into())) + .ok_or_else(|| SteelVal::StringV(format!("Column {} not found", column).into())); + } + + let base_name = table.split_once('_') + .map(|(_, rest)| rest) + .unwrap_or(table); + + let fk_column = format!("{}_id", base_name); + let fk_value = self.row_data.get(&fk_column) + .ok_or_else(|| SteelVal::StringV(format!("Foreign key {} not found", fk_column).into()))?; + + // Convert to async block for database access + let result = tokio::runtime::Runtime::new().unwrap().block_on(async { + let actual_table = self.get_related_table_name(base_name).await + .map_err(|e| SteelVal::StringV(e.to_string().into()))?; + + sqlx::query_scalar::<_, String>( + &format!("SELECT {} FROM {} WHERE id = $1", column, actual_table) + ) + .bind(fk_value.parse::().map_err(|_| + SteelVal::StringV("Invalid foreign key format".into()))?) + .fetch_one(&*self.db_pool) + .await + .map_err(|e| SteelVal::StringV(e.to_string().into())) + }); + + result.map(|v| SteelVal::StringV(v.into())) + } + + pub fn steel_get_column_with_index( + &self, + table: &str, + index: i64, + column: &str + ) -> Result { + let value = self.steel_get_column(table, column)?; + if let SteelVal::StringV(s) = value { + let parts: Vec<_> = s.split(',').collect(); + parts.get(index as usize) + .map(|v| SteelVal::StringV(v.trim().into())) + .ok_or_else(|| SteelVal::StringV("Index out of bounds".into())) + } else { + Err(SteelVal::StringV("Expected comma-separated string".into())) + } + } +}