use powdb_query::executor::Engine;
use powdb_query::parser::{parse, ParseError};
use powdb_query::sql::parse_sql;
const KNOWN_BAD: usize = 5_000;
const TEN_X: usize = 50_000;
fn chain(head: &str, term: &str, n: usize) -> String {
let mut query = String::with_capacity(head.len() + term.len() * n);
query.push_str(head);
for _ in 0..n {
query.push_str(term);
}
query
}
fn expect_err<T: std::fmt::Debug>(result: Result<T, ParseError>, what: &str) -> ParseError {
match result {
Err(err) => err,
Ok(ok) => panic!("{what}: expected a parse error, got {ok:?}"),
}
}
fn assert_depth_error<T: std::fmt::Debug>(result: Result<T, ParseError>, what: &str) {
let err = expect_err(result, what);
assert!(
err.message().contains("nesting depth"),
"{what}: expected a nesting-depth error, got: {}",
err.message()
);
}
#[test]
fn powql_and_chain_is_rejected() {
for n in [KNOWN_BAD, TEN_X] {
let query = chain("User filter .a = 1", " and .a = 1", n);
assert_depth_error(parse(&query), &format!("PowQL and chain n={n}"));
}
}
#[test]
fn powql_or_chain_is_rejected() {
for n in [KNOWN_BAD, TEN_X] {
let query = chain("User filter .a = 1", " or .a = 1", n);
assert_depth_error(parse(&query), &format!("PowQL or chain n={n}"));
}
}
#[test]
fn powql_additive_chain_is_rejected() {
for n in [KNOWN_BAD, TEN_X] {
let query = chain("User filter .a", " + 1", n);
assert_depth_error(parse(&query), &format!("PowQL additive chain n={n}"));
}
}
#[test]
fn powql_multiplicative_chain_is_rejected() {
for n in [KNOWN_BAD, TEN_X] {
let query = chain("User filter .a", " * 2", n);
assert_depth_error(parse(&query), &format!("PowQL multiplicative chain n={n}"));
}
}
#[test]
fn powql_having_chain_is_rejected() {
for n in [KNOWN_BAD, TEN_X] {
let query = chain("User group .a having .a = 1", " having .a = 1", n);
assert_depth_error(parse(&query), &format!("PowQL having chain n={n}"));
}
}
#[test]
fn sql_and_chain_is_rejected() {
for n in [KNOWN_BAD, TEN_X] {
let query = chain("SELECT a FROM User WHERE a = 1", " AND a = 1", n);
assert_depth_error(parse_sql(&query), &format!("SQL and chain n={n}"));
}
}
#[test]
fn sql_additive_chain_is_rejected() {
for n in [KNOWN_BAD, TEN_X] {
let query = chain("SELECT a FROM User WHERE a = 1", " + 1", n);
assert_depth_error(parse_sql(&query), &format!("SQL additive chain n={n}"));
}
}
#[test]
fn engine_rejects_chain_without_crashing() {
let dir = tempfile::tempdir().unwrap();
let mut engine = Engine::new(dir.path()).unwrap();
engine
.execute_powql("type User { required a: int }")
.unwrap();
engine.execute_powql("insert User { a := 1 }").unwrap();
let powql = chain("User filter .a = 1", " and .a = 1", KNOWN_BAD);
assert!(
engine.execute_powql(&powql).is_err(),
"deep PowQL and chain must be refused"
);
let sql = chain("SELECT a FROM User WHERE a = 1", " AND a = 1", KNOWN_BAD);
assert!(
engine.execute_sql(&sql).is_err(),
"deep SQL and chain must be refused"
);
}
#[test]
fn moderate_chain_still_works() {
let dir = tempfile::tempdir().unwrap();
let mut engine = Engine::new(dir.path()).unwrap();
engine
.execute_powql("type User { required a: int }")
.unwrap();
engine.execute_powql("insert User { a := 1 }").unwrap();
let powql = chain("User filter .a = 1", " and .a = 1", 40);
engine
.execute_powql(&powql)
.expect("a 40-term and chain must still be accepted");
let sql = chain("SELECT a FROM User WHERE a = 1", " AND a = 1", 40);
engine
.execute_sql(&sql)
.expect("a 40-term SQL and chain must still be accepted");
}
#[test]
fn deepest_accepted_tree_executes_and_drops() {
let dir = tempfile::tempdir().unwrap();
let mut engine = Engine::new(dir.path()).unwrap();
engine
.execute_powql("type User { required a: int }")
.unwrap();
engine.execute_powql("insert User { a := 1 }").unwrap();
let inner = chain(".a = 1", " and .a = 1", 8);
let query = format!("User filter {}{inner}{}", "(".repeat(25), ")".repeat(25));
let statement = parse(&query).expect("deeply parenthesized chain must parse");
drop(statement);
engine
.execute_powql(&query)
.expect("deeply parenthesized chain must execute");
}