use stoolap::api::Database;
#[test]
fn test_analyze_builds_zone_maps() {
let db = Database::open_in_memory().expect("Failed to create database");
db.execute(
"CREATE TABLE test_zonemap (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.expect("Failed to create table");
for i in 0..100 {
db.execute(
&format!("INSERT INTO test_zonemap VALUES ({}, {})", i, i * 10),
(),
)
.expect("Insert failed");
}
let _: Vec<_> = db
.query("ANALYZE test_zonemap", ())
.expect("ANALYZE failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect results");
let result: Vec<_> = db
.query("SELECT * FROM test_zonemap WHERE value > 950", ())
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
assert_eq!(result.len(), 4, "Should return 4 rows (id 96-99)");
}
#[test]
fn test_zonemap_explain_shows_pruning() {
let db = Database::open("memory://zonemap_explain").expect("Failed to create database");
db.execute(
"CREATE TABLE large_table (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.expect("Failed to create table");
for i in 0..1000 {
db.execute(
&format!("INSERT INTO large_table VALUES ({}, {})", i, i),
(),
)
.expect("Insert failed");
}
db.execute("ANALYZE large_table", ()).unwrap();
let result: Vec<_> = db
.query("EXPLAIN SELECT * FROM large_table WHERE value > 900", ())
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
assert!(!result.is_empty(), "EXPLAIN should return results");
}
#[test]
fn test_zonemap_range_query() {
let db = Database::open_in_memory().expect("Failed to create database");
db.execute(
"CREATE TABLE events (id INTEGER PRIMARY KEY, event_date INTEGER, data TEXT)",
(),
)
.expect("Failed to create table");
for i in 0..365 {
db.execute(
&format!("INSERT INTO events VALUES ({}, {}, 'event_{}')", i, i, i),
(),
)
.expect("Insert failed");
}
db.execute("ANALYZE events", ()).unwrap();
let result: Vec<_> = db
.query("SELECT COUNT(*) FROM events WHERE event_date >= 330", ())
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
let count: i64 = result[0].get(0).expect("Failed to get count");
assert_eq!(count, 35, "Should return 35 events (days 330-364)");
}
#[test]
fn test_zonemap_multiple_columns() {
let db = Database::open_in_memory().expect("Failed to create database");
db.execute(
"CREATE TABLE products (
id INTEGER PRIMARY KEY,
price INTEGER,
quantity INTEGER,
category TEXT
)",
(),
)
.expect("Failed to create table");
for i in 0..100 {
let price = (i % 10) * 100; let quantity = i * 5;
db.execute(
&format!(
"INSERT INTO products VALUES ({}, {}, {}, 'cat_{}')",
i,
price,
quantity,
i % 5
),
(),
)
.expect("Insert failed");
}
db.execute("ANALYZE products", ()).unwrap();
let result: Vec<_> = db
.query(
"SELECT * FROM products WHERE price >= 700 AND quantity > 400",
(),
)
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
for row in &result {
let price: i64 = row.get(1).expect("Failed to get price");
let quantity: i64 = row.get(2).expect("Failed to get quantity");
assert!(price >= 700, "Price should be >= 700");
assert!(quantity > 400, "Quantity should be > 400");
}
}
#[test]
fn test_zonemap_equality() {
let db = Database::open_in_memory().expect("Failed to create database");
db.execute(
"CREATE TABLE lookup (id INTEGER PRIMARY KEY, code INTEGER, name TEXT)",
(),
)
.expect("Failed to create table");
for i in 0..50 {
let code = i / 10 * 100; db.execute(
&format!("INSERT INTO lookup VALUES ({}, {}, 'item_{}')", i, code, i),
(),
)
.expect("Insert failed");
}
db.execute("ANALYZE lookup", ()).unwrap();
let result: Vec<_> = db
.query("SELECT * FROM lookup WHERE code = 200", ())
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
assert_eq!(result.len(), 10, "Should return 10 rows with code = 200");
}
#[test]
fn test_zonemap_with_nulls() {
let db = Database::open_in_memory().expect("Failed to create database");
db.execute(
"CREATE TABLE nullable_data (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.expect("Failed to create table");
for i in 0..20 {
if i % 3 == 0 {
db.execute(
&format!("INSERT INTO nullable_data VALUES ({}, NULL)", i),
(),
)
.expect("Insert failed");
} else {
db.execute(
&format!("INSERT INTO nullable_data VALUES ({}, {})", i, i * 10),
(),
)
.expect("Insert failed");
}
}
db.execute("ANALYZE nullable_data", ()).unwrap();
let result: Vec<_> = db
.query("SELECT * FROM nullable_data WHERE value > 100", ())
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
for row in &result {
let value: i64 = row.get(1).expect("Failed to get value");
assert!(value > 100, "Value should be > 100");
}
}
#[test]
fn test_zonemap_text_columns() {
let db = Database::open_in_memory().expect("Failed to create database");
db.execute(
"CREATE TABLE text_data (id INTEGER PRIMARY KEY, name TEXT)",
(),
)
.expect("Failed to create table");
let names = ["Alice", "Bob", "Charlie", "David", "Eve", "Frank", "Grace"];
for (i, name) in names.iter().enumerate() {
for j in 0..10 {
let idx = i * 10 + j;
db.execute(
&format!("INSERT INTO text_data VALUES ({}, '{}{}')", idx, name, j),
(),
)
.expect("Insert failed");
}
}
db.execute("ANALYZE text_data", ()).unwrap();
let result: Vec<_> = db
.query("SELECT * FROM text_data WHERE name >= 'David0'", ())
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
assert_eq!(
result.len(),
40,
"Should return 40 rows for names >= 'David0'"
);
}
#[test]
fn test_zonemap_rebuild_after_analyze() {
let db = Database::open_in_memory().expect("Failed to create database");
db.execute(
"CREATE TABLE rebuild_test (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.expect("Failed to create table");
for i in 0..50 {
db.execute(
&format!("INSERT INTO rebuild_test VALUES ({}, {})", i, i),
(),
)
.expect("Insert failed");
}
db.execute("ANALYZE rebuild_test", ()).unwrap();
for i in 50..100 {
db.execute(
&format!("INSERT INTO rebuild_test VALUES ({}, {})", i, i),
(),
)
.expect("Insert failed");
}
db.execute("ANALYZE rebuild_test", ()).unwrap();
let result: Vec<_> = db
.query("SELECT * FROM rebuild_test WHERE value >= 75", ())
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
assert_eq!(result.len(), 25, "Should find 25 rows after re-analyze");
}
#[test]
fn test_zonemap_compound_filters() {
let db = Database::open_in_memory().expect("Failed to create database");
db.execute(
"CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER,
amount INTEGER,
status TEXT
)",
(),
)
.expect("Failed to create table");
for i in 0..200 {
let customer_id = i % 10;
let amount = i * 50;
let status = if i % 2 == 0 { "completed" } else { "pending" };
db.execute(
&format!(
"INSERT INTO orders VALUES ({}, {}, {}, '{}')",
i, customer_id, amount, status
),
(),
)
.expect("Insert failed");
}
db.execute("ANALYZE orders", ()).unwrap();
let result: Vec<_> = db
.query(
"SELECT * FROM orders WHERE amount > 8000 AND customer_id = 5",
(),
)
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
for row in &result {
let customer_id: i64 = row.get(1).expect("Failed to get customer_id");
let amount: i64 = row.get(2).expect("Failed to get amount");
assert_eq!(customer_id, 5, "Customer ID should be 5");
assert!(amount > 8000, "Amount should be > 8000");
}
}
#[test]
fn test_zonemap_after_updates() {
let db = Database::open_in_memory().expect("Failed to create database");
db.execute(
"CREATE TABLE update_test (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.expect("Failed to create table");
for i in 0..50 {
db.execute(
&format!("INSERT INTO update_test VALUES ({}, {})", i, i * 10),
(),
)
.expect("Insert failed");
}
db.execute("ANALYZE update_test", ()).unwrap();
db.execute(
"UPDATE update_test SET value = 9999 WHERE id BETWEEN 10 AND 20",
(),
)
.expect("Update failed");
let result1: Vec<_> = db
.query("SELECT COUNT(*) FROM update_test WHERE value = 9999", ())
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
let count1: i64 = result1[0].get(0).expect("Failed to get count");
assert_eq!(count1, 11, "Should find 11 updated rows");
db.execute("ANALYZE update_test", ()).unwrap();
let result2: Vec<_> = db
.query("SELECT COUNT(*) FROM update_test WHERE value = 9999", ())
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
let count2: i64 = result2[0].get(0).expect("Failed to get count");
assert_eq!(count2, 11, "Should still find 11 rows after re-analyze");
}
#[test]
fn test_zonemap_between() {
let db = Database::open_in_memory().expect("Failed to create database");
db.execute(
"CREATE TABLE between_test (id INTEGER PRIMARY KEY, score INTEGER)",
(),
)
.expect("Failed to create table");
for i in 0..100 {
db.execute(
&format!("INSERT INTO between_test VALUES ({}, {})", i, i),
(),
)
.expect("Insert failed");
}
db.execute("ANALYZE between_test", ()).unwrap();
let result: Vec<_> = db
.query(
"SELECT * FROM between_test WHERE score BETWEEN 40 AND 60",
(),
)
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
assert_eq!(
result.len(),
21,
"Should return 21 rows for BETWEEN 40 AND 60"
);
}
#[test]
fn test_zonemap_prune_entire_scan() {
let db = Database::open_in_memory().expect("Failed to create database");
db.execute(
"CREATE TABLE prune_test (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.expect("Failed to create table");
for i in 1..=100 {
db.execute(&format!("INSERT INTO prune_test VALUES ({}, {})", i, i), ())
.expect("Insert failed");
}
db.execute("ANALYZE prune_test", ()).unwrap();
let result: Vec<_> = db
.query("SELECT * FROM prune_test WHERE value > 1000", ())
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
assert!(
result.is_empty(),
"Should return empty result for out-of-range query"
);
let result2: Vec<_> = db
.query("SELECT * FROM prune_test WHERE value < 0", ())
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
assert!(
result2.is_empty(),
"Should return empty result for negative value query"
);
let result3: Vec<_> = db
.query("SELECT * FROM prune_test WHERE value = 500", ())
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
assert!(
result3.is_empty(),
"Should return empty result for non-existent exact value"
);
}
#[test]
fn test_zonemap_prune_with_aggregation() {
let db = Database::open_in_memory().expect("Failed to create database");
db.execute(
"CREATE TABLE agg_prune_test (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.expect("Failed to create table");
for i in 1..=100 {
db.execute(
&format!("INSERT INTO agg_prune_test VALUES ({}, {})", i, i),
(),
)
.expect("Insert failed");
}
db.execute("ANALYZE agg_prune_test", ()).unwrap();
let result: Vec<_> = db
.query("SELECT COUNT(*) FROM agg_prune_test WHERE value > 1000", ())
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
let count: i64 = result[0].get(0).expect("Failed to get count");
assert_eq!(count, 0, "COUNT should return 0 for impossible condition");
let result2: Vec<_> = db
.query(
"SELECT SUM(value) FROM agg_prune_test WHERE value > 1000",
(),
)
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
let sum: Option<i64> = result2[0].get(0).unwrap_or(None);
assert!(
sum.is_none() || sum == Some(0),
"SUM should return NULL or 0 for impossible condition"
);
}