removal of hardcoded fix, now working in general
This commit is contained in:
@@ -106,7 +106,11 @@ impl DependencyAnalyzer {
|
|||||||
|
|
||||||
/// Analyzes a Steel script to extract all table dependencies
|
/// Analyzes a Steel script to extract all table dependencies
|
||||||
/// Uses regex patterns to find function calls that create dependencies
|
/// Uses regex patterns to find function calls that create dependencies
|
||||||
pub fn analyze_script_dependencies(&self, script: &str) -> Result<Vec<Dependency>, DependencyError> {
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `script` - The Steel script to analyze
|
||||||
|
/// * `current_table_name` - Name of the table this script belongs to (for self-references)
|
||||||
|
pub fn analyze_script_dependencies(&self, script: &str, current_table_name: &str) -> Result<Vec<Dependency>, DependencyError> {
|
||||||
let mut dependencies = Vec::new();
|
let mut dependencies = Vec::new();
|
||||||
|
|
||||||
// Extract function calls and SQL dependencies using regex
|
// Extract function calls and SQL dependencies using regex
|
||||||
@@ -114,10 +118,10 @@ impl DependencyAnalyzer {
|
|||||||
dependencies.extend(self.extract_sql_dependencies(script)?);
|
dependencies.extend(self.extract_sql_dependencies(script)?);
|
||||||
|
|
||||||
// Extract get-var calls (for transformed scripts with variables)
|
// Extract get-var calls (for transformed scripts with variables)
|
||||||
dependencies.extend(self.extract_get_var_calls(script)?);
|
dependencies.extend(self.extract_get_var_calls(script, current_table_name)?);
|
||||||
|
|
||||||
// Extract direct variable references like @price, @quantity
|
// Extract direct variable references like @price, @quantity
|
||||||
dependencies.extend(self.extract_variable_references(script)?);
|
dependencies.extend(self.extract_variable_references(script, current_table_name)?);
|
||||||
|
|
||||||
Ok(dependencies)
|
Ok(dependencies)
|
||||||
}
|
}
|
||||||
@@ -160,7 +164,8 @@ impl DependencyAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Extract get-var calls as dependencies (for transformed scripts with variables)
|
/// Extract get-var calls as dependencies (for transformed scripts with variables)
|
||||||
fn extract_get_var_calls(&self, script: &str) -> Result<Vec<Dependency>, DependencyError> {
|
/// These are self-references to the current table
|
||||||
|
fn extract_get_var_calls(&self, script: &str, current_table_name: &str) -> Result<Vec<Dependency>, DependencyError> {
|
||||||
let mut dependencies = Vec::new();
|
let mut dependencies = Vec::new();
|
||||||
|
|
||||||
// Look for get-var patterns in transformed scripts: (get-var "variable")
|
// Look for get-var patterns in transformed scripts: (get-var "variable")
|
||||||
@@ -170,7 +175,7 @@ impl DependencyAnalyzer {
|
|||||||
for caps in get_var_pattern.captures_iter(script) {
|
for caps in get_var_pattern.captures_iter(script) {
|
||||||
let variable_name = caps[1].to_string();
|
let variable_name = caps[1].to_string();
|
||||||
dependencies.push(Dependency {
|
dependencies.push(Dependency {
|
||||||
target_table: "SAME_TABLE".to_string(), // Special marker for same-table references
|
target_table: current_table_name.to_string(), // Use actual table name
|
||||||
dependency_type: DependencyType::ColumnAccess { column: variable_name },
|
dependency_type: DependencyType::ColumnAccess { column: variable_name },
|
||||||
context: None,
|
context: None,
|
||||||
});
|
});
|
||||||
@@ -180,7 +185,8 @@ impl DependencyAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Extract direct variable references like @price, @quantity
|
/// Extract direct variable references like @price, @quantity
|
||||||
fn extract_variable_references(&self, script: &str) -> Result<Vec<Dependency>, DependencyError> {
|
/// These are self-references to the current table
|
||||||
|
fn extract_variable_references(&self, script: &str, current_table_name: &str) -> Result<Vec<Dependency>, DependencyError> {
|
||||||
let mut dependencies = Vec::new();
|
let mut dependencies = Vec::new();
|
||||||
|
|
||||||
// Look for @variable patterns: @price, @quantity, etc.
|
// Look for @variable patterns: @price, @quantity, etc.
|
||||||
@@ -190,7 +196,7 @@ impl DependencyAnalyzer {
|
|||||||
for caps in variable_pattern.captures_iter(script) {
|
for caps in variable_pattern.captures_iter(script) {
|
||||||
let variable_name = caps[1].to_string();
|
let variable_name = caps[1].to_string();
|
||||||
dependencies.push(Dependency {
|
dependencies.push(Dependency {
|
||||||
target_table: "SAME_TABLE".to_string(), // Same table reference
|
target_table: current_table_name.to_string(), // Use actual table name
|
||||||
dependency_type: DependencyType::ColumnAccess { column: variable_name },
|
dependency_type: DependencyType::ColumnAccess { column: variable_name },
|
||||||
context: None,
|
context: None,
|
||||||
});
|
});
|
||||||
@@ -286,23 +292,19 @@ impl DependencyAnalyzer {
|
|||||||
|
|
||||||
// Add new dependencies to test - EXCLUDE self-references
|
// Add new dependencies to test - EXCLUDE self-references
|
||||||
for dep in new_dependencies {
|
for dep in new_dependencies {
|
||||||
// Look up target table ID
|
// Look up target table ID using the actual table name
|
||||||
let target_id = if dep.target_table == "SAME_TABLE" {
|
let target_id = sqlx::query_scalar!(
|
||||||
table_id // Same table reference
|
"SELECT id FROM table_definitions WHERE schema_id = $1 AND table_name = $2",
|
||||||
} else {
|
self.schema_id,
|
||||||
sqlx::query_scalar!(
|
dep.target_table
|
||||||
"SELECT id FROM table_definitions WHERE schema_id = $1 AND table_name = $2",
|
)
|
||||||
self.schema_id,
|
.fetch_optional(&mut **tx)
|
||||||
dep.target_table
|
.await
|
||||||
)
|
.map_err(|e| DependencyError::DatabaseError { error: e.to_string() })?
|
||||||
.fetch_optional(&mut **tx)
|
.ok_or_else(|| DependencyError::InvalidTableReference {
|
||||||
.await
|
table_name: dep.target_table.clone(),
|
||||||
.map_err(|e| DependencyError::DatabaseError { error: e.to_string() })?
|
script_context: format!("table_id_{}", table_id),
|
||||||
.ok_or_else(|| DependencyError::InvalidTableReference {
|
})?;
|
||||||
table_name: dep.target_table.clone(),
|
|
||||||
script_context: format!("table_id_{}", table_id),
|
|
||||||
})?
|
|
||||||
};
|
|
||||||
|
|
||||||
// Only add to cycle detection graph if it's NOT a self-reference
|
// Only add to cycle detection graph if it's NOT a self-reference
|
||||||
if table_id != target_id {
|
if table_id != target_id {
|
||||||
@@ -444,8 +446,8 @@ impl DependencyAnalyzer {
|
|||||||
match &dep.dependency_type {
|
match &dep.dependency_type {
|
||||||
// Structured access must respect link constraints (but self-references are always allowed)
|
// Structured access must respect link constraints (but self-references are always allowed)
|
||||||
DependencyType::ColumnAccess { column } | DependencyType::IndexedAccess { column, .. } => {
|
DependencyType::ColumnAccess { column } | DependencyType::IndexedAccess { column, .. } => {
|
||||||
// Skip validation for SAME_TABLE marker (these are always allowed)
|
// Self-references are always allowed (compare table names directly)
|
||||||
if dep.target_table == "SAME_TABLE" {
|
if dep.target_table == current_table_name {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,22 +524,19 @@ impl DependencyAnalyzer {
|
|||||||
|
|
||||||
// Insert new dependencies
|
// Insert new dependencies
|
||||||
for dep in dependencies {
|
for dep in dependencies {
|
||||||
let target_id = if dep.target_table == "SAME_TABLE" {
|
// Look up target table ID using actual table name (no magic strings!)
|
||||||
table_id // Use the same table as the script
|
let target_id = sqlx::query_scalar!(
|
||||||
} else {
|
"SELECT id FROM table_definitions WHERE schema_id = $1 AND table_name = $2",
|
||||||
sqlx::query_scalar!(
|
self.schema_id,
|
||||||
"SELECT id FROM table_definitions WHERE schema_id = $1 AND table_name = $2",
|
dep.target_table
|
||||||
self.schema_id,
|
)
|
||||||
dep.target_table
|
.fetch_optional(&mut **tx)
|
||||||
)
|
.await
|
||||||
.fetch_optional(&mut **tx)
|
.map_err(|e| DependencyError::DatabaseError { error: e.to_string() })?
|
||||||
.await
|
.ok_or_else(|| DependencyError::InvalidTableReference {
|
||||||
.map_err(|e| DependencyError::DatabaseError { error: e.to_string() })?
|
table_name: dep.target_table.clone(),
|
||||||
.ok_or_else(|| DependencyError::InvalidTableReference {
|
script_context: format!("script_id_{}", script_id),
|
||||||
table_name: dep.target_table.clone(),
|
})?;
|
||||||
script_context: format!("script_id_{}", script_id),
|
|
||||||
})?
|
|
||||||
};
|
|
||||||
|
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"INSERT INTO script_dependencies
|
r#"INSERT INTO script_dependencies
|
||||||
|
|||||||
@@ -631,7 +631,7 @@ pub async fn post_table_script(
|
|||||||
|
|
||||||
// Analyze script dependencies
|
// Analyze script dependencies
|
||||||
let dependencies = analyzer
|
let dependencies = analyzer
|
||||||
.analyze_script_dependencies(&request.script)
|
.analyze_script_dependencies(&request.script, &table_def.table_name)
|
||||||
.map_err(|e| Status::from(e))?;
|
.map_err(|e| Status::from(e))?;
|
||||||
|
|
||||||
// Check for circular dependencies BEFORE making any changes
|
// Check for circular dependencies BEFORE making any changes
|
||||||
|
|||||||
Reference in New Issue
Block a user