crucial self reference allowed
This commit is contained in:
@@ -273,11 +273,13 @@ impl DependencyAnalyzer {
|
||||
|
||||
/// Validates that structured table access (steel_get_column functions) respects link constraints
|
||||
/// Raw SQL access (steel_query_sql) is allowed to reference any table
|
||||
///
|
||||
/// SELF-REFERENCES are always allowed (table can access its own columns)
|
||||
///
|
||||
/// Example:
|
||||
/// - Table A can ALWAYS use: (steel_get_column "table_a" "column_name") ✅ (self-reference)
|
||||
/// - Table A is linked to Table B via table_definition_links
|
||||
/// - Script for Table A can use: (steel_get_column "table_b" "column_name") ✅
|
||||
/// - Script for Table A CANNOT use: (steel_get_column "table_c" "column_name") ❌
|
||||
/// - Script for Table A CANNOT use: (steel_get_column "table_c" "column_name") ❌
|
||||
/// - Script for Table A CAN use: (steel_query_sql "SELECT * FROM table_c") ✅
|
||||
async fn validate_link_constraints(
|
||||
&self,
|
||||
@@ -285,6 +287,15 @@ impl DependencyAnalyzer {
|
||||
source_table_id: i64,
|
||||
dependencies: &[Dependency],
|
||||
) -> Result<(), DependencyError> {
|
||||
// Get the current table name for self-reference checking
|
||||
let current_table_name = sqlx::query_scalar!(
|
||||
"SELECT table_name FROM table_definitions WHERE id = $1",
|
||||
source_table_id
|
||||
)
|
||||
.fetch_one(&mut **tx)
|
||||
.await
|
||||
.map_err(|e| DependencyError::DatabaseError { error: e.to_string() })?;
|
||||
|
||||
// Get all valid linked tables for the source table
|
||||
let linked_tables = sqlx::query!(
|
||||
r#"SELECT td.table_name, tdl.is_required
|
||||
@@ -298,30 +309,24 @@ impl DependencyAnalyzer {
|
||||
.map_err(|e| DependencyError::DatabaseError { error: e.to_string() })?;
|
||||
|
||||
// Create a set of allowed table names for quick lookup
|
||||
let allowed_tables: std::collections::HashSet<String> = linked_tables
|
||||
let mut allowed_tables: std::collections::HashSet<String> = linked_tables
|
||||
.into_iter()
|
||||
.map(|row| row.table_name)
|
||||
.collect();
|
||||
|
||||
// Get the current table name for better error messages
|
||||
let current_table_name = sqlx::query_scalar!(
|
||||
"SELECT table_name FROM table_definitions WHERE id = $1",
|
||||
source_table_id
|
||||
)
|
||||
.fetch_one(&mut **tx)
|
||||
.await
|
||||
.map_err(|e| DependencyError::DatabaseError { error: e.to_string() })?;
|
||||
// ALWAYS allow self-references
|
||||
allowed_tables.insert(current_table_name.clone());
|
||||
|
||||
// Validate each dependency
|
||||
for dep in dependencies {
|
||||
match &dep.dependency_type {
|
||||
// Structured access must respect link constraints
|
||||
// Structured access must respect link constraints (but self-references are always allowed)
|
||||
DependencyType::ColumnAccess { column } | DependencyType::IndexedAccess { column, .. } => {
|
||||
if !allowed_tables.contains(&dep.target_table) {
|
||||
return Err(DependencyError::InvalidTableReference {
|
||||
table_name: dep.target_table.clone(),
|
||||
script_context: format!(
|
||||
"Table '{}' is not linked to '{}'. Add a link in the table definition to access '{}' via steel_get_column functions. Column attempted: '{}'",
|
||||
"Table '{}' is not linked to '{}'. Add a link in the table definition to access '{}' via steel_get_column functions. Column attempted: '{}'. Note: Self-references are always allowed.",
|
||||
dep.target_table,
|
||||
current_table_name,
|
||||
dep.target_table,
|
||||
|
||||
@@ -223,6 +223,7 @@ mod tests {
|
||||
|
||||
let cycle = detect_cycle_dfs(1, &graph, &mut visited, &mut rec_stack, &table_names);
|
||||
assert!(cycle.is_some());
|
||||
assert!(cycle.unwrap().contains("table_a") && cycle.unwrap().contains("table_b"));
|
||||
let cycle_str = cycle.unwrap();
|
||||
assert!(cycle_str.contains("table_a") && cycle_str.contains("table_b"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user