use stoolap::Database;
fn setup_db() -> Database {
Database::open_in_memory().expect("Failed to create in-memory database")
}
#[test]
fn test_bugs3_window_coalesce_aggregate() {
let db = setup_db();
db.execute(
"CREATE TABLE window_coalesce_test (grp TEXT, val INTEGER)",
(),
)
.expect("Failed to create table");
db.execute(
"INSERT INTO window_coalesce_test VALUES ('A', 10), ('B', 30), ('C', 20)",
(),
)
.expect("Failed to insert data");
let mut rows = db
.query(
"SELECT grp, COALESCE(SUM(val), 0) as total, \
RANK() OVER (ORDER BY COALESCE(SUM(val), 0) DESC) as rnk \
FROM window_coalesce_test GROUP BY grp ORDER BY rnk",
(),
)
.expect("Query failed");
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<String>(0).unwrap(), "B");
assert_eq!(row.get::<i64>(1).unwrap(), 30);
assert_eq!(row.get::<i64>(2).unwrap(), 1);
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<String>(0).unwrap(), "C");
assert_eq!(row.get::<i64>(1).unwrap(), 20);
assert_eq!(row.get::<i64>(2).unwrap(), 2);
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<String>(0).unwrap(), "A");
assert_eq!(row.get::<i64>(1).unwrap(), 10);
assert_eq!(row.get::<i64>(2).unwrap(), 3);
}
#[test]
fn test_bugs3_window_dense_rank_coalesce() {
let db = setup_db();
db.execute("CREATE TABLE dense_rank_test (grp TEXT, val INTEGER)", ())
.expect("Failed to create table");
db.execute(
"INSERT INTO dense_rank_test VALUES ('A', 10), ('B', 20), ('C', 20)",
(),
)
.expect("Failed to insert data");
let mut rows = db
.query(
"SELECT grp, COALESCE(SUM(val), 0) as total, \
DENSE_RANK() OVER (ORDER BY COALESCE(SUM(val), 0) DESC) as rnk \
FROM dense_rank_test GROUP BY grp ORDER BY grp",
(),
)
.expect("Query failed");
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<String>(0).unwrap(), "A");
assert_eq!(row.get::<i64>(2).unwrap(), 2);
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<String>(0).unwrap(), "B");
assert_eq!(row.get::<i64>(2).unwrap(), 1);
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<String>(0).unwrap(), "C");
assert_eq!(row.get::<i64>(2).unwrap(), 1); }
#[test]
fn test_bugs3_window_row_number_coalesce() {
let db = setup_db();
db.execute("CREATE TABLE row_num_test (grp TEXT, val INTEGER)", ())
.expect("Failed to create table");
db.execute(
"INSERT INTO row_num_test VALUES ('A', 10), ('B', 30), ('C', 20)",
(),
)
.expect("Failed to insert data");
let mut rows = db
.query(
"SELECT grp, COALESCE(SUM(val), 0) as total, \
ROW_NUMBER() OVER (ORDER BY COALESCE(SUM(val), 0) DESC) as rn \
FROM row_num_test GROUP BY grp ORDER BY rn",
(),
)
.expect("Query failed");
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<String>(0).unwrap(), "B");
assert_eq!(row.get::<i64>(2).unwrap(), 1);
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<String>(0).unwrap(), "C");
assert_eq!(row.get::<i64>(2).unwrap(), 2);
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<String>(0).unwrap(), "A");
assert_eq!(row.get::<i64>(2).unwrap(), 3);
}
#[test]
fn test_bugs3_subquery_join_on_condition() {
let db = setup_db();
db.execute("CREATE TABLE subq_a (id INTEGER, val TEXT)", ())
.expect("Failed to create table");
db.execute("CREATE TABLE subq_b (id INTEGER, data TEXT)", ())
.expect("Failed to create table");
db.execute("INSERT INTO subq_a VALUES (1, 'a'), (2, 'b')", ())
.expect("Failed to insert data");
db.execute("INSERT INTO subq_b VALUES (1, 'x'), (3, 'y')", ())
.expect("Failed to insert data");
let mut rows = db
.query(
"SELECT * FROM (SELECT * FROM subq_a) AS a \
JOIN (SELECT * FROM subq_b) AS b ON a.id = b.id",
(),
)
.expect("Query failed");
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 1); assert_eq!(row.get::<String>(1).unwrap(), "a"); assert_eq!(row.get::<i64>(2).unwrap(), 1); assert_eq!(row.get::<String>(3).unwrap(), "x");
assert!(
rows.next().is_none(),
"Should have only 1 row (only id=1 matches)"
);
}
#[test]
fn test_bugs3_subquery_left_join() {
let db = setup_db();
db.execute("CREATE TABLE left_a (id INTEGER, val TEXT)", ())
.expect("Failed to create table");
db.execute("CREATE TABLE left_b (id INTEGER, data TEXT)", ())
.expect("Failed to create table");
db.execute("INSERT INTO left_a VALUES (1, 'a'), (2, 'b')", ())
.expect("Failed to insert data");
db.execute("INSERT INTO left_b VALUES (1, 'x'), (3, 'y')", ())
.expect("Failed to insert data");
let mut rows = db
.query(
"SELECT * FROM (SELECT * FROM left_a) AS a \
LEFT JOIN (SELECT * FROM left_b) AS b ON a.id = b.id \
ORDER BY a.id",
(),
)
.expect("Query failed");
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 1);
assert_eq!(row.get::<String>(1).unwrap(), "a");
assert_eq!(row.get::<i64>(2).unwrap(), 1);
assert_eq!(row.get::<String>(3).unwrap(), "x");
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 2);
assert_eq!(row.get::<String>(1).unwrap(), "b");
let value = row.get_value(2);
assert!(
value.map(|v| v.is_null()).unwrap_or(true),
"b.id should be NULL for unmatched row"
);
assert!(rows.next().is_none(), "Should have only 2 rows");
}
#[test]
fn test_bugs3_regular_table_join_still_works() {
let db = setup_db();
db.execute("CREATE TABLE reg_a (id INTEGER, val TEXT)", ())
.expect("Failed to create table");
db.execute("CREATE TABLE reg_b (id INTEGER, data TEXT)", ())
.expect("Failed to create table");
db.execute("INSERT INTO reg_a VALUES (1, 'a'), (2, 'b')", ())
.expect("Failed to insert data");
db.execute("INSERT INTO reg_b VALUES (1, 'x'), (3, 'y')", ())
.expect("Failed to insert data");
let mut rows = db
.query("SELECT * FROM reg_a a JOIN reg_b b ON a.id = b.id", ())
.expect("Query failed");
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 1);
assert!(rows.next().is_none(), "Should have only 1 row");
}
#[test]
fn test_bugs3_insert_returning_auto_increment() {
let db = setup_db();
db.execute(
"CREATE TABLE auto_ret_test (id INTEGER PRIMARY KEY AUTO_INCREMENT, name TEXT)",
(),
)
.expect("Failed to create table");
let mut rows = db
.query(
"INSERT INTO auto_ret_test (name) VALUES ('test1') RETURNING *",
(),
)
.expect("Query failed");
let row = rows.next().unwrap().unwrap();
let id = row.get::<i64>(0).unwrap();
assert_eq!(id, 1, "AUTO_INCREMENT id should be 1, got {}", id);
assert_eq!(row.get::<String>(1).unwrap(), "test1");
assert!(rows.next().is_none(), "Should return only 1 row");
}
#[test]
fn test_bugs3_insert_returning_auto_increment_multiple() {
let db = setup_db();
db.execute(
"CREATE TABLE auto_multi_test (id INTEGER PRIMARY KEY AUTO_INCREMENT, name TEXT)",
(),
)
.expect("Failed to create table");
let mut rows1 = db
.query(
"INSERT INTO auto_multi_test (name) VALUES ('first') RETURNING id",
(),
)
.expect("Query failed");
let row = rows1.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 1);
let mut rows2 = db
.query(
"INSERT INTO auto_multi_test (name) VALUES ('second') RETURNING id",
(),
)
.expect("Query failed");
let row = rows2.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 2);
let mut rows3 = db
.query(
"INSERT INTO auto_multi_test (name) VALUES ('third') RETURNING id",
(),
)
.expect("Query failed");
let row = rows3.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 3);
}
#[test]
fn test_bugs3_insert_returning_specific_columns() {
let db = setup_db();
db.execute(
"CREATE TABLE auto_cols_test (id INTEGER PRIMARY KEY AUTO_INCREMENT, name TEXT, age INTEGER)",
(),
)
.expect("Failed to create table");
let mut rows = db
.query(
"INSERT INTO auto_cols_test (name, age) VALUES ('Alice', 30) RETURNING id, name",
(),
)
.expect("Query failed");
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 1); assert_eq!(row.get::<String>(1).unwrap(), "Alice");
}
#[test]
fn test_bugs3_insert_returning_with_explicit_id() {
let db = setup_db();
db.execute(
"CREATE TABLE auto_explicit_test (id INTEGER PRIMARY KEY AUTO_INCREMENT, name TEXT)",
(),
)
.expect("Failed to create table");
let mut rows = db
.query(
"INSERT INTO auto_explicit_test (id, name) VALUES (100, 'explicit') RETURNING *",
(),
)
.expect("Query failed");
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 100);
let mut rows2 = db
.query(
"INSERT INTO auto_explicit_test (name) VALUES ('next') RETURNING id",
(),
)
.expect("Query failed");
let row = rows2.next().unwrap().unwrap();
assert_eq!(
row.get::<i64>(0).unwrap(),
101,
"Next AUTO_INCREMENT should be 101 after explicit 100"
);
}
#[test]
fn test_bugs3_where_expression_equals_column() {
let db = setup_db();
db.execute("CREATE TABLE expr_cmp (a INTEGER, b INTEGER)", ())
.expect("Failed to create table");
db.execute("INSERT INTO expr_cmp VALUES (2, 4), (3, 6), (5, 10)", ())
.expect("Failed to insert data");
let mut rows = db
.query("SELECT * FROM expr_cmp WHERE a * 2 = b ORDER BY a", ())
.expect("Query failed");
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 2);
assert_eq!(row.get::<i64>(1).unwrap(), 4);
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 3);
assert_eq!(row.get::<i64>(1).unwrap(), 6);
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 5);
assert_eq!(row.get::<i64>(1).unwrap(), 10);
assert!(rows.next().is_none());
}
#[test]
fn test_bugs3_where_column_equals_expression() {
let db = setup_db();
db.execute("CREATE TABLE expr_cmp2 (a INTEGER, b INTEGER)", ())
.expect("Failed to create table");
db.execute("INSERT INTO expr_cmp2 VALUES (2, 4), (3, 6), (5, 10)", ())
.expect("Failed to insert data");
let mut rows = db
.query("SELECT * FROM expr_cmp2 WHERE a = b / 2 ORDER BY a", ())
.expect("Query failed");
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 2);
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 3);
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 5);
assert!(rows.next().is_none());
}
#[test]
fn test_bugs3_where_expression_greater_than_column() {
let db = setup_db();
db.execute("CREATE TABLE expr_cmp3 (a INTEGER, b INTEGER)", ())
.expect("Failed to create table");
db.execute("INSERT INTO expr_cmp3 VALUES (2, 4), (3, 6), (5, 10)", ())
.expect("Failed to insert data");
let mut rows = db
.query("SELECT * FROM expr_cmp3 WHERE a * 3 > b ORDER BY a", ())
.expect("Query failed");
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 2);
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 3);
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 5);
assert!(rows.next().is_none());
}
#[test]
fn test_bugs3_where_both_sides_expressions() {
let db = setup_db();
db.execute("CREATE TABLE expr_cmp4 (a INTEGER, b INTEGER)", ())
.expect("Failed to create table");
db.execute("INSERT INTO expr_cmp4 VALUES (2, 4), (3, 7), (5, 10)", ())
.expect("Failed to insert data");
let mut rows = db
.query("SELECT * FROM expr_cmp4 WHERE a * 2 = b * 1 ORDER BY a", ())
.expect("Query failed");
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 2);
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 5);
assert!(rows.next().is_none());
}
#[test]
fn test_bugs3_where_expression_minus_column_equals_zero() {
let db = setup_db();
db.execute("CREATE TABLE expr_cmp5 (a INTEGER, b INTEGER)", ())
.expect("Failed to create table");
db.execute("INSERT INTO expr_cmp5 VALUES (2, 4), (3, 6), (5, 10)", ())
.expect("Failed to insert data");
let mut rows = db
.query("SELECT * FROM expr_cmp5 WHERE a * 2 - b = 0 ORDER BY a", ())
.expect("Query failed");
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 2);
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 3);
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 5);
assert!(rows.next().is_none());
}
#[test]
fn test_bugs3_delete_with_expression_comparison() {
let db = setup_db();
db.execute(
"CREATE TABLE delete_expr (id INTEGER PRIMARY KEY, a INTEGER, b INTEGER)",
(),
)
.expect("Failed to create table");
db.execute(
"INSERT INTO delete_expr VALUES (1, 1, 2), (2, 2, 4), (3, 3, 5)",
(),
)
.expect("Failed to insert data");
db.execute("DELETE FROM delete_expr WHERE a * 2 = b", ())
.expect("Delete failed");
let mut rows = db
.query("SELECT * FROM delete_expr ORDER BY id", ())
.expect("Query failed");
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 3);
assert_eq!(row.get::<i64>(1).unwrap(), 3);
assert_eq!(row.get::<i64>(2).unwrap(), 5);
assert!(rows.next().is_none(), "Should have only 1 row after delete");
}
#[test]
fn test_bugs3_update_with_expression_comparison() {
let db = setup_db();
db.execute("CREATE TABLE update_expr (a INTEGER, b INTEGER)", ())
.expect("Failed to create table");
db.execute("INSERT INTO update_expr VALUES (2, 4), (3, 6), (5, 10)", ())
.expect("Failed to insert data");
db.execute("UPDATE update_expr SET b = b + 1 WHERE a * 2 = b", ())
.expect("Update failed");
let mut rows = db
.query("SELECT * FROM update_expr ORDER BY a", ())
.expect("Query failed");
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 2);
assert_eq!(row.get::<i64>(1).unwrap(), 5);
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 3);
assert_eq!(row.get::<i64>(1).unwrap(), 7);
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i64>(0).unwrap(), 5);
assert_eq!(row.get::<i64>(1).unwrap(), 11);
assert!(rows.next().is_none());
}