use spg_engine::{Engine, QueryResult};
fn plan(e: &mut Engine, sql: &str) -> Vec<String> {
match e.execute(sql).unwrap() {
QueryResult::Rows { rows, .. } => rows
.iter()
.map(|r| match &r.values[0] {
spg_storage::Value::Text(s) => s.to_string(),
other => format!("{other:?}"),
})
.collect(),
other => panic!("{other:?}"),
}
}
fn seeded() -> Engine {
let mut e = Engine::new();
e.execute("CREATE TABLE p1 (id int PRIMARY KEY, v int, s text)")
.unwrap();
e.execute("INSERT INTO p1 VALUES (1,2,'a'),(2,4,'b'),(5,10,'x5')")
.unwrap();
e
}
#[test]
fn index_cond_and_filter_split_like_pg() {
let mut e = seeded();
assert_eq!(
plan(
&mut e,
"EXPLAIN (COSTS OFF) SELECT * FROM p1 WHERE id = 5 AND v > 3"
),
vec![
"Index Scan using p1_pkey on p1",
" Index Cond: (id = 5)",
" Filter: (v > 3)",
]
);
assert_eq!(
plan(
&mut e,
"EXPLAIN (COSTS OFF) SELECT * FROM p1 WHERE id = 5 AND s = 'x5'"
),
vec![
"Index Scan using p1_pkey on p1",
" Index Cond: (id = 5)",
" Filter: (s = 'x5')",
]
);
assert_eq!(
plan(&mut e, "EXPLAIN (COSTS OFF) SELECT * FROM p1 WHERE id = 5"),
vec!["Index Scan using p1_pkey on p1", " Index Cond: (id = 5)"]
);
assert_eq!(
plan(
&mut e,
"EXPLAIN (COSTS OFF) SELECT * FROM p1 WHERE v > 3 AND s = 'a'"
),
vec!["Seq Scan on p1", " Filter: ((v > 3) AND (s = 'a'))"]
);
}
#[test]
fn format_json_emits_pg_node_objects() {
let mut e = seeded();
let out = plan(
&mut e,
"EXPLAIN (FORMAT JSON, COSTS OFF) SELECT * FROM p1 WHERE id = 5 AND v > 3",
);
assert_eq!(out.len(), 1, "JSON is one row");
let j = &out[0];
assert!(
j.starts_with("[\n {\n \"Plan\": {"),
"PG array/Plan wrapper: {j}"
);
assert!(j.contains("\"Node Type\": \"Index Scan\""), "{j}");
assert!(j.contains("\"Index Name\": \"p1_pkey\""), "{j}");
assert!(j.contains("\"Relation Name\": \"p1\""), "{j}");
assert!(j.contains("\"Index Cond\": \"(id = 5)\""), "{j}");
assert!(j.contains("\"Filter\": \"(v > 3)\""), "{j}");
assert!(!j.contains("Total Cost"), "COSTS OFF drops costs: {j}");
}
#[test]
fn format_json_nests_children_under_plans() {
let mut e = seeded();
let j = &plan(
&mut e,
"EXPLAIN (FORMAT JSON, COSTS OFF) SELECT v, count(*) FROM p1 GROUP BY v",
)[0];
assert!(j.contains("\"Node Type\": \"Aggregate\""), "{j}");
assert!(j.contains("\"Strategy\": \"Hashed\""), "{j}");
assert!(
j.contains("\"Group Key\": [\"v\"]"),
"key list is an array: {j}"
);
assert!(j.contains("\"Plans\": ["), "{j}");
assert!(j.contains("\"Parent Relationship\": \"Outer\""), "{j}");
assert!(j.contains("\"Node Type\": \"Seq Scan\""), "{j}");
}
#[test]
fn format_json_carries_costs_when_on() {
let mut e = seeded();
let j = &plan(&mut e, "EXPLAIN (FORMAT JSON) SELECT * FROM p1 WHERE v = 4")[0];
assert!(j.contains("\"Startup Cost\": 0.00"), "{j}");
assert!(j.contains("\"Total Cost\": "), "{j}");
assert!(j.contains("\"Plan Rows\": "), "{j}");
assert!(j.contains("\"Plan Width\": "), "{j}");
}