steel script working perfectly well
This commit is contained in:
@@ -28,11 +28,8 @@ pub fn execute_script(
|
|||||||
script: String,
|
script: String,
|
||||||
target_type: &str,
|
target_type: &str,
|
||||||
_db_pool: Arc<PgPool>, // Passed to the SteelContext
|
_db_pool: Arc<PgPool>, // Passed to the SteelContext
|
||||||
mut context: SteelContext, // Make mutable to inject runtime
|
context: SteelContext,
|
||||||
) -> Result<Value, ExecutionError> {
|
) -> Result<Value, ExecutionError> {
|
||||||
// Inject the current runtime handle
|
|
||||||
context.runtime = tokio::runtime::Handle::current();
|
|
||||||
|
|
||||||
let mut vm = Engine::new();
|
let mut vm = Engine::new();
|
||||||
let context = Arc::new(context);
|
let context = Arc::new(context);
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ use std::collections::HashMap;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
use tokio::runtime::Handle;
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum FunctionError {
|
pub enum FunctionError {
|
||||||
@@ -25,7 +24,6 @@ pub struct SteelContext {
|
|||||||
pub profile_id: i64,
|
pub profile_id: i64,
|
||||||
pub row_data: HashMap<String, String>,
|
pub row_data: HashMap<String, String>,
|
||||||
pub db_pool: Arc<PgPool>,
|
pub db_pool: Arc<PgPool>,
|
||||||
pub runtime: Handle, // Add runtime handle
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SteelContext {
|
impl SteelContext {
|
||||||
@@ -59,19 +57,23 @@ impl SteelContext {
|
|||||||
let fk_value = self.row_data.get(&fk_column)
|
let fk_value = self.row_data.get(&fk_column)
|
||||||
.ok_or_else(|| SteelVal::StringV(format!("Foreign key {} not found", fk_column).into()))?;
|
.ok_or_else(|| SteelVal::StringV(format!("Foreign key {} not found", fk_column).into()))?;
|
||||||
|
|
||||||
// Use the injected runtime handle
|
// Use `tokio::task::block_in_place` to safely block the thread
|
||||||
let result = self.runtime.block_on(async {
|
let result = tokio::task::block_in_place(|| {
|
||||||
let actual_table = self.get_related_table_name(base_name).await
|
let handle = tokio::runtime::Handle::current();
|
||||||
.map_err(|e| SteelVal::StringV(e.to_string().into()))?;
|
handle.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>(
|
// Add quotes around the table name
|
||||||
&format!("SELECT {} FROM {} WHERE id = $1", column, actual_table)
|
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()))?)
|
.bind(fk_value.parse::<i64>().map_err(|_|
|
||||||
.fetch_one(&*self.db_pool)
|
SteelVal::StringV("Invalid foreign key format".into()))?)
|
||||||
.await
|
.fetch_one(&*self.db_pool)
|
||||||
.map_err(|e| SteelVal::StringV(e.to_string().into()))
|
.await
|
||||||
|
.map_err(|e| SteelVal::StringV(e.to_string().into()))
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
result.map(|v| SteelVal::StringV(v.into()))
|
result.map(|v| SteelVal::StringV(v.into()))
|
||||||
@@ -103,21 +105,26 @@ impl SteelContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let pool = self.db_pool.clone();
|
let pool = self.db_pool.clone();
|
||||||
let result = self.runtime.block_on(async {
|
|
||||||
// Execute and get first column of all rows as strings
|
|
||||||
let rows = sqlx::query(query)
|
|
||||||
.fetch_all(&*pool)
|
|
||||||
.await
|
|
||||||
.map_err(|e| SteelVal::StringV(e.to_string().into()))?;
|
|
||||||
|
|
||||||
let mut results = Vec::new();
|
// Use `tokio::task::block_in_place` to safely block the thread
|
||||||
for row in rows {
|
let result = tokio::task::block_in_place(|| {
|
||||||
let val: String = row.try_get(0)
|
let handle = tokio::runtime::Handle::current();
|
||||||
|
handle.block_on(async {
|
||||||
|
// Execute and get first column of all rows as strings
|
||||||
|
let rows = sqlx::query(query)
|
||||||
|
.fetch_all(&*pool)
|
||||||
|
.await
|
||||||
.map_err(|e| SteelVal::StringV(e.to_string().into()))?;
|
.map_err(|e| SteelVal::StringV(e.to_string().into()))?;
|
||||||
results.push(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(results.join(","))
|
let mut results = Vec::new();
|
||||||
|
for row in rows {
|
||||||
|
let val: String = row.try_get(0)
|
||||||
|
.map_err(|e| SteelVal::StringV(e.to_string().into()))?;
|
||||||
|
results.push(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(results.join(","))
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
result.map(|s| SteelVal::StringV(s.into()))
|
result.map(|s| SteelVal::StringV(s.into()))
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ use crate::steel::server::syntax_parser::SyntaxParser;
|
|||||||
|
|
||||||
const SYSTEM_COLUMNS: &[&str] = &["id", "deleted", "created_at"];
|
const SYSTEM_COLUMNS: &[&str] = &["id", "deleted", "created_at"];
|
||||||
|
|
||||||
|
/// Validates the target column and ensures it is not a system column.
|
||||||
|
/// Returns the column type if valid.
|
||||||
fn validate_target_column(
|
fn validate_target_column(
|
||||||
table_name: &str,
|
table_name: &str,
|
||||||
target: &str,
|
target: &str,
|
||||||
@@ -16,9 +18,11 @@ fn validate_target_column(
|
|||||||
return Err(format!("Cannot override system column: {}", target));
|
return Err(format!("Cannot override system column: {}", target));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse the columns JSON into a vector of strings
|
||||||
let columns: Vec<String> = serde_json::from_value(table_columns.clone())
|
let columns: Vec<String> = serde_json::from_value(table_columns.clone())
|
||||||
.map_err(|e| format!("Invalid column data: {}", e))?;
|
.map_err(|e| format!("Invalid column data: {}", e))?;
|
||||||
|
|
||||||
|
// Extract column names and types
|
||||||
let column_info: Vec<(&str, &str)> = columns
|
let column_info: Vec<(&str, &str)> = columns
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|c| {
|
.filter_map(|c| {
|
||||||
@@ -29,19 +33,20 @@ fn validate_target_column(
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let column_type = column_info
|
// Find the target column and return its type
|
||||||
|
column_info
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(name, _)| *name == target)
|
.find(|(name, _)| *name == target)
|
||||||
.map(|(_, dt)| *dt)
|
.map(|(_, dt)| dt.to_string())
|
||||||
.ok_or_else(|| format!("Target column {} not defined in table {}", target, table_name))?;
|
.ok_or_else(|| format!("Target column '{}' not defined in table '{}'", target, table_name))
|
||||||
|
|
||||||
Ok(column_type.to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handles the creation of a new table script.
|
||||||
pub async fn post_table_script(
|
pub async fn post_table_script(
|
||||||
db_pool: &PgPool,
|
db_pool: &PgPool,
|
||||||
request: PostTableScriptRequest,
|
request: PostTableScriptRequest,
|
||||||
) -> Result<TableScriptResponse, Status> {
|
) -> Result<TableScriptResponse, Status> {
|
||||||
|
// Fetch the table definition
|
||||||
let table_def = sqlx::query!(
|
let table_def = sqlx::query!(
|
||||||
r#"SELECT id, table_name, columns, profile_id
|
r#"SELECT id, table_name, columns, profile_id
|
||||||
FROM table_definitions WHERE id = $1"#,
|
FROM table_definitions WHERE id = $1"#,
|
||||||
@@ -49,10 +54,12 @@ pub async fn post_table_script(
|
|||||||
)
|
)
|
||||||
.fetch_optional(db_pool)
|
.fetch_optional(db_pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| Status::internal(format!("Database error: {}", e)))?
|
.map_err(|e| {
|
||||||
|
Status::internal(format!("Failed to fetch table definition: {}", e))
|
||||||
|
})?
|
||||||
.ok_or_else(|| Status::not_found("Table definition not found"))?;
|
.ok_or_else(|| Status::not_found("Table definition not found"))?;
|
||||||
|
|
||||||
// Validate target column and get its type
|
// Validate the target column and get its type
|
||||||
let column_type = validate_target_column(
|
let column_type = validate_target_column(
|
||||||
&table_def.table_name,
|
&table_def.table_name,
|
||||||
&request.target_column,
|
&request.target_column,
|
||||||
@@ -60,14 +67,14 @@ pub async fn post_table_script(
|
|||||||
)
|
)
|
||||||
.map_err(|e| Status::invalid_argument(e))?;
|
.map_err(|e| Status::invalid_argument(e))?;
|
||||||
|
|
||||||
// Parse and transform the script
|
// Parse and transform the script using the syntax parser
|
||||||
let parser = SyntaxParser::new();
|
let parser = SyntaxParser::new();
|
||||||
let parsed_script = parser.parse(&request.script, &table_def.table_name);
|
let parsed_script = parser.parse(&request.script, &table_def.table_name);
|
||||||
|
|
||||||
// Store script in database with automatic target_table
|
// Insert the script into the database
|
||||||
let script_record = sqlx::query!(
|
let script_record = sqlx::query!(
|
||||||
r#"INSERT INTO table_scripts
|
r#"INSERT INTO table_scripts
|
||||||
(table_definitions_id, target_table, target_column,
|
(table_definitions_id, target_table, target_column,
|
||||||
target_column_type, script, description, profile_id)
|
target_column_type, script, description, profile_id)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
RETURNING id"#,
|
RETURNING id"#,
|
||||||
@@ -85,11 +92,12 @@ pub async fn post_table_script(
|
|||||||
SqlxError::Database(db_err) if db_err.constraint() == Some("table_scripts_table_definitions_id_target_column_key") => {
|
SqlxError::Database(db_err) if db_err.constraint() == Some("table_scripts_table_definitions_id_target_column_key") => {
|
||||||
Status::already_exists("Script already exists for this column")
|
Status::already_exists("Script already exists for this column")
|
||||||
}
|
}
|
||||||
_ => Status::internal(format!("Database error: {}", e)),
|
_ => Status::internal(format!("Failed to insert script: {}", e)),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
// Return the response with the new script ID
|
||||||
Ok(TableScriptResponse {
|
Ok(TableScriptResponse {
|
||||||
id: script_record.id,
|
id: script_record.id,
|
||||||
warnings: String::new(),
|
warnings: String::new(), // No warnings for now
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,9 +128,9 @@ pub async fn post_table_data(
|
|||||||
profile_id,
|
profile_id,
|
||||||
row_data: data.clone(),
|
row_data: data.clone(),
|
||||||
db_pool: Arc::new(db_pool.clone()),
|
db_pool: Arc::new(db_pool.clone()),
|
||||||
runtime: Handle::current(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Execute validation script
|
// Execute validation script
|
||||||
let script_result = execution::execute_script(
|
let script_result = execution::execute_script(
|
||||||
script_record.script,
|
script_record.script,
|
||||||
|
|||||||
Reference in New Issue
Block a user