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
|
||||||
// src/steel/server/execution.rs
|
|
||||||
use steel::steel_vm::engine::Engine;
|
use steel::steel_vm::engine::Engine;
|
||||||
use steel::steel_vm::register_fn::RegisterFn;
|
use steel::steel_vm::register_fn::RegisterFn;
|
||||||
use steel::rvals::SteelVal;
|
use steel::rvals::SteelVal;
|
||||||
use thiserror::Error;
|
use super::functions::{SteelContext, FunctionError};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::collections::HashMap;
|
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)]
|
#[derive(Debug, Error)]
|
||||||
pub enum ExecutionError {
|
pub enum ExecutionError {
|
||||||
@@ -19,69 +25,45 @@ pub enum ExecutionError {
|
|||||||
UnsupportedType(String),
|
UnsupportedType(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Value {
|
|
||||||
Strings(Vec<String>),
|
|
||||||
Numbers(Vec<i64>),
|
|
||||||
Mixed(Vec<SteelVal>),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn execute_script(
|
pub fn execute_script(
|
||||||
script: String,
|
script: String,
|
||||||
target_type: &str,
|
target_type: &str,
|
||||||
db_pool: Option<Arc<PgPool>>,
|
db_pool: Arc<PgPool>,
|
||||||
current_data: HashMap<String, String>,
|
context: SteelContext, // Contains row_data, profile_id, etc.
|
||||||
) -> Result<Value, ExecutionError> {
|
) -> Result<Value, ExecutionError> {
|
||||||
let mut vm = Engine::new();
|
let mut vm = Engine::new();
|
||||||
let handle = Handle::current();
|
let context = Arc::new(context);
|
||||||
|
|
||||||
// Clone data for closures
|
// Register steel_get_column with row context
|
||||||
let data = current_data.clone();
|
vm.register_fn("steel_get_column", {
|
||||||
|
let ctx = context.clone();
|
||||||
// Register steel_get_column (current table access)
|
move |table: String, column: String| {
|
||||||
vm.register_fn("steel_get_column", move |column: String| {
|
ctx.steel_get_column(&table, &column)
|
||||||
data.get(&column)
|
.map_err(|e| e.to_string())
|
||||||
.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()))
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 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)
|
let results = vm.compile_and_run_raw_program(script)
|
||||||
.map_err(|e| ExecutionError::RuntimeError(e.to_string()))?;
|
.map_err(|e| ExecutionError::RuntimeError(e.to_string()))?;
|
||||||
|
|
||||||
// Process results based on target type
|
// Convert results to target type
|
||||||
match 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();
|
let mut strings = Vec::new();
|
||||||
for result in results {
|
for result in results {
|
||||||
if let SteelVal::StringV(s) = result {
|
if let SteelVal::StringV(s) = result {
|
||||||
@@ -93,7 +75,4 @@ pub fn execute_script(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Value::Strings(strings))
|
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