we got a full passer now
This commit is contained in:
@@ -15,8 +15,8 @@ use crate::table_script::handlers::dependency_analyzer::DependencyAnalyzer;
|
|||||||
const SYSTEM_COLUMNS: &[&str] = &["id", "deleted", "created_at"];
|
const SYSTEM_COLUMNS: &[&str] = &["id", "deleted", "created_at"];
|
||||||
|
|
||||||
// Define prohibited data types for Steel scripts (boolean is explicitly allowed)
|
// Define prohibited data types for Steel scripts (boolean is explicitly allowed)
|
||||||
const PROHIBITED_TYPES: &[&str] = &["DATE", "TIMESTAMPTZ"];
|
const PROHIBITED_TYPES: &[&str] = &["BIGINT", "DATE", "TIMESTAMPTZ"];
|
||||||
const MATH_PROHIBITED_TYPES: &[&str] = &["BIGINT", "TEXT", "BOOLEAN"];
|
const MATH_PROHIBITED_TYPES: &[&str] = &["BIGINT", "TEXT", "BOOLEAN", "DATE", "TIMESTAMPTZ"];
|
||||||
|
|
||||||
// Math operations that Steel Decimal will transform
|
// Math operations that Steel Decimal will transform
|
||||||
const MATH_OPERATIONS: &[&str] = &[
|
const MATH_OPERATIONS: &[&str] = &[
|
||||||
@@ -616,12 +616,12 @@ pub async fn post_table_script(
|
|||||||
)
|
)
|
||||||
.map_err(|e| Status::invalid_argument(e))?;
|
.map_err(|e| Status::invalid_argument(e))?;
|
||||||
|
|
||||||
// Validate that script doesn't reference prohibited column types by checking actual DB schema
|
// REORDER: Math validation FIRST so we get specific error messages for math operations
|
||||||
validate_script_column_references(db_pool, table_def.schema_id, &request.script).await?;
|
|
||||||
|
|
||||||
// NEW: Validate that mathematical operations don't use TEXT or BOOLEAN columns
|
|
||||||
validate_math_operations_column_types(db_pool, table_def.schema_id, &request.script).await?;
|
validate_math_operations_column_types(db_pool, table_def.schema_id, &request.script).await?;
|
||||||
|
|
||||||
|
// THEN general column validation (catches non-math prohibited access)
|
||||||
|
validate_script_column_references(db_pool, table_def.schema_id, &request.script).await?;
|
||||||
|
|
||||||
// Validate SQL queries in script for prohibited type operations
|
// Validate SQL queries in script for prohibited type operations
|
||||||
validate_sql_queries_in_script(&request.script)
|
validate_sql_queries_in_script(&request.script)
|
||||||
.map_err(|e| Status::invalid_argument(e))?;
|
.map_err(|e| Status::invalid_argument(e))?;
|
||||||
|
|||||||
@@ -218,59 +218,76 @@ async fn test_advanced_validation_scenarios(
|
|||||||
// Don't assert failure here - these are edge cases that might be handled differently
|
// Don't assert failure here - these are edge cases that might be handled differently
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn create_table_link(pool: &PgPool, source_table_id: i64, linked_table_id: i64) {
|
||||||
|
sqlx::query!(
|
||||||
|
"INSERT INTO table_definition_links (source_table_id, linked_table_id, is_required)
|
||||||
|
VALUES ($1, $2, false)",
|
||||||
|
source_table_id,
|
||||||
|
linked_table_id
|
||||||
|
)
|
||||||
|
.execute(pool)
|
||||||
|
.await
|
||||||
|
.expect("Failed to create table link");
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_dependency_cycle_detection() {
|
async fn test_dependency_cycle_detection() {
|
||||||
let pool = setup_isolated_db().await;
|
let pool = setup_isolated_db().await;
|
||||||
let schema_id = get_default_schema_id(&pool).await;
|
let schema_id = get_default_schema_id(&pool).await;
|
||||||
|
|
||||||
// Create two tables for dependency testing
|
// Create table_b first
|
||||||
let table_a_columns = vec![
|
|
||||||
("value_a", "NUMERIC(10, 2)"),
|
|
||||||
("result_a", "NUMERIC(10, 2)"),
|
|
||||||
];
|
|
||||||
let table_a_id = create_test_table(&pool, schema_id, "table_a", table_a_columns).await;
|
|
||||||
|
|
||||||
let table_b_columns = vec![
|
let table_b_columns = vec![
|
||||||
("value_b", "NUMERIC(10, 2)"),
|
("value_b", "NUMERIC(10, 2)"),
|
||||||
("result_b", "NUMERIC(10, 2)"),
|
("result_b", "NUMERIC(10, 2)"),
|
||||||
];
|
];
|
||||||
let table_b_id = create_test_table(&pool, schema_id, "table_b", table_b_columns).await;
|
let table_b_id = create_test_table(&pool, schema_id, "table_b", table_b_columns).await;
|
||||||
|
|
||||||
// Create first dependency: table_a.result_a depends on table_b.value_b
|
// Create table_a
|
||||||
|
let table_a_columns = vec![
|
||||||
|
("value_a", "NUMERIC(10, 2)"),
|
||||||
|
("result_a", "NUMERIC(10, 2)"),
|
||||||
|
];
|
||||||
|
let table_a_id = create_test_table(&pool, schema_id, "table_a", table_a_columns).await;
|
||||||
|
|
||||||
|
// CREATE BOTH LINKS for circular dependency testing
|
||||||
|
create_table_link(&pool, table_a_id, table_b_id).await; // table_a -> table_b
|
||||||
|
create_table_link(&pool, table_b_id, table_a_id).await; // table_b -> table_a
|
||||||
|
|
||||||
|
// First dependency should work
|
||||||
let script_a = r#"(+ (steel_get_column "table_b" "value_b") "10")"#;
|
let script_a = r#"(+ (steel_get_column "table_b" "value_b") "10")"#;
|
||||||
let request_a = PostTableScriptRequest {
|
let request_a = PostTableScriptRequest {
|
||||||
table_definition_id: table_a_id,
|
table_definition_id: table_a_id,
|
||||||
target_column: "result_a".to_string(),
|
target_column: "result_a".to_string(),
|
||||||
script: script_a.to_string(),
|
script: script_a.to_string(),
|
||||||
description: "First dependency".to_string(), // Fixed: removed Some()
|
description: "First dependency".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let result_a = post_table_script(&pool, request_a).await;
|
let result_a = post_table_script(&pool, request_a).await;
|
||||||
assert!(result_a.is_ok(), "First dependency should succeed");
|
assert!(result_a.is_ok(), "First dependency should succeed");
|
||||||
|
|
||||||
// Try to create circular dependency: table_b.result_b depends on table_a.result_a
|
// Try circular dependency - should now work since links exist both ways
|
||||||
let script_b = r#"(* (steel_get_column "table_a" "result_a") "2")"#;
|
let script_b = r#"(* (steel_get_column "table_a" "result_a") "2")"#;
|
||||||
let request_b = PostTableScriptRequest {
|
let request_b = PostTableScriptRequest {
|
||||||
table_definition_id: table_b_id,
|
table_definition_id: table_b_id,
|
||||||
target_column: "result_b".to_string(),
|
target_column: "result_b".to_string(),
|
||||||
script: script_b.to_string(),
|
script: script_b.to_string(),
|
||||||
description: "Circular dependency attempt".to_string(), // Fixed: removed Some()
|
description: "Circular dependency attempt".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let result_b = post_table_script(&pool, request_b).await;
|
let result_b = post_table_script(&pool, request_b).await;
|
||||||
|
|
||||||
// Depending on implementation, this should either succeed or detect the cycle
|
// This should either succeed or detect the cycle
|
||||||
match result_b {
|
match result_b {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// Implementation allows this pattern
|
// Implementation allows this pattern
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
// Implementation detects circular dependencies
|
// Implementation detects circular dependencies
|
||||||
let error_msg = error.to_string();
|
let error_msg = error.to_string().to_lowercase();
|
||||||
assert!(
|
assert!(
|
||||||
error_msg.contains("cycle") || error_msg.contains("circular"),
|
error_msg.contains("cycle") || error_msg.contains("circular"),
|
||||||
"Circular dependency should be detected properly: {}",
|
"Circular dependency should be detected properly: {}",
|
||||||
error_msg
|
error.to_string()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user