use vibesql_executor::{CreateTableExecutor, DeleteExecutor, InsertExecutor, UpdateExecutor};
use vibesql_parser::Parser;
use vibesql_storage::Database;
use vibesql_types::SqlValue;
fn execute_sql(sql: &str) -> Database {
let mut db = Database::new();
db.set_foreign_keys_enabled(true);
for sql_stmt in sql.split(';') {
let trimmed = sql_stmt.trim();
if trimmed.is_empty() {
continue;
}
let stmt = Parser::parse_sql(trimmed).expect("Failed to parse SQL");
execute_statement(&stmt, &mut db);
}
db
}
fn execute_statement(stmt: &vibesql_ast::Statement, db: &mut Database) {
use vibesql_ast::Statement;
match stmt {
Statement::CreateTable(create_stmt) => {
CreateTableExecutor::execute(create_stmt, db).expect("Failed to execute CREATE TABLE");
}
Statement::Insert(insert_stmt) => {
InsertExecutor::execute(db, insert_stmt).expect("Failed to execute INSERT");
}
Statement::Delete(delete_stmt) => {
DeleteExecutor::execute(delete_stmt, db).expect("Failed to execute DELETE");
}
Statement::Update(update_stmt) => {
UpdateExecutor::execute(update_stmt, db).expect("Failed to execute UPDATE");
}
_ => panic!("Unsupported statement type"),
}
}
fn get_all_rows(db: &Database, table_name: &str) -> Vec<Vec<SqlValue>> {
let table = db.get_table(table_name).expect("Table not found");
table.scan().iter().map(|row| row.values.to_vec()).collect()
}
#[test]
fn test_on_delete_set_default_literal_integer() {
let db = execute_sql(
r#"
CREATE TABLE parent (id INTEGER PRIMARY KEY);
CREATE TABLE child (
id INTEGER PRIMARY KEY,
parent_id INTEGER DEFAULT 0,
FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE SET DEFAULT
);
INSERT INTO parent VALUES (0);
INSERT INTO parent VALUES (1);
INSERT INTO parent VALUES (2);
INSERT INTO child VALUES (1, 1);
INSERT INTO child VALUES (2, 2);
"#,
);
let rows = get_all_rows(&db, "child");
assert_eq!(rows.len(), 2);
assert_eq!(rows[0], vec![SqlValue::Integer(1), SqlValue::Integer(1)]);
assert_eq!(rows[1], vec![SqlValue::Integer(2), SqlValue::Integer(2)]);
let mut db = db;
let stmt = Parser::parse_sql("DELETE FROM parent WHERE id = 2").unwrap();
execute_statement(&stmt, &mut db);
let rows = get_all_rows(&db, "child");
assert_eq!(rows.len(), 2);
assert_eq!(rows[0], vec![SqlValue::Integer(1), SqlValue::Integer(1)]);
assert_eq!(rows[1], vec![SqlValue::Integer(2), SqlValue::Integer(0)]);
}
#[test]
fn test_on_delete_set_default_literal_string() {
let db = execute_sql(
r#"
CREATE TABLE parent (code VARCHAR(10) PRIMARY KEY);
CREATE TABLE child (
id INTEGER PRIMARY KEY,
parent_code VARCHAR(10) DEFAULT 'NONE',
FOREIGN KEY (parent_code) REFERENCES parent(code) ON DELETE SET DEFAULT
);
INSERT INTO parent VALUES ('NONE');
INSERT INTO parent VALUES ('A');
INSERT INTO parent VALUES ('B');
INSERT INTO child VALUES (1, 'A');
INSERT INTO child VALUES (2, 'B');
"#,
);
let mut db = db;
let stmt = Parser::parse_sql("DELETE FROM parent WHERE code = 'B'").unwrap();
execute_statement(&stmt, &mut db);
let rows = get_all_rows(&db, "child");
assert_eq!(rows.len(), 2);
assert_eq!(rows[0], vec![SqlValue::Integer(1), SqlValue::Varchar(arcstr::ArcStr::from("A"))]);
assert_eq!(
rows[1],
vec![SqlValue::Integer(2), SqlValue::Varchar(arcstr::ArcStr::from("NONE"))]
);
}
#[test]
fn test_on_delete_set_default_null() {
let db = execute_sql(
r#"
CREATE TABLE parent (id INTEGER PRIMARY KEY);
CREATE TABLE child (
id INTEGER PRIMARY KEY,
parent_id INTEGER,
FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE SET DEFAULT
);
INSERT INTO parent VALUES (1);
INSERT INTO parent VALUES (2);
INSERT INTO child VALUES (1, 1);
INSERT INTO child VALUES (2, 2);
"#,
);
let mut db = db;
let stmt = Parser::parse_sql("DELETE FROM parent WHERE id = 2").unwrap();
execute_statement(&stmt, &mut db);
let rows = get_all_rows(&db, "child");
assert_eq!(rows.len(), 2);
assert_eq!(rows[0], vec![SqlValue::Integer(1), SqlValue::Integer(1)]);
assert_eq!(rows[1], vec![SqlValue::Integer(2), SqlValue::Null]);
}
#[test]
fn test_on_update_set_default_literal() {
let db = execute_sql(
r#"
CREATE TABLE parent (id INTEGER PRIMARY KEY);
CREATE TABLE child (
id INTEGER PRIMARY KEY,
parent_id INTEGER DEFAULT 99,
FOREIGN KEY (parent_id) REFERENCES parent(id) ON UPDATE SET DEFAULT
);
INSERT INTO parent VALUES (99);
INSERT INTO parent VALUES (1);
INSERT INTO parent VALUES (2);
INSERT INTO child VALUES (1, 1);
INSERT INTO child VALUES (2, 2);
"#,
);
let mut db = db;
let stmt = Parser::parse_sql("UPDATE parent SET id = 3 WHERE id = 2").unwrap();
execute_statement(&stmt, &mut db);
let rows = get_all_rows(&db, "child");
assert_eq!(rows.len(), 2);
assert_eq!(rows[0], vec![SqlValue::Integer(1), SqlValue::Integer(1)]);
assert_eq!(rows[1], vec![SqlValue::Integer(2), SqlValue::Integer(99)]);
}
#[test]
fn test_on_delete_set_default_composite_key() {
let db = execute_sql(
r#"
CREATE TABLE parent (id1 INTEGER, id2 INTEGER, PRIMARY KEY (id1, id2));
CREATE TABLE child (
id INTEGER PRIMARY KEY,
parent_id1 INTEGER DEFAULT 0,
parent_id2 INTEGER DEFAULT 0,
FOREIGN KEY (parent_id1, parent_id2) REFERENCES parent(id1, id2) ON DELETE SET DEFAULT
);
INSERT INTO parent VALUES (0, 0);
INSERT INTO parent VALUES (1, 1);
INSERT INTO parent VALUES (2, 2);
INSERT INTO child VALUES (1, 1, 1);
INSERT INTO child VALUES (2, 2, 2);
"#,
);
let mut db = db;
let stmt = Parser::parse_sql("DELETE FROM parent WHERE id1 = 2 AND id2 = 2").unwrap();
execute_statement(&stmt, &mut db);
let rows = get_all_rows(&db, "child");
assert_eq!(rows.len(), 2);
assert_eq!(rows[0], vec![SqlValue::Integer(1), SqlValue::Integer(1), SqlValue::Integer(1)]);
assert_eq!(rows[1], vec![SqlValue::Integer(2), SqlValue::Integer(0), SqlValue::Integer(0)]);
}
#[test]
fn test_on_delete_set_default_multiple_child_rows() {
let db = execute_sql(
r#"
CREATE TABLE parent (id INTEGER PRIMARY KEY);
CREATE TABLE child (
id INTEGER PRIMARY KEY,
parent_id INTEGER DEFAULT 0,
FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE SET DEFAULT
);
INSERT INTO parent VALUES (0);
INSERT INTO parent VALUES (1);
INSERT INTO child VALUES (1, 1);
INSERT INTO child VALUES (2, 1);
INSERT INTO child VALUES (3, 1);
"#,
);
let mut db = db;
let stmt = Parser::parse_sql("DELETE FROM parent WHERE id = 1").unwrap();
execute_statement(&stmt, &mut db);
let rows = get_all_rows(&db, "child");
assert_eq!(rows.len(), 3);
assert_eq!(rows[0], vec![SqlValue::Integer(1), SqlValue::Integer(0)]);
assert_eq!(rows[1], vec![SqlValue::Integer(2), SqlValue::Integer(0)]);
assert_eq!(rows[2], vec![SqlValue::Integer(3), SqlValue::Integer(0)]);
}
#[test]
fn test_on_delete_set_default_no_child_rows() {
let db = execute_sql(
r#"
CREATE TABLE parent (id INTEGER PRIMARY KEY);
CREATE TABLE child (
id INTEGER PRIMARY KEY,
parent_id INTEGER DEFAULT 0,
FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE SET DEFAULT
);
INSERT INTO parent VALUES (0);
INSERT INTO parent VALUES (1);
INSERT INTO parent VALUES (2);
INSERT INTO child VALUES (1, 1);
"#,
);
let mut db = db;
let stmt = Parser::parse_sql("DELETE FROM parent WHERE id = 2").unwrap();
execute_statement(&stmt, &mut db);
let rows = get_all_rows(&db, "child");
assert_eq!(rows.len(), 1);
assert_eq!(rows[0], vec![SqlValue::Integer(1), SqlValue::Integer(1)]);
}