fixing the row issue, missing sql stuff

This commit is contained in:
filipriec
2025-03-16 13:39:46 +01:00
parent 50487c1269
commit b731e4ef2c
2 changed files with 138 additions and 65 deletions

View File

@@ -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<String>),
Numbers(Vec<i64>),
Mixed(Vec<SteelVal>),
}
#[derive(Debug, Error)]
pub enum ExecutionError {
@@ -19,81 +25,54 @@ pub enum ExecutionError {
UnsupportedType(String),
}
#[derive(Debug)]
pub enum Value {
Strings(Vec<String>),
Numbers(Vec<i64>),
Mixed(Vec<SteelVal>),
}
pub fn execute_script(
script: String,
target_type: &str,
db_pool: Option<Arc<PgPool>>,
current_data: HashMap<String, String>,
db_pool: Arc<PgPool>,
context: SteelContext, // Contains row_data, profile_id, etc.
) -> Result<Value, ExecutionError> {
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<SteelVal>) -> Result<Value, ExecutionError> {
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))
}

View File

@@ -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<String, String>,
pub db_pool: Arc<PgPool>,
}
impl SteelContext {
pub async fn get_related_table_name(&self, base_name: &str) -> Result<String, FunctionError> {
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<SteelVal, SteelVal> {
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::<i64>().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<SteelVal, SteelVal> {
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()))
}
}
}