fixing the row issue, missing sql stuff
This commit is contained in:
@@ -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,69 +25,45 @@ 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_index (related table indexed access)
|
||||
let indexed_data = current_data.clone();
|
||||
vm.register_fn("steel_get_column_with_index",
|
||||
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()
|
||||
))
|
||||
});
|
||||
|
||||
// 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()))
|
||||
})
|
||||
});
|
||||
// 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())
|
||||
}
|
||||
});
|
||||
|
||||
// Execute all expressions in script
|
||||
// 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| {
|
||||
ctx.steel_get_column_with_index(&table, index, &column)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
});
|
||||
|
||||
// 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" => {
|
||||
"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 {
|
||||
@@ -93,7 +75,4 @@ pub fn execute_script(
|
||||
}
|
||||
}
|
||||
Ok(Value::Strings(strings))
|
||||
},
|
||||
_ => Err(ExecutionError::UnsupportedType(target_type.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user