use std::collections::HashSet;
use vibesql_storage::Database;
use super::{constraints::get_fk_children, triggers::validate_no_delete_triggers};
use crate::{errors::ExecutorError, privilege_checker::PrivilegeChecker};
pub fn reset_auto_increment_sequences(
database: &mut Database,
table_name: &str,
) -> Result<(), ExecutorError> {
let table_schema = database
.catalog
.get_table(table_name)
.ok_or_else(|| ExecutorError::TableNotFound(table_name.to_string()))?;
let mut sequence_names = Vec::new();
for column in &table_schema.columns {
if let Some(vibesql_ast::Expression::NextValue { sequence_name }) = &column.default_value {
sequence_names.push(sequence_name.clone());
}
}
for sequence_name in sequence_names {
if let Ok(sequence) = database.catalog.get_sequence_mut(&sequence_name) {
sequence.restart(None);
}
}
Ok(())
}
pub fn execute_truncate(database: &mut Database, table_name: &str) -> Result<usize, ExecutorError> {
let table = database
.get_table_mut(table_name)
.ok_or_else(|| ExecutorError::TableNotFound(table_name.to_string()))?;
let row_count = table.row_count();
table.clear();
database.rebuild_indexes(table_name);
if row_count > 0 {
database.invalidate_columnar_cache(table_name);
}
reset_auto_increment_sequences(database, table_name)?;
database.invalidate_columnar_cache(table_name);
Ok(row_count)
}
pub fn execute_truncate_cascade(
database: &mut Database,
table_name: &str,
) -> Result<usize, ExecutorError> {
let truncate_order = collect_fk_dependencies(database, table_name)?;
for tbl in &truncate_order {
validate_no_delete_triggers(database, tbl, table_name)?;
PrivilegeChecker::check_delete(database, tbl)?;
}
let mut total_rows_deleted = 0;
for tbl in truncate_order {
let rows_deleted = execute_truncate(database, &tbl)?;
total_rows_deleted += rows_deleted;
}
Ok(total_rows_deleted)
}
fn collect_fk_dependencies(
database: &Database,
root_table: &str,
) -> Result<Vec<String>, ExecutorError> {
let mut visited = HashSet::new();
let mut order = Vec::new();
let mut recursion_stack = HashSet::new();
fn visit(
database: &Database,
table_name: &str,
visited: &mut HashSet<String>,
order: &mut Vec<String>,
recursion_stack: &mut HashSet<String>,
) -> Result<(), ExecutorError> {
if recursion_stack.contains(table_name) {
return Err(ExecutorError::Other(format!(
"Circular foreign key dependency detected involving table '{}'",
table_name
)));
}
if visited.contains(table_name) {
return Ok(());
}
recursion_stack.insert(table_name.to_string());
visited.insert(table_name.to_string());
let children = get_fk_children(database, table_name)?;
for child in children {
visit(database, &child, visited, order, recursion_stack)?;
}
recursion_stack.remove(table_name);
order.push(table_name.to_string());
Ok(())
}
visit(database, root_table, &mut visited, &mut order, &mut recursion_stack)?;
Ok(order)
}