use rudb::Database;
const SORT: &str = "CREATE TABLE s AS SELECT r AS a, (r * 7919) % 1000003 AS b, r + 1 AS c, \
r + 2 AS d, r + 3 AS e, r + 4 AS f, r + 5 AS g, r + 6 AS h \
FROM range(2000000) AS t(r) ORDER BY (r * 7919) % 1000003, r";
#[test]
fn a_sort_of_two_million_rows_fits_in_a_limit_that_holds_one_copy_of_them() {
let database = Database::new();
database.execute("SET threads=1").expect("sets the thread count");
database.execute("SET memory_limit='300MB'").expect("sets the limit");
database.execute(SORT).expect("two million rows sort inside the limit");
assert_eq!(
database.value("SELECT count(*) FROM s").expect("counts").to_string(),
"2000000",
"every row came through"
);
assert_eq!(
database.value("SELECT min(b) FROM s").expect("reads").to_string(),
database.value("SELECT b FROM s LIMIT 1").expect("reads").to_string(),
"the smallest key is first"
);
}
#[test]
fn a_sort_that_does_not_fit_spills_and_answers_the_same_thing() {
let scrambled = "SELECT r AS a, (r * 7919) % 1000003 AS b FROM range(2000000) AS t(r)";
let ordered = format!("SELECT a, b FROM ({scrambled}) ORDER BY b, a");
let spilling = Database::new();
spilling.execute("SET threads=1").expect("sets the thread count");
spilling.execute("SET memory_limit='100MB'").expect("sets the limit");
spilling
.execute(&format!("CREATE TABLE s AS {ordered}"))
.expect("two million rows sort by spilling");
let roomy = Database::new();
roomy.execute("SET threads=1").expect("sets the thread count");
roomy.execute("SET memory_limit='2GB'").expect("sets the limit");
roomy.execute(&format!("CREATE TABLE s AS {ordered}")).expect("and again without spilling");
assert_eq!(
spilling.value("SELECT count(*) FROM s").expect("counts").to_string(),
"2000000",
"every row came through"
);
spilling.execute("SET memory_limit='2GB'").expect("raises the limit for the read");
let digest = "SELECT sum(pos * (a * 1000003 + b)) \
FROM (SELECT a, b, row_number() OVER () AS pos FROM s)";
assert_eq!(
spilling.value(digest).expect("reads").to_string(),
roomy.value(digest).expect("reads").to_string(),
"the spilled sort is in the same order as the one that fitted"
);
}
#[test]
fn a_string_column_laid_in_ranges_matches_one_laid_whole() {
let sorted = "CREATE TABLE s AS SELECT r, CASE WHEN r % 97 = 0 THEN NULL WHEN r % 5 = 0 \
THEN CAST(r AS VARCHAR) ELSE 'a string long enough to leave its view ' || r END \
AS c FROM range(800000) AS t(r) ORDER BY r % 7, r";
let digest = "SELECT sum(pos * r) FROM (SELECT r, row_number() OVER () AS pos FROM s)";
let wrong = "SELECT count(*) FROM s WHERE c IS DISTINCT FROM (CASE WHEN r % 97 = 0 THEN NULL \
WHEN r % 5 = 0 THEN CAST(r AS VARCHAR) \
ELSE 'a string long enough to leave its view ' || r END)";
let split = Database::new();
split.execute("SET threads=4").expect("sets the thread count");
split.execute(sorted).expect("sorts on four threads");
let whole = Database::new();
whole.execute("SET threads=1").expect("sets the thread count");
whole.execute(sorted).expect("sorts on one thread");
assert_eq!(split.value(wrong).expect("reads").to_string(), "0", "every string on its row");
assert_eq!(
split.value(digest).expect("reads").to_string(),
whole.value(digest).expect("reads").to_string(),
"the same order either way"
);
}