gbserver/src/services/keywords/set.rs
Rodrigo Rodriguez (Pragmatismo) 0a009f9789
Some checks failed
GBCI / build (push) Failing after 30m58s
- SET SCHEDULE keyword - ON keyword
2025-08-16 18:13:03 -03:00

140 lines
4.4 KiB
Rust

use rhai::Dynamic;
use rhai::Engine;
use serde_json::{json, Value};
use sqlx::PgPool;
use std::error::Error;
use crate::services::state::AppState;
use crate::services::utils;
pub fn set_keyword(state: &AppState, engine: &mut Engine) {
let db = state.db_custom.clone();
engine
.register_custom_syntax(&["SET", "$expr$", ",", "$expr$", ",", "$expr$"], false, {
let db = db.clone();
move |context, inputs| {
let table_name = context.eval_expression_tree(&inputs[0])?;
let filter = context.eval_expression_tree(&inputs[1])?;
let updates = context.eval_expression_tree(&inputs[2])?;
let binding = db.as_ref().unwrap();
// Use the current async context instead of creating a new runtime
let binding2 = table_name.to_string();
let binding3 = filter.to_string();
let binding4 = updates.to_string();
let fut = execute_set(binding, &binding2, &binding3, &binding4);
// Use tokio::task::block_in_place + tokio::runtime::Handle::current().block_on
let result =
tokio::task::block_in_place(|| tokio::runtime::Handle::current().block_on(fut))
.map_err(|e| format!("DB error: {}", e))?;
if let Some(rows_affected) = result.get("rows_affected") {
Ok(Dynamic::from(rows_affected.as_i64().unwrap_or(0)))
} else {
Err("No rows affected".into())
}
}
})
.unwrap();
}
pub async fn execute_set(
pool: &PgPool,
table_str: &str,
filter_str: &str,
updates_str: &str,
) -> Result<Value, String> {
println!(
"Starting execute_set with table: {}, filter: {}, updates: {}",
table_str, filter_str, updates_str
);
// Parse updates with proper type handling
let (set_clause, update_values) = parse_updates(updates_str).map_err(|e| e.to_string())?;
let update_params_count = update_values.len();
// Parse filter with proper type handling
let (where_clause, filter_values) =
utils::parse_filter_with_offset(filter_str, update_params_count)
.map_err(|e| e.to_string())?;
let query = format!(
"UPDATE {} SET {} WHERE {}",
table_str, set_clause, where_clause
);
println!("Executing query: {}", query);
// Build query with proper parameter binding
let mut query = sqlx::query(&query);
// Bind update values
for value in update_values {
query = bind_value(query, value);
}
// Bind filter values
for value in filter_values {
query = bind_value(query, value);
}
let result = query.execute(pool).await.map_err(|e| {
eprintln!("SQL execution error: {}", e);
e.to_string()
})?;
Ok(json!({
"command": "set",
"table": table_str,
"filter": filter_str,
"updates": updates_str,
"rows_affected": result.rows_affected()
}))
}
fn bind_value<'q>(
query: sqlx::query::Query<'q, sqlx::Postgres, sqlx::postgres::PgArguments>,
value: String,
) -> sqlx::query::Query<'q, sqlx::Postgres, sqlx::postgres::PgArguments> {
if let Ok(int_val) = value.parse::<i64>() {
query.bind(int_val)
} else if let Ok(float_val) = value.parse::<f64>() {
query.bind(float_val)
} else if value.eq_ignore_ascii_case("true") {
query.bind(true)
} else if value.eq_ignore_ascii_case("false") {
query.bind(false)
} else {
query.bind(value)
}
}
// Parse updates without adding quotes
fn parse_updates(updates_str: &str) -> Result<(String, Vec<String>), Box<dyn Error>> {
let mut set_clauses = Vec::new();
let mut params = Vec::new();
for (i, update) in updates_str.split(',').enumerate() {
let parts: Vec<&str> = update.split('=').collect();
if parts.len() != 2 {
return Err("Invalid update format".into());
}
let column = parts[0].trim();
let value = parts[1].trim();
if !column
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '_')
{
return Err("Invalid column name".into());
}
set_clauses.push(format!("{} = ${}", column, i + 1));
params.push(value.to_string()); // Store raw value without quotes
}
Ok((set_clauses.join(", "), params))
}