#![allow(
clippy::cast_lossless,
clippy::cast_possible_truncation,
clippy::cast_precision_loss,
clippy::cast_sign_loss,
clippy::doc_markdown,
clippy::similar_names,
clippy::uninlined_format_args
)]
use spg_engine::{Engine, QueryResult};
use spg_storage::Value;
const N_NATIONS: usize = 5;
const N_REGIONS: usize = 2;
const N_CUSTOMERS: usize = 50;
const N_ORDERS: usize = 80;
const N_LINEITEMS: usize = 200;
const N_SUPPLIERS: usize = 20;
fn setup_tpch() -> Engine {
let mut e = Engine::new();
e.execute(
"CREATE TABLE region (\
r_regionkey INT NOT NULL, r_name TEXT NOT NULL)",
)
.unwrap();
e.execute(
"CREATE TABLE nation (\
n_nationkey INT NOT NULL, n_name TEXT NOT NULL, n_regionkey INT NOT NULL)",
)
.unwrap();
e.execute(
"CREATE TABLE customer (\
c_custkey INT NOT NULL, c_name TEXT NOT NULL, c_nationkey INT NOT NULL, \
c_acctbal FLOAT NOT NULL, c_mktsegment TEXT NOT NULL)",
)
.unwrap();
e.execute(
"CREATE TABLE supplier (\
s_suppkey INT NOT NULL, s_name TEXT NOT NULL, s_nationkey INT NOT NULL, \
s_acctbal FLOAT NOT NULL)",
)
.unwrap();
e.execute(
"CREATE TABLE orders (\
o_orderkey INT NOT NULL, o_custkey INT NOT NULL, o_orderdate TEXT NOT NULL, \
o_orderpriority TEXT NOT NULL, o_totalprice FLOAT NOT NULL, o_orderstatus TEXT NOT NULL)",
)
.unwrap();
e.execute(
"CREATE TABLE lineitem (\
l_orderkey INT NOT NULL, l_suppkey INT NOT NULL, \
l_linenumber INT NOT NULL, l_quantity INT NOT NULL, \
l_extendedprice FLOAT NOT NULL, l_discount FLOAT NOT NULL, \
l_tax FLOAT NOT NULL, l_returnflag TEXT NOT NULL, \
l_linestatus TEXT NOT NULL, l_shipdate TEXT NOT NULL)",
)
.unwrap();
for r in 0..N_REGIONS {
e.execute(&format!("INSERT INTO region VALUES ({r}, 'REGION{r}')"))
.unwrap();
}
for n in 0..N_NATIONS {
e.execute(&format!(
"INSERT INTO nation VALUES ({n}, 'NATION{n}', {})",
n % N_REGIONS
))
.unwrap();
}
for c in 0..N_CUSTOMERS {
let seg = match c % 3 {
0 => "BUILDING",
1 => "AUTOMOBILE",
_ => "MACHINERY",
};
e.execute(&format!(
"INSERT INTO customer VALUES ({c}, 'CUST{c}', {}, {}, '{seg}')",
c % N_NATIONS,
(c as f64 * 17.0) % 1000.0
))
.unwrap();
}
for s in 0..N_SUPPLIERS {
e.execute(&format!(
"INSERT INTO supplier VALUES ({s}, 'SUPP{s}', {}, {})",
s % N_NATIONS,
(s as f64 * 23.0) % 1000.0
))
.unwrap();
}
for o in 0..N_ORDERS {
let priority = match o % 5 {
0 => "1-URGENT",
1 => "2-HIGH",
2 => "3-MEDIUM",
3 => "4-NOT SPECIFIED",
_ => "5-LOW",
};
let status = if o % 2 == 0 { "F" } else { "O" };
let day = (o % 365) + 1;
let date = format!("1995-{:02}-{:02}", (day / 31).max(1), (day % 28).max(1));
e.execute(&format!(
"INSERT INTO orders VALUES ({o}, {}, '{date}', '{priority}', {}, '{status}')",
o % N_CUSTOMERS,
(o as f64 * 100.0) + 50.0
))
.unwrap();
}
for l in 0..N_LINEITEMS {
let order = l % N_ORDERS;
let supplier = l % N_SUPPLIERS;
let qty = (l % 50) as i32 + 1;
let price = (l as f64 * 13.0) + 100.0;
let discount = (l as f64 % 7.0) / 100.0;
let tax = (l as f64 % 9.0) / 100.0;
let returnflag = match l % 3 {
0 => "A",
1 => "N",
_ => "R",
};
let linestatus = if l % 2 == 0 { "F" } else { "O" };
let date = format!("1998-{:02}-{:02}", (l / 28 % 12).max(1), (l % 28).max(1));
e.execute(&format!(
"INSERT INTO lineitem VALUES \
({order}, {supplier}, {l}, {qty}, {price}, {discount}, {tax}, \
'{returnflag}', '{linestatus}', '{date}')"
))
.unwrap();
}
e.execute("ANALYZE").unwrap();
e
}
fn rows(r: QueryResult) -> Vec<Vec<Value>> {
match r {
QueryResult::Rows { rows, .. } => rows.into_iter().map(|r| r.values).collect(),
_ => panic!("expected Rows"),
}
}
#[test]
fn q1_pricing_summary_report() {
let mut e = setup_tpch();
let sql = "SELECT l_returnflag, l_linestatus, \
SUM(l_quantity) AS sum_qty, \
COUNT(*) AS count_order \
FROM lineitem \
GROUP BY l_returnflag, l_linestatus \
ORDER BY l_returnflag";
let result = e.execute(sql).expect("Q1 SELECT");
let rs = rows(result);
assert!(
rs.len() >= 3 && rs.len() <= 6,
"Q1 group count plausible; got {}",
rs.len()
);
let total: i64 = rs
.iter()
.map(|r| match &r[3] {
Value::Int(n) => i64::from(*n),
Value::BigInt(n) => *n,
other => panic!("expected int count, got {other:?}"),
})
.sum();
assert_eq!(total, N_LINEITEMS as i64, "Q1 row preservation");
}
#[test]
fn q3_shipping_priority() {
let mut e = setup_tpch();
let sql = "SELECT orders.o_orderkey, \
SUM(lineitem.l_extendedprice) AS revenue \
FROM customer \
INNER JOIN orders ON orders.o_custkey = customer.c_custkey \
INNER JOIN lineitem ON lineitem.l_orderkey = orders.o_orderkey \
WHERE customer.c_mktsegment = 'BUILDING' \
GROUP BY orders.o_orderkey \
ORDER BY SUM(lineitem.l_extendedprice) DESC \
LIMIT 10";
let result = e.execute(sql).expect("Q3 SELECT");
let rs = rows(result);
assert!(rs.len() <= 10, "Q3 LIMIT 10");
assert!(!rs.is_empty(), "Q3 has matching BUILDING customers");
let mut prev = f64::INFINITY;
for r in &rs {
let rev = match &r[1] {
Value::Float(x) => *x,
other => panic!("expected Float revenue, got {other:?}"),
};
assert!(rev <= prev + 1e-6, "Q3 ORDER BY DESC: {rev} > prev {prev}");
prev = rev;
}
}
#[test]
fn q5_local_supplier_volume() {
let mut e = setup_tpch();
let sql = "SELECT nation.n_name, \
SUM(lineitem.l_extendedprice) AS revenue \
FROM customer \
INNER JOIN orders ON orders.o_custkey = customer.c_custkey \
INNER JOIN lineitem ON lineitem.l_orderkey = orders.o_orderkey \
INNER JOIN supplier ON supplier.s_suppkey = lineitem.l_suppkey \
INNER JOIN nation ON nation.n_nationkey = supplier.s_nationkey AND nation.n_nationkey = customer.c_nationkey \
GROUP BY nation.n_name \
ORDER BY SUM(lineitem.l_extendedprice) DESC";
let result = e.execute(sql).expect("Q5 SELECT");
let rs = rows(result);
assert!(rs.len() <= N_NATIONS);
let mut prev = f64::INFINITY;
for r in &rs {
let rev = match &r[1] {
Value::Float(x) => *x,
other => panic!("expected Float, got {other:?}"),
};
assert!(rev <= prev + 1e-6);
prev = rev;
}
}
#[test]
fn q2_minimum_cost_supplier_via_subquery() {
let mut e = setup_tpch();
let sql = "SELECT supplier.s_name, supplier.s_acctbal, nation.n_name \
FROM supplier \
INNER JOIN nation ON nation.n_nationkey = supplier.s_nationkey \
INNER JOIN region ON region.r_regionkey = nation.n_regionkey \
WHERE region.r_name = 'REGION0' \
ORDER BY supplier.s_acctbal DESC \
LIMIT 5";
let result = e.execute(sql).expect("Q2-shape SELECT");
let rs = rows(result);
assert!(rs.len() <= 5);
assert!(!rs.is_empty(), "Q2-shape has matching REGION0 suppliers");
}
#[test]
fn q4_order_priority_check_via_exists() {
let mut e = setup_tpch();
let sql = "SELECT orders.o_orderpriority, COUNT(*) AS order_count \
FROM orders \
WHERE orders.o_orderkey IN \
(SELECT lineitem.l_orderkey FROM lineitem WHERE lineitem.l_quantity >= 25) \
GROUP BY orders.o_orderpriority \
ORDER BY orders.o_orderpriority";
let result = e.execute(sql).expect("Q4-shape SELECT");
let rs = rows(result);
assert!(!rs.is_empty(), "Q4-shape returns ≥ 1 priority group");
let total: i64 = rs
.iter()
.map(|r| match &r[1] {
Value::Int(n) => i64::from(*n),
Value::BigInt(n) => *n,
other => panic!("expected count, got {other:?}"),
})
.sum();
assert!(
total > 0 && total <= N_ORDERS as i64,
"Q4-shape order count ≤ N_ORDERS: got {total}"
);
}
#[test]
fn plan_stable_after_analyze() {
let mut e = setup_tpch();
let sql = "EXPLAIN SELECT orders.o_orderkey, customer.c_name \
FROM customer \
INNER JOIN orders ON orders.o_custkey = customer.c_custkey \
INNER JOIN lineitem ON lineitem.l_orderkey = orders.o_orderkey \
WHERE customer.c_mktsegment = 'BUILDING'";
let first = rows(e.execute(sql).unwrap());
for run in 1..5 {
let nth = rows(e.execute(sql).unwrap());
assert_eq!(
first, nth,
"plan changed between run 0 and run {run}\nfirst={first:?}\nnth={nth:?}"
);
}
}