use stoolap::Database;
#[test]
fn test_uncommitted_not_visible() {
let db = Database::open("memory://mvcc_uncommitted").expect("Failed to create database");
db.execute(
"CREATE TABLE test_isolation (id INTEGER PRIMARY KEY, value TEXT)",
(),
)
.expect("Failed to create table");
db.execute(
"INSERT INTO test_isolation (id, value) VALUES (1, 'initial')",
(),
)
.expect("Failed to insert");
let mut tx = db.begin().expect("Failed to begin transaction");
tx.execute(
"UPDATE test_isolation SET value = 'updated' WHERE id = 1",
(),
)
.expect("Failed to update in tx");
let result = db
.query("SELECT value FROM test_isolation WHERE id = 1", ())
.expect("Failed to query");
for row in result {
let row = row.expect("Failed to get row");
let value: String = row.get(0).unwrap();
assert_eq!(value, "initial", "Should see initial value before commit");
}
tx.rollback().expect("Failed to rollback");
}
#[test]
fn test_committed_visible() {
let db = Database::open("memory://mvcc_committed").expect("Failed to create database");
db.execute(
"CREATE TABLE test_isolation (id INTEGER PRIMARY KEY, value TEXT)",
(),
)
.expect("Failed to create table");
db.execute(
"INSERT INTO test_isolation (id, value) VALUES (1, 'initial')",
(),
)
.expect("Failed to insert");
db.execute("BEGIN", ())
.expect("Failed to begin transaction");
db.execute(
"UPDATE test_isolation SET value = 'committed' WHERE id = 1",
(),
)
.expect("Failed to update in tx");
db.execute("COMMIT", ()).expect("Failed to commit");
let result = db
.query("SELECT value FROM test_isolation WHERE id = 1", ())
.expect("Failed to query");
for row in result {
let row = row.expect("Failed to get row");
let value: String = row.get(0).unwrap();
assert_eq!(value, "committed", "Should see committed value");
}
}
#[test]
fn test_transaction_rollback() {
let db = Database::open("memory://mvcc_rollback").expect("Failed to create database");
db.execute(
"CREATE TABLE test_rollback (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.expect("Failed to create table");
db.execute("INSERT INTO test_rollback (id, value) VALUES (1, 100)", ())
.expect("Failed to insert");
db.execute("BEGIN", ()).expect("Failed to begin");
db.execute("UPDATE test_rollback SET value = 999 WHERE id = 1", ())
.expect("Failed to update");
db.execute("INSERT INTO test_rollback (id, value) VALUES (2, 200)", ())
.expect("Failed to insert in tx");
db.execute("ROLLBACK", ()).expect("Failed to rollback");
let count: i64 = db
.query_one("SELECT COUNT(*) FROM test_rollback", ())
.expect("Failed to count");
assert_eq!(count, 1, "Should have only 1 row after rollback");
let value: i64 = db
.query_one("SELECT value FROM test_rollback WHERE id = 1", ())
.expect("Failed to query");
assert_eq!(value, 100, "Value should be unchanged after rollback");
}
#[test]
fn test_transaction_multiple_inserts() {
let db = Database::open("memory://mvcc_multi_insert").expect("Failed to create database");
db.execute(
"CREATE TABLE test_batch (id INTEGER PRIMARY KEY, name TEXT)",
(),
)
.expect("Failed to create table");
db.execute("BEGIN", ()).expect("Failed to begin");
db.execute("INSERT INTO test_batch (id, name) VALUES (1, 'Alice')", ())
.expect("Failed to insert 1");
db.execute("INSERT INTO test_batch (id, name) VALUES (2, 'Bob')", ())
.expect("Failed to insert 2");
db.execute(
"INSERT INTO test_batch (id, name) VALUES (3, 'Charlie')",
(),
)
.expect("Failed to insert 3");
db.execute("COMMIT", ()).expect("Failed to commit");
let count: i64 = db
.query_one("SELECT COUNT(*) FROM test_batch", ())
.expect("Failed to count");
assert_eq!(count, 3, "Should have 3 rows after commit");
}
#[test]
fn test_transaction_delete() {
let db = Database::open("memory://mvcc_delete").expect("Failed to create database");
db.execute(
"CREATE TABLE test_delete (id INTEGER PRIMARY KEY, value TEXT)",
(),
)
.expect("Failed to create table");
db.execute(
"INSERT INTO test_delete (id, value) VALUES (1, 'one'), (2, 'two'), (3, 'three')",
(),
)
.expect("Failed to insert");
db.execute("BEGIN", ()).expect("Failed to begin");
db.execute("DELETE FROM test_delete WHERE id = 2", ())
.expect("Failed to delete");
db.execute("COMMIT", ()).expect("Failed to commit");
let count: i64 = db
.query_one("SELECT COUNT(*) FROM test_delete", ())
.expect("Failed to count");
assert_eq!(count, 2, "Should have 2 rows after delete");
let count_2: i64 = db
.query_one("SELECT COUNT(*) FROM test_delete WHERE id = 2", ())
.expect("Failed to count");
assert_eq!(count_2, 0, "Row with id=2 should be deleted");
}
#[test]
fn test_transaction_update() {
let db = Database::open("memory://mvcc_update").expect("Failed to create database");
db.execute(
"CREATE TABLE test_update (id INTEGER PRIMARY KEY, counter INTEGER)",
(),
)
.expect("Failed to create table");
db.execute("INSERT INTO test_update (id, counter) VALUES (1, 0)", ())
.expect("Failed to insert");
db.execute("BEGIN", ()).expect("Failed to begin");
db.execute(
"UPDATE test_update SET counter = counter + 1 WHERE id = 1",
(),
)
.expect("Failed to update 1");
db.execute(
"UPDATE test_update SET counter = counter + 1 WHERE id = 1",
(),
)
.expect("Failed to update 2");
db.execute(
"UPDATE test_update SET counter = counter + 1 WHERE id = 1",
(),
)
.expect("Failed to update 3");
db.execute("COMMIT", ()).expect("Failed to commit");
let counter: i64 = db
.query_one("SELECT counter FROM test_update WHERE id = 1", ())
.expect("Failed to query");
assert_eq!(counter, 3, "Counter should be 3 after three increments");
}
#[test]
fn test_read_own_changes() {
let db = Database::open("memory://mvcc_own_changes").expect("Failed to create database");
db.execute(
"CREATE TABLE test_own (id INTEGER PRIMARY KEY, value TEXT)",
(),
)
.expect("Failed to create table");
let mut tx = db.begin().expect("Failed to begin");
tx.execute("INSERT INTO test_own (id, value) VALUES (1, 'new')", ())
.expect("Failed to insert");
let result = tx
.query("SELECT value FROM test_own WHERE id = 1", ())
.expect("Failed to query in tx");
let mut found = false;
for row in result {
let row = row.expect("Failed to get row");
let value: String = row.get(0).unwrap();
assert_eq!(value, "new");
found = true;
}
assert!(found, "Should be able to read own uncommitted insert");
tx.commit().expect("Failed to commit");
}
#[test]
fn test_transaction_aggregates() {
let db = Database::open("memory://mvcc_agg").expect("Failed to create database");
db.execute(
"CREATE TABLE test_agg (id INTEGER PRIMARY KEY, amount INTEGER)",
(),
)
.expect("Failed to create table");
db.execute(
"INSERT INTO test_agg (id, amount) VALUES (1, 100), (2, 200), (3, 300)",
(),
)
.expect("Failed to insert");
let initial_sum: i64 = db
.query_one("SELECT SUM(amount) FROM test_agg", ())
.expect("Failed to sum");
assert_eq!(initial_sum, 600, "Initial sum should be 600");
db.execute("BEGIN", ()).expect("Failed to begin");
db.execute("INSERT INTO test_agg (id, amount) VALUES (4, 400)", ())
.expect("Failed to insert in tx");
db.execute("COMMIT", ()).expect("Failed to commit");
let final_sum: i64 = db
.query_one("SELECT SUM(amount) FROM test_agg", ())
.expect("Failed to sum");
assert_eq!(final_sum, 1000, "Final sum should be 1000");
}