use stoolap::Database;
#[test]
fn test_create_table_rollback() {
let db = Database::open("memory://ddl_create_rollback").expect("Failed to create database");
db.execute("BEGIN", ())
.expect("Failed to begin transaction");
db.execute("CREATE TABLE rollback_test (id INTEGER, name TEXT)", ())
.expect("Failed to create table");
db.execute("INSERT INTO rollback_test VALUES (1, 'test')", ())
.expect("Failed to insert");
let count: i64 = db
.query_one("SELECT COUNT(*) FROM rollback_test", ())
.expect("Table should exist within transaction");
assert_eq!(count, 1, "Should have 1 row within transaction");
db.execute("ROLLBACK", ())
.expect("Failed to rollback transaction");
let result = db.query("SELECT * FROM rollback_test", ());
assert!(result.is_err(), "Table should not exist after rollback");
}
#[test]
fn test_create_table_commit() {
let db = Database::open("memory://ddl_create_commit").expect("Failed to create database");
db.execute("BEGIN", ())
.expect("Failed to begin transaction");
db.execute("CREATE TABLE commit_test (id INTEGER, name TEXT)", ())
.expect("Failed to create table");
db.execute("INSERT INTO commit_test VALUES (1, 'test')", ())
.expect("Failed to insert");
db.execute("COMMIT", ())
.expect("Failed to commit transaction");
let count: i64 = db
.query_one("SELECT COUNT(*) FROM commit_test", ())
.expect("Table should exist after commit");
assert_eq!(count, 1, "Should have 1 row after commit");
}
#[test]
fn test_drop_table_rollback() {
let db = Database::open("memory://ddl_drop_rollback").expect("Failed to create database");
db.execute("CREATE TABLE persist_test (id INTEGER, name TEXT)", ())
.expect("Failed to create table");
db.execute("INSERT INTO persist_test VALUES (1, 'test')", ())
.expect("Failed to insert");
let count: i64 = db
.query_one("SELECT COUNT(*) FROM persist_test", ())
.expect("Table should exist");
assert_eq!(count, 1, "Should have 1 row");
db.execute("BEGIN", ())
.expect("Failed to begin transaction");
db.execute("DROP TABLE persist_test", ())
.expect("Failed to drop table");
let result = db.query("SELECT * FROM persist_test", ());
assert!(
result.is_err(),
"Table should not exist after DROP within transaction"
);
db.execute("ROLLBACK", ())
.expect("Failed to rollback transaction");
let result = db.query("SELECT * FROM persist_test", ());
assert!(
result.is_ok(),
"Table schema should be restored after rollback"
);
}
#[test]
fn test_drop_table_commit() {
let db = Database::open("memory://ddl_drop_commit").expect("Failed to create database");
db.execute("CREATE TABLE drop_commit_test (id INTEGER, name TEXT)", ())
.expect("Failed to create table");
db.execute("BEGIN", ())
.expect("Failed to begin transaction");
db.execute("DROP TABLE drop_commit_test", ())
.expect("Failed to drop table");
db.execute("COMMIT", ())
.expect("Failed to commit transaction");
let result = db.query("SELECT * FROM drop_commit_test", ());
assert!(
result.is_err(),
"Table should not exist after committed DROP"
);
}
#[test]
fn test_multiple_ddl_in_transaction() {
let db = Database::open("memory://ddl_multiple").expect("Failed to create database");
db.execute("BEGIN", ())
.expect("Failed to begin transaction");
db.execute("CREATE TABLE table1 (id INTEGER)", ())
.expect("Failed to create table1");
db.execute("CREATE TABLE table2 (id INTEGER)", ())
.expect("Failed to create table2");
db.execute("CREATE TABLE table3 (id INTEGER)", ())
.expect("Failed to create table3");
db.execute("INSERT INTO table1 VALUES (1)", ())
.expect("table1 should exist");
db.execute("INSERT INTO table2 VALUES (2)", ())
.expect("table2 should exist");
db.execute("INSERT INTO table3 VALUES (3)", ())
.expect("table3 should exist");
db.execute("ROLLBACK", ())
.expect("Failed to rollback transaction");
assert!(
db.query("SELECT * FROM table1", ()).is_err(),
"table1 should not exist after rollback"
);
assert!(
db.query("SELECT * FROM table2", ()).is_err(),
"table2 should not exist after rollback"
);
assert!(
db.query("SELECT * FROM table3", ()).is_err(),
"table3 should not exist after rollback"
);
}
#[test]
fn test_ddl_outside_transaction_auto_commits() {
let db = Database::open("memory://ddl_auto_commit").expect("Failed to create database");
db.execute("CREATE TABLE auto_commit_test (id INTEGER)", ())
.expect("Failed to create table");
let result = db.query("SELECT * FROM auto_commit_test", ());
assert!(result.is_ok(), "Table should exist after auto-commit");
}
#[test]
fn test_mixed_ddl_and_dml_rollback() {
let db = Database::open("memory://ddl_dml_mixed").expect("Failed to create database");
db.execute("CREATE TABLE existing_table (id INTEGER, value TEXT)", ())
.expect("Failed to create table");
db.execute("INSERT INTO existing_table VALUES (1, 'original')", ())
.expect("Failed to insert");
db.execute("BEGIN", ())
.expect("Failed to begin transaction");
db.execute("CREATE TABLE new_table (id INTEGER)", ())
.expect("Failed to create new table");
db.execute(
"UPDATE existing_table SET value = 'modified' WHERE id = 1",
(),
)
.expect("Failed to update");
db.execute("ROLLBACK", ())
.expect("Failed to rollback transaction");
assert!(
db.query("SELECT * FROM new_table", ()).is_err(),
"New table should not exist after rollback"
);
let value: String = db
.query_one("SELECT value FROM existing_table WHERE id = 1", ())
.expect("Should be able to query existing table");
assert_eq!(value, "original", "Value should be rolled back to original");
}
#[test]
fn test_modify_column_not_null_rejects_existing_nulls() {
let db = Database::open("memory://modify_not_null_reject").expect("Failed to create database");
db.execute(
"CREATE TABLE t_nullable (id INTEGER PRIMARY KEY, val TEXT)",
(),
)
.expect("Failed to create table");
db.execute("INSERT INTO t_nullable VALUES (1, NULL)", ())
.expect("Failed to insert");
db.execute("INSERT INTO t_nullable VALUES (2, 'hello')", ())
.expect("Failed to insert");
let result = db.execute("ALTER TABLE t_nullable MODIFY COLUMN val TEXT NOT NULL", ());
assert!(result.is_err(), "Should reject NOT NULL when NULLs exist");
let err = result.unwrap_err().to_string();
assert!(
err.contains("not null constraint"),
"Error should mention not null constraint, got: {}",
err
);
}
#[test]
fn test_modify_column_not_null_succeeds_without_nulls() {
let db = Database::open("memory://modify_not_null_success").expect("Failed to create database");
db.execute(
"CREATE TABLE t_non_null (id INTEGER PRIMARY KEY, val TEXT)",
(),
)
.expect("Failed to create table");
db.execute("INSERT INTO t_non_null VALUES (1, 'a')", ())
.expect("Failed to insert");
db.execute("INSERT INTO t_non_null VALUES (2, 'b')", ())
.expect("Failed to insert");
db.execute("ALTER TABLE t_non_null MODIFY COLUMN val TEXT NOT NULL", ())
.expect("MODIFY COLUMN should succeed when no NULLs exist");
let result = db.execute("INSERT INTO t_non_null VALUES (3, NULL)", ());
assert!(
result.is_err(),
"INSERT with NULL should fail after NOT NULL constraint"
);
}
#[test]
fn test_modify_column_not_null_empty_table() {
let db = Database::open("memory://modify_not_null_empty").expect("Failed to create database");
db.execute(
"CREATE TABLE t_empty (id INTEGER PRIMARY KEY, val TEXT)",
(),
)
.expect("Failed to create table");
db.execute("ALTER TABLE t_empty MODIFY COLUMN val TEXT NOT NULL", ())
.expect("Should succeed on empty table");
}