2026-06-03 11:48:33 +02:00
|
|
|
//! Hand-written query plan composed from `scan_atom`, `semijoin`, and `natural_join`.
|
|
|
|
|
//!
|
|
|
|
|
//! Schema:
|
|
|
|
|
//! - `author(name, book)`: who wrote each book
|
|
|
|
|
//! - `bestseller(book)`: the set of bestseller titles
|
|
|
|
|
//! - `price(book, dollars)`: price of each book
|
|
|
|
|
//!
|
|
|
|
|
//! Rule:
|
|
|
|
|
//! - `Q(name, book, dollars) :- author(name, book), bestseller(book), price(book, dollars).`
|
|
|
|
|
//! ("Authors of bestsellers along with each book's price.")
|
|
|
|
|
//!
|
|
|
|
|
//! The plan first scans each input table, then narrows `author` to authors of
|
|
|
|
|
//! bestsellers via a semijoin against `bestseller`, then attaches each book's
|
|
|
|
|
//! price via a natural join against `price`.
|
|
|
|
|
|
|
|
|
|
use query_ops::atom::{AtomPattern, Term, scan_atom};
|
|
|
|
|
use query_ops::join::{natural_join, semijoin};
|
2026-06-04 12:47:47 +02:00
|
|
|
use storage::table::Table;
|
|
|
|
|
use storage::value::Value;
|
2026-06-03 11:48:33 +02:00
|
|
|
|
|
|
|
|
fn s(x: &str) -> Value {
|
|
|
|
|
Value::Str(x.to_string())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn i(x: i64) -> Value {
|
|
|
|
|
Value::Int(x)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn authors_of_bestsellers_with_price() {
|
|
|
|
|
let author = Table::from_rows(
|
|
|
|
|
2,
|
|
|
|
|
vec![
|
2026-06-03 12:30:41 +02:00
|
|
|
vec![s("Ursula K. Le Guin"), s("A Wizard of Earthsea")],
|
|
|
|
|
vec![s("Toni Morrison"), s("Beloved")],
|
|
|
|
|
vec![s("Ursula K. Le Guin"), s("The Left Hand of Darkness")],
|
|
|
|
|
vec![s("Terry Pratchett"), s("Mort")],
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
let bestseller = Table::from_rows(
|
|
|
|
|
1,
|
|
|
|
|
vec![
|
|
|
|
|
vec![s("A Wizard of Earthsea")],
|
|
|
|
|
vec![s("The Left Hand of Darkness")],
|
2026-06-03 11:48:33 +02:00
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
let price = Table::from_rows(
|
|
|
|
|
2,
|
|
|
|
|
vec![
|
2026-06-03 12:30:41 +02:00
|
|
|
vec![s("A Wizard of Earthsea"), i(14)],
|
|
|
|
|
vec![s("Beloved"), i(17)],
|
|
|
|
|
vec![s("The Left Hand of Darkness"), i(15)],
|
|
|
|
|
vec![s("Mort"), i(12)],
|
2026-06-03 11:48:33 +02:00
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let author_rel = scan_atom(
|
|
|
|
|
&author,
|
|
|
|
|
&AtomPattern {
|
|
|
|
|
columns: vec![Term::Var("name".to_string()), Term::Var("book".to_string())],
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
let bestseller_rel = scan_atom(
|
|
|
|
|
&bestseller,
|
|
|
|
|
&AtomPattern {
|
|
|
|
|
columns: vec![Term::Var("book".to_string())],
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
let price_rel = scan_atom(
|
|
|
|
|
&price,
|
|
|
|
|
&AtomPattern {
|
|
|
|
|
columns: vec![
|
|
|
|
|
Term::Var("book".to_string()),
|
|
|
|
|
Term::Var("dollars".to_string()),
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let authors_of_bestsellers = semijoin(&author_rel, &bestseller_rel);
|
|
|
|
|
let result = natural_join(&authors_of_bestsellers, &price_rel);
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
result.columns,
|
|
|
|
|
vec![
|
|
|
|
|
"name".to_string(),
|
|
|
|
|
"book".to_string(),
|
|
|
|
|
"dollars".to_string()
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
result.rows,
|
|
|
|
|
vec![
|
2026-06-03 12:30:41 +02:00
|
|
|
vec![s("Ursula K. Le Guin"), s("A Wizard of Earthsea"), i(14)],
|
|
|
|
|
vec![
|
|
|
|
|
s("Ursula K. Le Guin"),
|
|
|
|
|
s("The Left Hand of Darkness"),
|
|
|
|
|
i(15)
|
|
|
|
|
],
|
2026-06-03 11:48:33 +02:00
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}
|