//! 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}; use query_ops::table::Table; use query_ops::value::Value; 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![ vec![s("Alice"), s("Foo")], vec![s("Bob"), s("Bar")], vec![s("Alice"), s("Baz")], vec![s("Carol"), s("Qux")], ], ); let bestseller = Table::from_rows(1, vec![vec![s("Foo")], vec![s("Baz")]]); let price = Table::from_rows( 2, vec![ vec![s("Foo"), i(25)], vec![s("Bar"), i(15)], vec![s("Baz"), i(30)], vec![s("Qux"), i(20)], ], ); 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![ vec![s("Alice"), s("Foo"), i(25)], vec![s("Alice"), s("Baz"), i(30)], ], ); }