gbserver/src/services/keywords/set.rs

141 lines
4.4 KiB
Rust
Raw Normal View History

2025-07-20 00:03:37 -03:00
use rhai::Dynamic;
use rhai::Engine;
2025-08-02 00:07:54 -03:00
use serde_json::{json, Value};
2025-08-16 18:13:03 -03:00
use sqlx::PgPool;
2025-08-02 00:07:54 -03:00
use std::error::Error;
2025-07-20 00:03:37 -03:00
use crate::services::state::AppState;
2025-08-02 00:07:54 -03:00
use crate::services::utils;
2025-07-20 00:03:37 -03:00
2025-08-02 00:07:54 -03:00
pub fn set_keyword(state: &AppState, engine: &mut Engine) {
let db = state.db_custom.clone();
2025-07-20 00:03:37 -03:00
engine
2025-08-02 00:07:54 -03:00
.register_custom_syntax(&["SET", "$expr$", ",", "$expr$", ",", "$expr$"], false, {
let db = db.clone();
2025-07-20 00:03:37 -03:00
2025-08-02 00:07:54 -03:00
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
);
2025-08-03 17:34:59 -03:00
// 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();
2025-08-02 00:49:33 -03:00
2025-08-03 17:34:59 -03:00
// Parse filter with proper type handling
2025-08-16 18:13:03 -03:00
let (where_clause, filter_values) =
utils::parse_filter_with_offset(filter_str, update_params_count)
.map_err(|e| e.to_string())?;
2025-08-02 00:07:54 -03:00
let query = format!(
"UPDATE {} SET {} WHERE {}",
table_str, set_clause, where_clause
);
println!("Executing query: {}", query);
2025-08-03 17:34:59 -03:00
// Build query with proper parameter binding
let mut query = sqlx::query(&query);
2025-08-16 18:13:03 -03:00
2025-08-03 17:34:59 -03:00
// Bind update values
for value in update_values {
query = bind_value(query, value);
}
2025-08-16 18:13:03 -03:00
2025-08-03 17:34:59 -03:00
// Bind filter values
for value in filter_values {
query = bind_value(query, value);
2025-08-02 00:49:33 -03:00
}
2025-08-16 18:13:03 -03:00
let result = query.execute(pool).await.map_err(|e| {
eprintln!("SQL execution error: {}", e);
e.to_string()
})?;
2025-08-02 00:07:54 -03:00
Ok(json!({
"command": "set",
"table": table_str,
"filter": filter_str,
"updates": updates_str,
"rows_affected": result.rows_affected()
}))
2025-07-20 00:03:37 -03:00
}
2025-08-02 00:07:54 -03:00
2025-08-16 18:13:03 -03:00
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> {
2025-08-03 17:34:59 -03:00
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
2025-08-02 00:07:54 -03:00
fn parse_updates(updates_str: &str) -> Result<(String, Vec<String>), Box<dyn Error>> {
let mut set_clauses = Vec::new();
let mut params = Vec::new();
2025-08-16 18:13:03 -03:00
2025-08-02 00:49:33 -03:00
for (i, update) in updates_str.split(',').enumerate() {
2025-08-02 00:07:54 -03:00
let parts: Vec<&str> = update.split('=').collect();
if parts.len() != 2 {
2025-08-03 17:34:59 -03:00
return Err("Invalid update format".into());
2025-08-02 00:07:54 -03:00
}
let column = parts[0].trim();
let value = parts[1].trim();
2025-08-16 18:13:03 -03:00
if !column
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '_')
{
2025-08-03 17:34:59 -03:00
return Err("Invalid column name".into());
2025-08-02 00:07:54 -03:00
}
2025-08-02 00:49:33 -03:00
set_clauses.push(format!("{} = ${}", column, i + 1));
2025-08-03 17:34:59 -03:00
params.push(value.to_string()); // Store raw value without quotes
2025-08-02 00:07:54 -03:00
}
Ok((set_clauses.join(", "), params))
2025-08-16 18:13:03 -03:00
}