use rudb::Database;
use rudb_common::Value;
fn fixture() -> String {
format!("'{}/../rudb-parquet/testdata/mixed.parquet'", env!("CARGO_MANIFEST_DIR"))
}
const WIDE: &str = "a, b, s, d, flag, day, t, a AS a2, b AS b2, s AS s2, d AS d2, flag AS flag2";
const KEYS: &str = "b DESC, t ASC, a ASC, d ASC";
fn wide(columns: &str, keys: &str, count: usize) -> String {
format!("SELECT {columns} FROM read_parquet({}) ORDER BY {keys} LIMIT {count}", fixture())
}
fn without_the_pass() -> Database {
let database = Database::new();
database
.execute("SET disabled_optimizers = 'late_materialization'")
.expect("the pass answers to that name");
database
}
fn rows(database: &Database, sql: &str) -> Vec<Vec<Value>> {
let result = database.query(sql).unwrap_or_else(|error| panic!("{sql} failed: {error}"));
result.rows().collect()
}
#[test]
fn a_wide_top_n_over_a_file_scans_the_ordering_column_and_fetches_the_rest() {
let database = Database::new();
let plan = database.plan(&wide(WIDE, "b DESC", 3)).expect("binds");
assert!(plan.contains("Fetch args="), "{plan}");
assert!(plan.contains("options=[file_row_number=TRUE::BOOLEAN]"), "{plan}");
assert!(plan.contains("[b::BIGINT, file_row_number::BIGINT]"), "{plan}");
let under = plan.split("TopN").nth(1).expect("the plan has a top N in it");
for dropped in ["s::VARCHAR", "d::DOUBLE", "flag::BOOLEAN", "day::DATE", "t::TIMESTAMP"] {
assert!(!under.contains(dropped), "{dropped} is read under the top N in {plan}");
}
}
#[test]
fn the_rewritten_plan_answers_what_the_plan_without_it_answers() {
let sql = wide(WIDE, KEYS, 5);
let deferred = rows(&Database::new(), &sql);
let plain = rows(&without_the_pass(), &sql);
assert_eq!(deferred.len(), 5);
assert_eq!(deferred, plain);
}
#[test]
fn an_offset_past_the_first_rows_is_the_same_rows_either_way() {
let sql = format!("{} OFFSET 7", wide(WIDE, KEYS, 4));
let deferred = rows(&Database::new(), &sql);
let plain = rows(&without_the_pass(), &sql);
assert_eq!(deferred.len(), 4);
assert_eq!(deferred, plain);
}
#[test]
fn the_fetched_rows_keep_the_order_the_top_n_put_them_in() {
let database = Database::new();
let result = database.query(&wide(WIDE, "b DESC", 6)).expect("runs");
let held: Vec<i64> = result
.column(1)
.map(|value| match value {
Value::BigInt(number) => number,
other => panic!("a BIGINT column produced {other:?}"),
})
.collect();
let mut sorted = held.clone();
sorted.sort_by(|left, right| right.cmp(left));
assert_eq!(held, sorted, "the rows came back out of order");
}
#[test]
fn the_scan_under_the_top_n_reads_the_ordering_column_and_no_others() {
let sql = wide(WIDE, "day ASC", 5);
let read = |database: &Database| {
let result = database.query(&sql).expect("runs");
let metrics = result.metrics().expect("a query that ran has metrics");
metrics
.operators
.iter()
.find(|operator| operator.kind == "FileScan")
.expect("the query contains a file scan")
.bytes_read
};
let deferred = read(&Database::new());
let plain = read(&without_the_pass());
assert!(deferred * 4 < plain, "the narrowed scan read {deferred} bytes against {plain}");
}
#[test]
fn a_projection_that_is_not_wide_is_left_alone() {
let database = Database::new();
let plan = database.plan(&wide("a, b, s", "b DESC", 5)).expect("binds");
assert!(!plan.contains("Fetch"), "{plan}");
}
#[test]
fn a_limit_that_is_not_small_is_left_alone() {
let database = Database::new();
let plan = database.plan(&wide(WIDE, "b DESC", 4096)).expect("binds");
assert!(!plan.contains("Fetch"), "{plan}");
}
#[test]
fn a_computed_ordering_key_is_replayed_after_the_fetch() {
let database = Database::new();
let sql = wide(WIDE, "b + 1 DESC", 5);
let plan = database.plan(&sql).expect("binds");
assert!(plan.contains("Fetch"), "{plan}");
assert_eq!(rows(&database, &sql), rows(&without_the_pass(), &sql));
}
#[test]
fn a_file_that_already_counts_its_rows_is_left_alone() {
let database = Database::new();
let call = format!("read_parquet({}, file_row_number=True)", fixture());
let sql = format!("SELECT {WIDE}, file_row_number FROM {call} ORDER BY b DESC LIMIT 5");
let plan = database.plan(&sql).expect("binds");
assert!(!plan.contains("Fetch"), "{plan}");
}