use motedb::{sql::QueryResult, types::Value, Database};
use tempfile::TempDir;
fn setup() -> (Database, TempDir) {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
(db, dir)
}
fn exec(db: &Database, sql: &str) -> QueryResult {
db.execute(sql).unwrap().materialize().unwrap()
}
fn rows(db: &Database, sql: &str) -> Vec<Vec<Value>> {
match exec(db, sql) {
QueryResult::Select { rows, .. } => rows,
_ => vec![],
}
}
fn row(db: &Database, sql: &str) -> Vec<Value> {
rows(db, sql).into_iter().next().unwrap()
}
fn val(db: &Database, sql: &str) -> Value {
row(db, sql).into_iter().next().unwrap()
}
fn int_val(db: &Database, sql: &str) -> i64 {
match val(db, sql) {
Value::Integer(i) => i,
v => panic!("Expected Integer, got {:?}", v),
}
}
fn float_val(db: &Database, sql: &str) -> f64 {
match val(db, sql) {
Value::Float(f) => f,
v => panic!("Expected Float, got {:?}", v),
}
}
fn text_val(db: &Database, sql: &str) -> String {
match val(db, sql) {
Value::Text(s) => s.to_string(),
v => panic!("Expected Text, got {:?}", v),
}
}
#[test]
fn test_select_single_row_exact_values() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, name TEXT, score FLOAT, active BOOLEAN)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 'Alice', 95.5, TRUE)")
.unwrap();
let r = row(&db, "SELECT id, name, score, active FROM t");
assert_eq!(r[0], Value::Integer(1));
assert_eq!(r[1], Value::Text("Alice".into()));
match &r[2] {
Value::Float(f) => assert!((f - 95.5).abs() < 0.001),
v => panic!("{:?}", v),
}
assert_eq!(r[3], Value::Bool(true));
}
#[test]
fn test_select_column_order_matches_create_table() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, z TEXT, a INT, m FLOAT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 'hello', 42, 3.14)")
.unwrap();
let r = row(&db, "SELECT * FROM t");
assert_eq!(r[0], Value::Integer(1));
assert_eq!(r[1], Value::Text("hello".into()));
assert_eq!(r[2], Value::Integer(42));
match &r[3] {
Value::Float(f) => assert!((f - 3.14).abs() < 0.001),
v => panic!("{:?}", v),
}
}
#[test]
fn test_select_specific_columns_reorder() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, a INT, b TEXT, c FLOAT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 10, 'x', 2.5)")
.unwrap();
let r = row(&db, "SELECT c, a, id FROM t");
assert_eq!(r.len(), 3);
match &r[0] {
Value::Float(f) => assert!((f - 2.5).abs() < 0.001),
v => panic!("{:?}", v),
}
assert_eq!(r[1], Value::Integer(10));
assert_eq!(r[2], Value::Integer(1));
}
#[test]
fn test_select_multiple_rows_all_returned() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
for i in 0..20 {
db.execute(&format!("INSERT INTO t VALUES ({}, {})", i, i * 10))
.unwrap();
}
let rs = rows(&db, "SELECT id, v FROM t");
assert_eq!(rs.len(), 20, "All 20 rows must be returned");
let mut found = std::collections::HashSet::new();
for r in &rs {
assert_eq!(r.len(), 2);
if let Value::Integer(id) = r[0] {
if let Value::Integer(v) = r[1] {
assert_eq!(v, id * 10, "v must be id * 10 for id={}", id);
found.insert(id);
} else {
panic!("v not Integer");
}
} else {
panic!("id not Integer");
}
}
assert_eq!(found.len(), 20);
}
#[test]
fn test_select_count_star_exact() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY)").unwrap();
assert_eq!(int_val(&db, "SELECT COUNT(*) FROM t"), 0);
for i in 0..50 {
db.execute(&format!("INSERT INTO t VALUES ({})", i))
.unwrap();
}
assert_eq!(int_val(&db, "SELECT COUNT(*) FROM t"), 50);
db.execute("DELETE FROM t WHERE id < 25").unwrap();
assert_eq!(int_val(&db, "SELECT COUNT(*) FROM t"), 25);
db.execute("DELETE FROM t").unwrap();
assert_eq!(int_val(&db, "SELECT COUNT(*) FROM t"), 0);
}
#[test]
fn test_select_expressions_exact() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, price FLOAT, qty INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 9.99, 3)").unwrap();
let r = row(
&db,
"SELECT price * qty, price + 0.01, qty - 1, qty / 2 FROM t",
);
match &r[0] {
Value::Float(f) => assert!((f - 29.97).abs() < 0.01),
v => panic!("{:?}", v),
}
match &r[1] {
Value::Float(f) => assert!((f - 10.0).abs() < 0.01),
v => panic!("{:?}", v),
}
assert_eq!(r[2], Value::Integer(2));
assert_eq!(r[3], Value::Integer(1)); }
#[test]
fn test_where_eq_exactly_one_match() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
for i in 0..100 {
db.execute(&format!("INSERT INTO t VALUES ({}, {})", i, i))
.unwrap();
}
let rs = rows(&db, "SELECT id FROM t WHERE v = 50");
assert_eq!(rs.len(), 1);
assert_eq!(rs[0][0], Value::Integer(50));
}
#[test]
fn test_where_range_boundaries() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
for i in 0..100 {
db.execute(&format!("INSERT INTO t VALUES ({}, {})", i, i))
.unwrap();
}
let rs = rows(&db, "SELECT v FROM t WHERE v >= 10 AND v < 20 ORDER BY v");
assert_eq!(rs.len(), 10);
for (i, r) in rs.iter().enumerate() {
assert_eq!(r[0], Value::Integer(10 + i as i64));
}
let rs = rows(&db, "SELECT v FROM t WHERE v > 95 ORDER BY v");
assert_eq!(rs.len(), 4);
for (i, r) in rs.iter().enumerate() {
assert_eq!(r[0], Value::Integer(96 + i as i64));
}
let rs = rows(&db, "SELECT v FROM t WHERE v <= 3 ORDER BY v");
assert_eq!(rs.len(), 4);
for (i, r) in rs.iter().enumerate() {
assert_eq!(r[0], Value::Integer(i as i64));
}
}
#[test]
fn test_where_between_inclusive() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
for i in 0..20 {
db.execute(&format!("INSERT INTO t VALUES ({}, {})", i, i))
.unwrap();
}
let rs = rows(&db, "SELECT v FROM t WHERE v BETWEEN 5 AND 10 ORDER BY v");
assert_eq!(rs.len(), 6); assert_eq!(rs[0][0], Value::Integer(5));
assert_eq!(rs[5][0], Value::Integer(10));
}
#[test]
fn test_where_in_list() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
for i in 0..20 {
db.execute(&format!("INSERT INTO t VALUES ({}, {})", i, i))
.unwrap();
}
let rs = rows(&db, "SELECT v FROM t WHERE v IN (3, 7, 15, 99) ORDER BY v");
assert_eq!(rs.len(), 3); assert_eq!(rs[0][0], Value::Integer(3));
assert_eq!(rs[1][0], Value::Integer(7));
assert_eq!(rs[2][0], Value::Integer(15));
}
#[test]
fn test_where_not_in() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
for i in 0..10 {
db.execute(&format!("INSERT INTO t VALUES ({}, {})", i, i))
.unwrap();
}
let rs = rows(&db, "SELECT v FROM t WHERE v NOT IN (0, 1, 2) ORDER BY v");
assert_eq!(rs.len(), 7);
assert_eq!(rs[0][0], Value::Integer(3));
assert_eq!(rs[6][0], Value::Integer(9));
}
#[test]
fn test_where_or_independent() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, category TEXT, v INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 'A', 10)").unwrap();
db.execute("INSERT INTO t VALUES (2, 'A', 20)").unwrap();
db.execute("INSERT INTO t VALUES (3, 'B', 30)").unwrap();
db.execute("INSERT INTO t VALUES (4, 'C', 40)").unwrap();
let rs = rows(
&db,
"SELECT id FROM t WHERE category = 'A' OR v = 30 ORDER BY id",
);
assert_eq!(rs.len(), 3); assert_eq!(rs[0][0], Value::Integer(1));
assert_eq!(rs[1][0], Value::Integer(2));
assert_eq!(rs[2][0], Value::Integer(3));
}
#[test]
fn test_where_complex_boolean() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, a INT, b INT, c INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 10, 15, 0)").unwrap(); db.execute("INSERT INTO t VALUES (2, 3, 10, 0)").unwrap(); db.execute("INSERT INTO t VALUES (3, 10, 25, 0)").unwrap(); db.execute("INSERT INTO t VALUES (4, 1, 30, 100)").unwrap(); db.execute("INSERT INTO t VALUES (5, 10, 10, 100)").unwrap();
let rs = rows(
&db,
"SELECT id FROM t WHERE (a > 5 AND b < 20) OR c = 100 ORDER BY id",
);
assert_eq!(rs.len(), 3);
assert_eq!(rs[0][0], Value::Integer(1));
assert_eq!(rs[1][0], Value::Integer(4));
assert_eq!(rs[2][0], Value::Integer(5));
}
#[test]
fn test_where_null_filtering() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 10)").unwrap();
db.execute("INSERT INTO t VALUES (2, NULL)").unwrap();
db.execute("INSERT INTO t VALUES (3, 30)").unwrap();
db.execute("INSERT INTO t VALUES (4, NULL)").unwrap();
let rs = rows(&db, "SELECT id FROM t WHERE v = 10");
assert_eq!(rs.len(), 1);
assert_eq!(rs[0][0], Value::Integer(1));
let rs = rows(&db, "SELECT id FROM t WHERE v IS NULL ORDER BY id");
assert_eq!(rs.len(), 2);
assert_eq!(rs[0][0], Value::Integer(2));
assert_eq!(rs[1][0], Value::Integer(4));
let rs = rows(&db, "SELECT id FROM t WHERE v IS NOT NULL ORDER BY id");
assert_eq!(rs.len(), 2);
assert_eq!(rs[0][0], Value::Integer(1));
assert_eq!(rs[1][0], Value::Integer(3));
let rs = rows(&db, "SELECT id FROM t WHERE v <> 10");
assert_eq!(rs.len(), 1);
assert_eq!(rs[0][0], Value::Integer(3));
}
#[test]
fn test_where_like_patterns() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, name TEXT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 'Alice')").unwrap();
db.execute("INSERT INTO t VALUES (2, 'alice')").unwrap();
db.execute("INSERT INTO t VALUES (3, 'Bob')").unwrap();
db.execute("INSERT INTO t VALUES (4, 'Alicia')").unwrap();
db.execute("INSERT INTO t VALUES (5, 'Malice')").unwrap();
let rs = rows(&db, "SELECT name FROM t WHERE name LIKE 'A%' ORDER BY id");
assert_eq!(rs.len(), 2);
assert_eq!(rs[0][0], Value::Text("Alice".into()));
assert_eq!(rs[1][0], Value::Text("Alicia".into()));
let rs = rows(
&db,
"SELECT name FROM t WHERE name LIKE '%lic%' ORDER BY id",
);
assert!(rs.len() >= 3);
let rs = rows(&db, "SELECT name FROM t WHERE name LIKE 'Bob'");
assert_eq!(rs.len(), 1);
}
#[test]
fn test_order_by_asc_exact() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (3, 30)").unwrap();
db.execute("INSERT INTO t VALUES (1, 10)").unwrap();
db.execute("INSERT INTO t VALUES (4, 40)").unwrap();
db.execute("INSERT INTO t VALUES (2, 20)").unwrap();
let rs = rows(&db, "SELECT v FROM t ORDER BY v ASC");
assert_eq!(rs.len(), 4);
assert_eq!(rs[0][0], Value::Integer(10));
assert_eq!(rs[1][0], Value::Integer(20));
assert_eq!(rs[2][0], Value::Integer(30));
assert_eq!(rs[3][0], Value::Integer(40));
}
#[test]
fn test_order_by_desc_exact() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
for i in [5, 1, 9, 3, 7] {
db.execute(&format!("INSERT INTO t VALUES ({}, {})", i, i * 10))
.unwrap();
}
let rs = rows(&db, "SELECT id, v FROM t ORDER BY v DESC");
let expected: Vec<(i64, i64)> = vec![(9, 90), (7, 70), (5, 50), (3, 30), (1, 10)];
for (i, (eid, ev)) in expected.iter().enumerate() {
assert_eq!(rs[i][0], Value::Integer(*eid), "Row {} id mismatch", i);
assert_eq!(rs[i][1], Value::Integer(*ev), "Row {} v mismatch", i);
}
}
#[test]
fn test_order_by_with_nulls() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 30)").unwrap();
db.execute("INSERT INTO t VALUES (2, NULL)").unwrap();
db.execute("INSERT INTO t VALUES (3, 10)").unwrap();
db.execute("INSERT INTO t VALUES (4, NULL)").unwrap();
db.execute("INSERT INTO t VALUES (5, 20)").unwrap();
let rs = rows(&db, "SELECT id, v FROM t ORDER BY v ASC");
let non_null: Vec<i64> = rs
.iter()
.filter(|r| !matches!(r[1], Value::Null))
.map(|r| match r[1] {
Value::Integer(v) => v,
_ => panic!(),
})
.collect();
assert_eq!(
non_null,
vec![10, 20, 30],
"Non-null values must be in ASC order"
);
}
#[test]
fn test_order_by_expression() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, a INT, b INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 5, 3)").unwrap();
db.execute("INSERT INTO t VALUES (2, 1, 1)").unwrap();
db.execute("INSERT INTO t VALUES (3, 2, 8)").unwrap();
let rs = rows(&db, "SELECT id, a + b FROM t ORDER BY a + b ASC");
assert_eq!(rs.len(), 3);
assert_eq!(rs[0][0], Value::Integer(2));
assert_eq!(rs[1][0], Value::Integer(1));
assert_eq!(rs[2][0], Value::Integer(3));
}
#[test]
fn test_limit_offset_exact() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
for i in 0..20 {
db.execute(&format!("INSERT INTO t VALUES ({}, {})", i, i))
.unwrap();
}
let rs = rows(&db, "SELECT v FROM t ORDER BY v LIMIT 5");
assert_eq!(rs.len(), 5);
for (i, r) in rs.iter().enumerate() {
assert_eq!(r[0], Value::Integer(i as i64));
}
let rs = rows(&db, "SELECT v FROM t ORDER BY v LIMIT 5 OFFSET 10");
assert_eq!(rs.len(), 5);
for (i, r) in rs.iter().enumerate() {
assert_eq!(r[0], Value::Integer(10 + i as i64));
}
let rs = rows(&db, "SELECT v FROM t ORDER BY v LIMIT 5 OFFSET 100");
assert!(rs.is_empty());
let rs = rows(&db, "SELECT v FROM t LIMIT 0");
assert!(rs.is_empty());
let rs = rows(&db, "SELECT v FROM t ORDER BY v LIMIT 999");
assert_eq!(rs.len(), 20);
}
#[test]
fn test_distinct_exact() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, category TEXT, v INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 'A', 10)").unwrap();
db.execute("INSERT INTO t VALUES (2, 'B', 20)").unwrap();
db.execute("INSERT INTO t VALUES (3, 'A', 30)").unwrap();
db.execute("INSERT INTO t VALUES (4, 'C', 40)").unwrap();
db.execute("INSERT INTO t VALUES (5, 'B', 50)").unwrap();
let rs = rows(&db, "SELECT DISTINCT category FROM t ORDER BY category");
assert_eq!(rs.len(), 3);
assert_eq!(rs[0][0], Value::Text("A".into()));
assert_eq!(rs[1][0], Value::Text("B".into()));
assert_eq!(rs[2][0], Value::Text("C".into()));
}
#[test]
fn test_distinct_multi_column() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, a INT, b INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 1, 1)").unwrap();
db.execute("INSERT INTO t VALUES (2, 1, 2)").unwrap();
db.execute("INSERT INTO t VALUES (3, 1, 1)").unwrap(); db.execute("INSERT INTO t VALUES (4, 2, 1)").unwrap();
let rs = rows(&db, "SELECT DISTINCT a, b FROM t ORDER BY a, b");
assert_eq!(rs.len(), 3); assert_eq!(rs[0], vec![Value::Integer(1), Value::Integer(1)]);
assert_eq!(rs[1], vec![Value::Integer(1), Value::Integer(2)]);
assert_eq!(rs[2], vec![Value::Integer(2), Value::Integer(1)]);
}
#[test]
fn test_sum_exact() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
for i in 1..=100 {
db.execute(&format!("INSERT INTO t VALUES ({}, {})", i, i))
.unwrap();
}
assert_eq!(int_val(&db, "SELECT SUM(v) FROM t"), 5050);
}
#[test]
fn test_avg_exact() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
for v in [10, 20, 30, 40, 50] {
db.execute(&format!("INSERT INTO t VALUES ({}, {})", v, v))
.unwrap();
}
let avg = float_val(&db, "SELECT AVG(v) FROM t");
assert!((avg - 30.0).abs() < 0.001);
}
#[test]
fn test_min_max_exact() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
let values: [i64; 5] = [42, -10, 0, 999, 7];
for v in &values {
db.execute(&format!(
"INSERT INTO t VALUES ({}, {})",
v.abs() + v.signum() * 100,
v
))
.unwrap();
}
assert_eq!(int_val(&db, "SELECT MIN(v) FROM t"), -10);
assert_eq!(int_val(&db, "SELECT MAX(v) FROM t"), 999);
}
#[test]
fn test_count_column_skips_nulls() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 10)").unwrap();
db.execute("INSERT INTO t VALUES (2, NULL)").unwrap();
db.execute("INSERT INTO t VALUES (3, 30)").unwrap();
db.execute("INSERT INTO t VALUES (4, NULL)").unwrap();
db.execute("INSERT INTO t VALUES (5, 50)").unwrap();
assert_eq!(int_val(&db, "SELECT COUNT(*) FROM t"), 5);
assert_eq!(int_val(&db, "SELECT COUNT(v) FROM t"), 3);
}
#[test]
fn test_aggregate_empty_table() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
assert_eq!(int_val(&db, "SELECT COUNT(*) FROM t"), 0);
let v = val(&db, "SELECT SUM(v) FROM t");
assert_eq!(v, Value::Null, "SUM on empty table should be NULL");
let v = val(&db, "SELECT AVG(v) FROM t");
assert_eq!(v, Value::Null, "AVG on empty table should be NULL");
let v = val(&db, "SELECT MIN(v) FROM t");
assert_eq!(v, Value::Null, "MIN on empty table should be NULL");
}
#[test]
fn test_sum_with_nulls() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 10)").unwrap();
db.execute("INSERT INTO t VALUES (2, NULL)").unwrap();
db.execute("INSERT INTO t VALUES (3, 20)").unwrap();
db.execute("INSERT INTO t VALUES (4, NULL)").unwrap();
db.execute("INSERT INTO t VALUES (5, 30)").unwrap();
assert_eq!(int_val(&db, "SELECT SUM(v) FROM t"), 60);
let avg = float_val(&db, "SELECT AVG(v) FROM t");
assert!((avg - 20.0).abs() < 0.001);
}
#[test]
fn test_group_by_exact_results() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, cat TEXT, v INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 'A', 10)").unwrap();
db.execute("INSERT INTO t VALUES (2, 'A', 20)").unwrap();
db.execute("INSERT INTO t VALUES (3, 'A', 30)").unwrap();
db.execute("INSERT INTO t VALUES (4, 'B', 5)").unwrap();
db.execute("INSERT INTO t VALUES (5, 'B', 15)").unwrap();
db.execute("INSERT INTO t VALUES (6, 'C', 100)").unwrap();
let rs = rows(
&db,
"SELECT cat, SUM(v), COUNT(*), AVG(v), MIN(v), MAX(v) FROM t GROUP BY cat ORDER BY cat",
);
assert_eq!(rs.len(), 3);
assert_eq!(rs[0][0], Value::Text("A".into()));
assert_eq!(rs[0][1], Value::Integer(60)); assert_eq!(rs[0][2], Value::Integer(3)); match &rs[0][3] {
Value::Float(f) => assert!((f - 20.0).abs() < 0.001),
v => panic!("{:?}", v),
} assert_eq!(rs[0][4], Value::Integer(10)); assert_eq!(rs[0][5], Value::Integer(30));
assert_eq!(rs[1][0], Value::Text("B".into()));
assert_eq!(rs[1][1], Value::Integer(20)); assert_eq!(rs[1][2], Value::Integer(2));
assert_eq!(rs[2][0], Value::Text("C".into()));
assert_eq!(rs[2][1], Value::Integer(100)); assert_eq!(rs[2][2], Value::Integer(1)); }
#[test]
fn test_update_single_row_exact() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, name TEXT, v INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 'Alice', 10)").unwrap();
db.execute("INSERT INTO t VALUES (2, 'Bob', 20)").unwrap();
db.execute("UPDATE t SET name = 'Alicia', v = 15 WHERE id = 1")
.unwrap();
let r = row(&db, "SELECT name, v FROM t WHERE id = 1");
assert_eq!(r[0], Value::Text("Alicia".into()));
assert_eq!(r[1], Value::Integer(15));
let r = row(&db, "SELECT name, v FROM t WHERE id = 2");
assert_eq!(r[0], Value::Text("Bob".into()));
assert_eq!(r[1], Value::Integer(20));
}
#[test]
fn test_update_expression() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 100)").unwrap();
db.execute("UPDATE t SET v = v * 2 + 1 WHERE id = 1")
.unwrap();
let v = int_val(&db, "SELECT v FROM t WHERE id = 1");
assert_eq!(v, 201); }
#[test]
fn test_update_multiple_rows() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, cat TEXT, v INT)")
.unwrap();
for i in 0..10 {
let cat = if i < 5 { "A" } else { "B" };
db.execute(&format!(
"INSERT INTO t VALUES ({}, '{}', {})",
i,
cat,
i * 10
))
.unwrap();
}
db.execute("UPDATE t SET v = v + 100 WHERE cat = 'A'")
.unwrap();
let rs = rows(&db, "SELECT v FROM t WHERE cat = 'A' ORDER BY v");
assert_eq!(rs.len(), 5);
assert_eq!(rs[0][0], Value::Integer(100)); assert_eq!(rs[4][0], Value::Integer(140));
let rs = rows(&db, "SELECT v FROM t WHERE cat = 'B' ORDER BY v");
assert_eq!(rs[0][0], Value::Integer(50));
assert_eq!(rs[4][0], Value::Integer(90));
}
#[test]
fn test_update_set_null() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 42)").unwrap();
db.execute("UPDATE t SET v = NULL WHERE id = 1").unwrap();
let r = row(&db, "SELECT v FROM t WHERE id = 1");
assert_eq!(r[0], Value::Null);
}
#[test]
fn test_delete_specific_rows_remaining_exact() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
for i in 0..20 {
db.execute(&format!("INSERT INTO t VALUES ({}, {})", i, i))
.unwrap();
}
db.execute("DELETE FROM t WHERE v >= 10 AND v < 15")
.unwrap();
let rs = rows(&db, "SELECT id FROM t ORDER BY id");
assert_eq!(rs.len(), 15);
for i in 0..10 {
assert_eq!(rs[i][0], Value::Integer(i as i64));
}
for i in 10..15 {
assert_eq!(rs[i][0], Value::Integer(i as i64 + 5));
}
}
#[test]
fn test_delete_all_then_reinsert() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v TEXT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 'old')").unwrap();
db.execute("INSERT INTO t VALUES (2, 'old')").unwrap();
db.execute("DELETE FROM t").unwrap();
assert_eq!(int_val(&db, "SELECT COUNT(*) FROM t"), 0);
db.execute("INSERT INTO t VALUES (1, 'new')").unwrap();
db.execute("INSERT INTO t VALUES (2, 'new2')").unwrap();
let rs = rows(&db, "SELECT v FROM t ORDER BY id");
assert_eq!(rs.len(), 2);
assert_eq!(rs[0][0], Value::Text("new".into()));
assert_eq!(rs[1][0], Value::Text("new2".into()));
}
#[test]
fn test_delete_nonexistent_no_effect() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 10)").unwrap();
db.execute("INSERT INTO t VALUES (2, 20)").unwrap();
db.execute("DELETE FROM t WHERE id = 999").unwrap();
assert_eq!(int_val(&db, "SELECT COUNT(*) FROM t"), 2);
}
#[test]
fn test_null_arithmetic_all_null() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, a INT, b INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, NULL, NULL)").unwrap();
db.execute("INSERT INTO t VALUES (2, 10, NULL)").unwrap();
db.execute("INSERT INTO t VALUES (3, NULL, 20)").unwrap();
let r = row(&db, "SELECT a + b FROM t WHERE id = 1");
assert_eq!(r[0], Value::Null);
let r = row(&db, "SELECT a + b FROM t WHERE id = 2");
assert_eq!(r[0], Value::Null);
let r = row(&db, "SELECT a + b FROM t WHERE id = 3");
assert_eq!(r[0], Value::Null);
}
#[test]
fn test_null_comparison_all_false() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, NULL)").unwrap();
let rs = rows(&db, "SELECT id FROM t WHERE v = NULL");
assert!(rs.is_empty(), "NULL = NULL should not match");
let rs = rows(&db, "SELECT id FROM t WHERE v <> 1");
assert!(rs.is_empty(), "NULL <> 1 should not match");
let rs = rows(&db, "SELECT id FROM t WHERE v > 0");
assert!(rs.is_empty(), "NULL > 0 should not match");
}
#[test]
fn test_null_in_and_or() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, a INT, b INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, NULL, 10)").unwrap();
db.execute("INSERT INTO t VALUES (2, 5, NULL)").unwrap();
db.execute("INSERT INTO t VALUES (3, NULL, NULL)").unwrap();
let _rs = rows(&db, "SELECT id FROM t WHERE a > 0 AND b = 10");
let rs = rows(&db, "SELECT id FROM t WHERE b = 10 OR a > 0 ORDER BY id");
assert_eq!(rs.len(), 2, "TRUE OR NULL should return rows 1 and 2");
assert_eq!(rs[0][0], Value::Integer(1));
assert_eq!(rs[1][0], Value::Integer(2));
}
#[test]
fn test_inner_join_exact_rows() {
let (db, _dir) = setup();
db.execute("CREATE TABLE orders (id INT PRIMARY KEY, customer_id INT, amount FLOAT)")
.unwrap();
db.execute("CREATE TABLE customers (id INT PRIMARY KEY, name TEXT)")
.unwrap();
db.execute("INSERT INTO customers VALUES (1, 'Alice')")
.unwrap();
db.execute("INSERT INTO customers VALUES (2, 'Bob')")
.unwrap();
db.execute("INSERT INTO customers VALUES (3, 'Charlie')")
.unwrap();
db.execute("INSERT INTO orders VALUES (101, 1, 50.0)")
.unwrap();
db.execute("INSERT INTO orders VALUES (102, 1, 30.0)")
.unwrap();
db.execute("INSERT INTO orders VALUES (103, 2, 80.0)")
.unwrap();
let rs = rows(&db,
"SELECT o.id, c.name, o.amount FROM orders o INNER JOIN customers c ON o.customer_id = c.id ORDER BY o.id");
assert_eq!(rs.len(), 3, "INNER JOIN should return only matching rows");
assert_eq!(rs[0][0], Value::Integer(101));
assert_eq!(rs[0][1], Value::Text("Alice".into()));
match &rs[0][2] {
Value::Float(f) => assert!((f - 50.0).abs() < 0.01),
v => panic!("{:?}", v),
}
assert_eq!(rs[1][0], Value::Integer(102));
assert_eq!(rs[1][1], Value::Text("Alice".into()));
assert_eq!(rs[2][0], Value::Integer(103));
assert_eq!(rs[2][1], Value::Text("Bob".into()));
}
#[test]
fn test_left_join_null_fill() {
let (db, _dir) = setup();
db.execute("CREATE TABLE a (id INT PRIMARY KEY, name TEXT)")
.unwrap();
db.execute("CREATE TABLE b (id INT PRIMARY KEY, a_id INT, val TEXT)")
.unwrap();
db.execute("INSERT INTO a VALUES (1, 'X')").unwrap();
db.execute("INSERT INTO a VALUES (2, 'Y')").unwrap();
db.execute("INSERT INTO b VALUES (10, 1, 'hello')").unwrap();
let rs = rows(
&db,
"SELECT a.id, a.name, b.val FROM a LEFT JOIN b ON a.id = b.a_id ORDER BY a.id",
);
assert_eq!(rs.len(), 2);
assert_eq!(rs[0][0], Value::Integer(1));
assert_eq!(rs[0][1], Value::Text("X".into()));
assert_eq!(rs[0][2], Value::Text("hello".into()));
assert_eq!(rs[1][0], Value::Integer(2));
assert_eq!(rs[1][1], Value::Text("Y".into()));
assert_eq!(
rs[1][2],
Value::Null,
"LEFT JOIN unmatched should fill with NULL"
);
}
#[test]
fn test_column_index_consistency() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, cat TEXT, v INT)")
.unwrap();
for i in 0..50 {
let cat = if i % 3 == 0 {
"A"
} else if i % 3 == 1 {
"B"
} else {
"C"
};
db.execute(&format!(
"INSERT INTO t VALUES ({}, '{}', {})",
i,
cat,
i * 10
))
.unwrap();
}
let no_idx = rows(&db, "SELECT id, v FROM t WHERE cat = 'B' ORDER BY id");
db.execute("CREATE INDEX idx_cat ON t (cat)").unwrap();
db.wait_for_indexes_ready();
let with_idx = rows(&db, "SELECT id, v FROM t WHERE cat = 'B' ORDER BY id");
assert_eq!(
no_idx.len(),
with_idx.len(),
"Index should not change result count"
);
for (a, b) in no_idx.iter().zip(with_idx.iter()) {
assert_eq!(a, b, "Results must be identical with and without index");
}
}
#[test]
fn test_range_query_index_consistency() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
for i in 0..100 {
db.execute(&format!("INSERT INTO t VALUES ({}, {})", i, i * 5))
.unwrap();
}
let no_idx = rows(
&db,
"SELECT id FROM t WHERE v >= 200 AND v < 350 ORDER BY id",
);
db.execute("CREATE INDEX idx_v ON t (v)").unwrap();
db.wait_for_indexes_ready();
let with_idx = rows(
&db,
"SELECT id FROM t WHERE v >= 200 AND v < 350 ORDER BY id",
);
assert_eq!(no_idx, with_idx, "Range query results must be identical");
}
#[test]
fn test_flush_preserves_exact_data() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, name TEXT, score FLOAT)")
.unwrap();
for i in 0..30 {
db.execute(&format!(
"INSERT INTO t VALUES ({}, 'user_{}', {:.1})",
i,
i,
i as f64 * 1.5
))
.unwrap();
}
let before = rows(&db, "SELECT id, name, score FROM t ORDER BY id");
db.flush().unwrap();
let after = rows(&db, "SELECT id, name, score FROM t ORDER BY id");
assert_eq!(
before.len(),
after.len(),
"Row count must be identical after flush"
);
for (a, b) in before.iter().zip(after.iter()) {
assert_eq!(a, b, "Row data must be identical after flush");
}
db.close().ok();
}
#[test]
fn test_restart_preserves_exact_data() {
let dir = TempDir::new().unwrap();
let path = dir.path().to_path_buf();
{
let db = Database::create(&path).unwrap();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT, name TEXT)")
.unwrap();
for i in 0..50 {
db.execute(&format!(
"INSERT INTO t VALUES ({}, {}, 'name_{}')",
i,
i * 7,
i
))
.unwrap();
}
db.flush().unwrap();
db.close().unwrap();
}
{
let db = Database::open(&path).unwrap();
let rs = rows(&db, "SELECT id, v, name FROM t ORDER BY id");
assert_eq!(rs.len(), 50);
for (i, r) in rs.iter().enumerate() {
assert_eq!(r[0], Value::Integer(i as i64));
assert_eq!(r[1], Value::Integer((i as i64) * 7));
assert_eq!(r[2], Value::Text(format!("name_{}", i).into()));
}
db.close().unwrap();
}
}
#[test]
fn test_restart_preserves_deletes_and_updates() {
let dir = TempDir::new().unwrap();
let path = dir.path().to_path_buf();
{
let db = Database::create(&path).unwrap();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
for i in 0..20 {
db.execute(&format!("INSERT INTO t VALUES ({}, {})", i, i * 10))
.unwrap();
}
db.execute("DELETE FROM t WHERE id >= 15").unwrap();
db.execute("UPDATE t SET v = -1 WHERE id < 5").unwrap();
db.flush().unwrap();
db.close().unwrap();
}
{
let db = Database::open(&path).unwrap();
let rs = rows(&db, "SELECT id, v FROM t ORDER BY id");
assert_eq!(rs.len(), 15);
for r in &rs[0..5] {
assert_eq!(
r[1],
Value::Integer(-1),
"id={:?} should have v=-1 but got {:?}",
r[0],
r[1]
);
}
for r in &rs[5..15] {
let id = match r[0] {
Value::Integer(i) => i,
_ => panic!(),
};
assert_eq!(r[1], Value::Integer(id * 10));
}
db.close().unwrap();
}
}
#[test]
fn test_string_functions_exact() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, s TEXT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 'Hello World')")
.unwrap();
assert_eq!(
text_val(&db, "SELECT LOWER(s) FROM t WHERE id = 1"),
"hello world"
);
assert_eq!(
text_val(&db, "SELECT UPPER(s) FROM t WHERE id = 1"),
"HELLO WORLD"
);
assert_eq!(int_val(&db, "SELECT LENGTH(s) FROM t WHERE id = 1"), 11);
assert_eq!(text_val(&db, "SELECT TRIM(' hi ')"), "hi");
assert_eq!(
text_val(&db, "SELECT CONCAT(s, '!') FROM t WHERE id = 1"),
"Hello World!"
);
}
#[test]
fn test_math_functions_exact() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v FLOAT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 16.0)").unwrap();
db.execute("INSERT INTO t VALUES (2, -7.5)").unwrap();
assert!((float_val(&db, "SELECT ABS(v) FROM t WHERE id = 2") - 7.5).abs() < 0.001);
assert!((float_val(&db, "SELECT SQRT(v) FROM t WHERE id = 1") - 4.0).abs() < 0.001);
assert!((float_val(&db, "SELECT ROUND(3.7)") - 4.0).abs() < 0.001);
assert!((float_val(&db, "SELECT ROUND(3.3)") - 3.0).abs() < 0.001);
assert!((float_val(&db, "SELECT ABS(v) FROM t WHERE id = 1") - 16.0).abs() < 0.001);
}
#[test]
fn test_empty_string_vs_null() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, s TEXT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, '')").unwrap();
db.execute("INSERT INTO t VALUES (2, NULL)").unwrap();
let r1 = row(&db, "SELECT s FROM t WHERE id = 1");
assert_eq!(
r1[0],
Value::Text("".into()),
"Empty string should be Text, not Null"
);
let r2 = row(&db, "SELECT s FROM t WHERE id = 2");
assert_eq!(r2[0], Value::Null, "NULL should be Null");
assert_eq!(int_val(&db, "SELECT LENGTH(s) FROM t WHERE id = 1"), 0);
assert_eq!(
val(&db, "SELECT LENGTH(s) FROM t WHERE id = 2"),
Value::Null
);
}
#[test]
fn test_zero_and_negative_values() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 0)").unwrap();
db.execute("INSERT INTO t VALUES (2, -1)").unwrap();
db.execute("INSERT INTO t VALUES (3, -999)").unwrap();
db.execute("INSERT INTO t VALUES (4, 999)").unwrap();
let rs = rows(&db, "SELECT v FROM t WHERE v < 0 ORDER BY v");
assert_eq!(rs.len(), 2);
assert_eq!(rs[0][0], Value::Integer(-999));
assert_eq!(rs[1][0], Value::Integer(-1));
let rs = rows(&db, "SELECT v FROM t WHERE v = 0");
assert_eq!(rs.len(), 1);
}
#[test]
fn test_large_integer_values() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 9223372036854775807)")
.unwrap(); db.execute("INSERT INTO t VALUES (2, -9223372036854775808)")
.unwrap(); db.execute("INSERT INTO t VALUES (3, 0)").unwrap();
assert_eq!(int_val(&db, "SELECT v FROM t WHERE id = 1"), i64::MAX);
assert_eq!(int_val(&db, "SELECT v FROM t WHERE id = 2"), i64::MIN);
assert_eq!(int_val(&db, "SELECT v FROM t WHERE id = 3"), 0);
}
#[test]
fn test_unicode_text_exact() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, s TEXT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, '你好世界')").unwrap();
db.execute("INSERT INTO t VALUES (2, '🎉🚀')").unwrap();
db.execute("INSERT INTO t VALUES (3, 'Héllo Wörld')")
.unwrap();
assert_eq!(text_val(&db, "SELECT s FROM t WHERE id = 1"), "你好世界");
assert_eq!(text_val(&db, "SELECT s FROM t WHERE id = 2"), "🎉🚀");
assert_eq!(text_val(&db, "SELECT s FROM t WHERE id = 3"), "Héllo Wörld");
}
#[test]
fn test_multiple_tables_isolation() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t1 (id INT PRIMARY KEY, v INT)")
.unwrap();
db.execute("CREATE TABLE t2 (id INT PRIMARY KEY, v INT)")
.unwrap();
for i in 0..10 {
db.execute(&format!("INSERT INTO t1 VALUES ({}, {})", i, i))
.unwrap();
db.execute(&format!("INSERT INTO t2 VALUES ({}, {})", i, i * 100))
.unwrap();
}
assert_eq!(int_val(&db, "SELECT SUM(v) FROM t1"), 45); assert_eq!(int_val(&db, "SELECT SUM(v) FROM t2"), 4500);
db.execute("DELETE FROM t1 WHERE id < 5").unwrap();
assert_eq!(int_val(&db, "SELECT COUNT(*) FROM t1"), 5);
assert_eq!(
int_val(&db, "SELECT COUNT(*) FROM t2"),
10,
"t2 should be unaffected"
);
}
#[test]
fn test_coalesce_exact() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, a INT, b INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, NULL, 10)").unwrap();
db.execute("INSERT INTO t VALUES (2, 20, 30)").unwrap();
db.execute("INSERT INTO t VALUES (3, NULL, NULL)").unwrap();
let rs = rows(&db, "SELECT id, COALESCE(a, b, 0) FROM t ORDER BY id");
assert_eq!(rs[0][1], Value::Integer(10)); assert_eq!(rs[1][1], Value::Integer(20)); assert_eq!(rs[2][1], Value::Integer(0)); }
#[test]
fn test_float_precision_roundtrip() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v FLOAT)")
.unwrap();
let test_values = [0.1, 0.2, 3.14159265, -1.5, 1e10, 1e-10, 0.0];
for (i, v) in test_values.iter().enumerate() {
db.execute(&format!("INSERT INTO t VALUES ({}, {})", i, v))
.unwrap();
}
for (i, expected) in test_values.iter().enumerate() {
let got = float_val(&db, &format!("SELECT v FROM t WHERE id = {}", i));
assert!(
(got - expected).abs() < expected.abs() * 1e-6 + 1e-12,
"Float precision lost for value {}: got {}, expected {}",
expected,
got,
expected
);
}
}
#[test]
fn test_boolean_values_exact() {
let (db, _dir) = setup();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, flag BOOLEAN)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, TRUE)").unwrap();
db.execute("INSERT INTO t VALUES (2, FALSE)").unwrap();
db.execute("INSERT INTO t VALUES (3, NULL)").unwrap();
let rs = rows(&db, "SELECT flag FROM t ORDER BY id");
assert_eq!(rs[0][0], Value::Bool(true));
assert_eq!(rs[1][0], Value::Bool(false));
assert_eq!(rs[2][0], Value::Null);
}
#[test]
fn test_where_with_subquery() {
let (db, _dir) = setup();
db.execute("CREATE TABLE orders (id INT PRIMARY KEY, amount INT)")
.unwrap();
db.execute("INSERT INTO orders VALUES (1, 50)").unwrap();
db.execute("INSERT INTO orders VALUES (2, 150)").unwrap();
db.execute("INSERT INTO orders VALUES (3, 80)").unwrap();
db.execute("INSERT INTO orders VALUES (4, 200)").unwrap();
let rs = rows(
&db,
"SELECT id, amount FROM orders WHERE amount > (SELECT AVG(amount) FROM orders) ORDER BY id",
);
assert_eq!(rs.len(), 2);
assert_eq!(rs[0][0], Value::Integer(2)); assert_eq!(rs[1][0], Value::Integer(4)); }
#[test]
fn test_concurrent_reads_see_consistent_data() {
use std::sync::Arc;
use std::thread;
let dir = TempDir::new().unwrap();
let db = Arc::new(Database::create(dir.path()).unwrap());
db.execute("CREATE TABLE t (id INT PRIMARY KEY, v INT)")
.unwrap();
for i in 0..100 {
db.execute(&format!("INSERT INTO t VALUES ({}, {})", i, i))
.unwrap();
}
let db_clone = Arc::clone(&db);
let handle = thread::spawn(move || {
for _ in 0..20 {
let count = match db_clone
.execute("SELECT COUNT(*) FROM t")
.unwrap()
.materialize()
.unwrap()
{
QueryResult::Select { rows, .. } => match &rows[0][0] {
Value::Integer(c) => *c,
_ => -1,
},
_ => -1,
};
assert_eq!(count, 100, "Concurrent read must see consistent count");
}
});
let db2 = Arc::clone(&db);
db2.execute("CREATE TABLE t2 (id INT PRIMARY KEY, v INT)")
.unwrap();
for i in 0..50 {
db2.execute(&format!("INSERT INTO t2 VALUES ({}, {})", i, i))
.unwrap();
}
handle.join().unwrap();
db.close().ok();
}