use stoolap::api::Database;
#[test]
fn test_aqe_basic_join() {
let db = Database::open("memory://aqe_basic").expect("Failed to create database");
db.execute(
"CREATE TABLE left_table (id INTEGER PRIMARY KEY, value TEXT)",
(),
)
.expect("Failed to create left table");
db.execute(
"CREATE TABLE right_table (id INTEGER PRIMARY KEY, left_id INTEGER, data TEXT)",
(),
)
.expect("Failed to create right table");
for i in 0..50 {
db.execute(
&format!("INSERT INTO left_table VALUES ({}, 'left_{}')", i, i),
(),
)
.expect("Insert failed");
}
for i in 0..100 {
db.execute(
&format!(
"INSERT INTO right_table VALUES ({}, {}, 'right_{}')",
i,
i % 50,
i
),
(),
)
.expect("Insert failed");
}
db.execute("ANALYZE left_table", ()).unwrap();
db.execute("ANALYZE right_table", ()).unwrap();
let result: Vec<_> = db
.query(
"SELECT l.value, r.data
FROM left_table l
INNER JOIN right_table r ON l.id = r.left_id
ORDER BY l.id, r.id",
(),
)
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
assert_eq!(result.len(), 100, "Should return 100 rows from join");
}
#[test]
fn test_aqe_skewed_data() {
let db = Database::open("memory://aqe_skewed").expect("Failed to create database");
db.execute(
"CREATE TABLE customers (id INTEGER PRIMARY KEY, region TEXT)",
(),
)
.expect("Failed to create customers table");
db.execute(
"CREATE TABLE orders (id INTEGER PRIMARY KEY, customer_id INTEGER, total INTEGER)",
(),
)
.expect("Failed to create orders table");
for i in 0..100 {
db.execute(
&format!("INSERT INTO customers VALUES ({}, 'region_{}')", i, i % 10),
(),
)
.expect("Insert failed");
}
let mut order_id = 0;
for customer_id in 0..100 {
let num_orders = if customer_id == 0 {
100
} else {
1 + (customer_id % 2)
};
for _ in 0..num_orders {
db.execute(
&format!(
"INSERT INTO orders VALUES ({}, {}, {})",
order_id,
customer_id,
(order_id + 1) * 10
),
(),
)
.expect("Insert failed");
order_id += 1;
}
}
db.execute("ANALYZE customers", ()).unwrap();
db.execute("ANALYZE orders", ()).unwrap();
let result: Vec<_> = db
.query(
"SELECT c.region, o.total
FROM customers c
INNER JOIN orders o ON c.id = o.customer_id
WHERE c.id = 0
ORDER BY o.total",
(),
)
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
assert_eq!(result.len(), 100, "Should return 100 orders for customer 0");
}
#[test]
fn test_aqe_left_join() {
let db = Database::open("memory://aqe_left").expect("Failed to create database");
db.execute(
"CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT)",
(),
)
.expect("Failed to create products table");
db.execute(
"CREATE TABLE reviews (id INTEGER PRIMARY KEY, product_id INTEGER, rating INTEGER)",
(),
)
.expect("Failed to create reviews table");
for i in 0..50 {
db.execute(
&format!("INSERT INTO products VALUES ({}, 'product_{}')", i, i),
(),
)
.expect("Insert failed");
}
for (review_id, product_id) in (0..25).enumerate() {
db.execute(
&format!(
"INSERT INTO reviews VALUES ({}, {}, {})",
review_id,
product_id,
3 + (review_id % 3)
),
(),
)
.expect("Insert failed");
}
db.execute("ANALYZE products", ()).unwrap();
db.execute("ANALYZE reviews", ()).unwrap();
let result: Vec<_> = db
.query(
"SELECT p.name, r.rating
FROM products p
LEFT JOIN reviews r ON p.id = r.product_id
ORDER BY p.id",
(),
)
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
assert_eq!(
result.len(),
50,
"Should return all 50 products in LEFT JOIN"
);
let mut with_reviews = 0;
let mut without_reviews = 0;
for row in &result {
let rating: Result<i64, _> = row.get(1);
if rating.is_ok() {
with_reviews += 1;
} else {
without_reviews += 1;
}
}
assert_eq!(with_reviews, 25, "25 products should have reviews");
assert_eq!(without_reviews, 25, "25 products should not have reviews");
}
#[test]
fn test_aqe_multi_join() {
let db = Database::open("memory://aqe_multi").expect("Failed to create database");
db.execute(
"CREATE TABLE categories (id INTEGER PRIMARY KEY, name TEXT)",
(),
)
.expect("Failed to create categories table");
db.execute(
"CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT, category_id INTEGER)",
(),
)
.expect("Failed to create products table");
db.execute(
"CREATE TABLE inventory (id INTEGER PRIMARY KEY, product_id INTEGER, quantity INTEGER)",
(),
)
.expect("Failed to create inventory table");
for i in 0..5 {
db.execute(
&format!("INSERT INTO categories VALUES ({}, 'category_{}')", i, i),
(),
)
.expect("Insert failed");
}
for i in 0..50 {
db.execute(
&format!(
"INSERT INTO products VALUES ({}, 'product_{}', {})",
i,
i,
i % 5
),
(),
)
.expect("Insert failed");
}
for i in 0..50 {
db.execute(
&format!(
"INSERT INTO inventory VALUES ({}, {}, {})",
i,
i,
(i + 1) * 10
),
(),
)
.expect("Insert failed");
}
db.execute("ANALYZE categories", ()).unwrap();
db.execute("ANALYZE products", ()).unwrap();
db.execute("ANALYZE inventory", ()).unwrap();
let result: Vec<_> = db
.query(
"SELECT c.name as category, p.name as product, i.quantity
FROM categories c
INNER JOIN products p ON c.id = p.category_id
INNER JOIN inventory i ON p.id = i.product_id
WHERE i.quantity > 200
ORDER BY c.id, p.id",
(),
)
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
for row in &result {
let quantity: i64 = row.get(2).expect("Failed to get quantity");
assert!(quantity > 200, "Quantity should be > 200");
}
}
#[test]
fn test_aqe_join_with_filter() {
let db = Database::open("memory://aqe_filter").expect("Failed to create database");
db.execute(
"CREATE TABLE employees (id INTEGER PRIMARY KEY, name TEXT, dept_id INTEGER)",
(),
)
.expect("Failed to create employees table");
db.execute(
"CREATE TABLE departments (id INTEGER PRIMARY KEY, name TEXT, budget INTEGER)",
(),
)
.expect("Failed to create departments table");
for i in 0..10 {
db.execute(
&format!(
"INSERT INTO departments VALUES ({}, 'dept_{}', {})",
i,
i,
100000 + i * 50000
),
(),
)
.expect("Insert failed");
}
for i in 0..100 {
db.execute(
&format!(
"INSERT INTO employees VALUES ({}, 'emp_{}', {})",
i,
i,
i % 10
),
(),
)
.expect("Insert failed");
}
db.execute("ANALYZE employees", ()).unwrap();
db.execute("ANALYZE departments", ()).unwrap();
let result: Vec<_> = db
.query(
"SELECT e.name, d.name as dept, d.budget
FROM employees e
INNER JOIN departments d ON e.dept_id = d.id
WHERE d.budget > 300000
ORDER BY e.id",
(),
)
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
assert_eq!(
result.len(),
50,
"Should return 50 employees in high-budget departments"
);
}
#[test]
fn test_aqe_cross_join() {
let db = Database::open("memory://aqe_cross").expect("Failed to create database");
db.execute("CREATE TABLE sizes (id INTEGER PRIMARY KEY, name TEXT)", ())
.expect("Failed to create sizes table");
db.execute(
"CREATE TABLE colors (id INTEGER PRIMARY KEY, name TEXT)",
(),
)
.expect("Failed to create colors table");
db.execute(
"INSERT INTO sizes VALUES (1, 'Small'), (2, 'Medium'), (3, 'Large')",
(),
)
.expect("Insert failed");
db.execute(
"INSERT INTO colors VALUES (1, 'Red'), (2, 'Blue'), (3, 'Green'), (4, 'Yellow')",
(),
)
.expect("Insert failed");
let result: Vec<_> = db
.query(
"SELECT s.name as size, c.name as color
FROM sizes s
CROSS JOIN colors c
ORDER BY s.id, c.id",
(),
)
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
assert_eq!(result.len(), 12, "CROSS JOIN should produce 12 rows");
}
#[test]
fn test_aqe_large_small_join() {
let db = Database::open("memory://aqe_large_small").expect("Failed to create database");
db.execute(
"CREATE TABLE status_codes (id INTEGER PRIMARY KEY, description TEXT)",
(),
)
.expect("Failed to create status_codes table");
db.execute(
"CREATE TABLE transactions (id INTEGER PRIMARY KEY, status_id INTEGER, amount INTEGER)",
(),
)
.expect("Failed to create transactions table");
db.execute("INSERT INTO status_codes VALUES (1, 'Pending')", ())
.expect("Insert failed");
db.execute("INSERT INTO status_codes VALUES (2, 'Completed')", ())
.expect("Insert failed");
db.execute("INSERT INTO status_codes VALUES (3, 'Failed')", ())
.expect("Insert failed");
for i in 0..1000 {
let status_id = (i % 3) + 1;
db.execute(
&format!(
"INSERT INTO transactions VALUES ({}, {}, {})",
i,
status_id,
(i + 1) * 10
),
(),
)
.expect("Insert failed");
}
db.execute("ANALYZE status_codes", ()).unwrap();
db.execute("ANALYZE transactions", ()).unwrap();
let result: Vec<_> = db
.query(
"SELECT s.description, COUNT(*) as count
FROM transactions t
INNER JOIN status_codes s ON t.status_id = s.id
GROUP BY s.description
ORDER BY s.description",
(),
)
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
assert_eq!(result.len(), 3, "Should have 3 status groups");
for row in &result {
let count: i64 = row.get(1).expect("Failed to get count");
assert!(
(333..=334).contains(&count),
"Each status should have ~333-334 transactions"
);
}
}
#[test]
fn test_aqe_self_join() {
let db = Database::open("memory://aqe_self").expect("Failed to create database");
db.execute(
"CREATE TABLE employees (id INTEGER PRIMARY KEY, name TEXT, manager_id INTEGER)",
(),
)
.expect("Failed to create employees table");
db.execute("INSERT INTO employees VALUES (1, 'CEO', NULL)", ())
.expect("Insert failed");
db.execute("INSERT INTO employees VALUES (2, 'VP1', 1)", ())
.expect("Insert failed");
db.execute("INSERT INTO employees VALUES (3, 'VP2', 1)", ())
.expect("Insert failed");
for i in 4..20 {
let manager = if i < 10 { 2 } else { 3 };
db.execute(
&format!(
"INSERT INTO employees VALUES ({}, 'Employee{}', {})",
i, i, manager
),
(),
)
.expect("Insert failed");
}
db.execute("ANALYZE employees", ()).unwrap();
let result: Vec<_> = db
.query(
"SELECT e.name as employee, m.name as manager
FROM employees e
INNER JOIN employees m ON e.manager_id = m.id
ORDER BY e.id",
(),
)
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
assert_eq!(result.len(), 18, "18 employees should have managers");
}
#[test]
fn test_aqe_join_aggregate() {
let db = Database::open("memory://aqe_agg").expect("Failed to create database");
db.execute(
"CREATE TABLE stores (id INTEGER PRIMARY KEY, name TEXT, region TEXT)",
(),
)
.expect("Failed to create stores table");
db.execute(
"CREATE TABLE sales (id INTEGER PRIMARY KEY, store_id INTEGER, amount INTEGER, date INTEGER)",
(),
)
.expect("Failed to create sales table");
for i in 0..10 {
let region = match i % 3 {
0 => "North",
1 => "South",
_ => "West",
};
db.execute(
&format!(
"INSERT INTO stores VALUES ({}, 'Store{}', '{}')",
i, i, region
),
(),
)
.expect("Insert failed");
}
for i in 0..500 {
let store_id = i % 10;
db.execute(
&format!(
"INSERT INTO sales VALUES ({}, {}, {}, {})",
i,
store_id,
(i + 1) * 100,
i
),
(),
)
.expect("Insert failed");
}
db.execute("ANALYZE stores", ()).unwrap();
db.execute("ANALYZE sales", ()).unwrap();
let result: Vec<_> = db
.query(
"SELECT st.region, SUM(sa.amount) as total_sales
FROM stores st
INNER JOIN sales sa ON st.id = sa.store_id
GROUP BY st.region
ORDER BY st.region",
(),
)
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
assert_eq!(result.len(), 3, "Should have 3 regions");
}
#[test]
fn test_aqe_correctness_uniform() {
let db = Database::open("memory://aqe_uniform").expect("Failed to create database");
db.execute("CREATE TABLE a (id INTEGER PRIMARY KEY, value INTEGER)", ())
.expect("Failed to create table a");
db.execute(
"CREATE TABLE b (id INTEGER PRIMARY KEY, a_id INTEGER, data TEXT)",
(),
)
.expect("Failed to create table b");
for i in 0..100 {
db.execute(&format!("INSERT INTO a VALUES ({}, {})", i, i * 10), ())
.expect("Insert failed");
}
for i in 0..200 {
db.execute(
&format!("INSERT INTO b VALUES ({}, {}, 'data_{}')", i, i % 100, i),
(),
)
.expect("Insert failed");
}
db.execute("ANALYZE a", ()).unwrap();
db.execute("ANALYZE b", ()).unwrap();
let result: Vec<_> = db
.query(
"SELECT a.value, b.data
FROM a
INNER JOIN b ON a.id = b.a_id
WHERE a.value BETWEEN 100 AND 200
ORDER BY a.id, b.id",
(),
)
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
assert_eq!(result.len(), 22, "Should return 22 rows"); }
#[test]
fn test_aqe_empty_join() {
let db = Database::open("memory://aqe_empty").expect("Failed to create database");
db.execute("CREATE TABLE x (id INTEGER PRIMARY KEY, value INTEGER)", ())
.expect("Failed to create table x");
db.execute("CREATE TABLE y (id INTEGER PRIMARY KEY, x_id INTEGER)", ())
.expect("Failed to create table y");
for i in 0..50 {
db.execute(&format!("INSERT INTO x VALUES ({}, {})", i, i), ())
.expect("Insert failed");
}
for i in 100..150 {
db.execute(&format!("INSERT INTO y VALUES ({}, {})", i, i), ())
.expect("Insert failed");
}
db.execute("ANALYZE x", ()).unwrap();
db.execute("ANALYZE y", ()).unwrap();
let result: Vec<_> = db
.query(
"SELECT x.value, y.id FROM x INNER JOIN y ON x.id = y.x_id",
(),
)
.expect("Query failed")
.collect::<Result<Vec<_>, _>>()
.expect("Failed to collect");
assert_eq!(result.len(), 0, "Join with no matches should return empty");
}