use spg_engine::{Engine, QueryResult};
fn seeded() -> Engine {
let mut e = Engine::new();
e.execute("CREATE TABLE w (id int, g text, v int)").unwrap();
e.execute("INSERT INTO w VALUES (1,'a',10),(2,'a',20),(3,'a',20),(4,'b',5),(5,'b',15),(6,'b',15),(7,'b',30)")
.unwrap();
e
}
fn col1(e: &mut Engine, sql: &str) -> Vec<String> {
match e.execute(sql).unwrap() {
QueryResult::Rows { rows, .. } => rows
.iter()
.map(|r| match &r.values[r.values.len() - 1] {
spg_storage::Value::Null => String::new(),
other => format!("{other:?}"),
})
.collect(),
other => panic!("{other:?}"),
}
}
fn err(e: &mut Engine, sql: &str) -> String {
match e.execute(sql) {
Err(err) => format!("{err}"),
Ok(ok) => panic!("expected an error, got {ok:?}"),
}
}
#[test]
fn named_window_copy_inherits_partitioning() {
let mut e = seeded();
let want = [
"BigInt(10)",
"BigInt(50)",
"BigInt(50)",
"BigInt(5)",
"BigInt(35)",
"BigInt(35)",
"BigInt(65)",
];
assert_eq!(
col1(
&mut e,
"SELECT id, sum(v) OVER w2 FROM w \
WINDOW w1 AS (PARTITION BY g), w2 AS (w1 ORDER BY v) ORDER BY id"
),
want
);
assert_eq!(
col1(
&mut e,
"SELECT id, sum(v) OVER (w1 ORDER BY v) FROM w \
WINDOW w1 AS (PARTITION BY g) ORDER BY id"
),
want
);
assert_eq!(
col1(
&mut e,
"SELECT id, sum(v) OVER (w1 ROWS 1 PRECEDING) FROM w \
WINDOW w1 AS (PARTITION BY g ORDER BY v) ORDER BY id"
),
[
"BigInt(10)",
"BigInt(30)",
"BigInt(40)",
"BigInt(5)",
"BigInt(20)",
"BigInt(30)",
"BigInt(45)"
]
);
}
#[test]
fn named_window_copy_refusals_match_pg() {
let mut e = seeded();
for (sql, want) in [
(
"SELECT sum(v) OVER (w1 PARTITION BY g) FROM w WINDOW w1 AS (PARTITION BY g)",
"cannot override PARTITION BY clause of window \"w1\"",
),
(
"SELECT sum(v) OVER (w1 ORDER BY v) FROM w WINDOW w1 AS (PARTITION BY g ORDER BY v)",
"cannot override ORDER BY clause of window \"w1\"",
),
(
"SELECT sum(v) OVER (w1 ROWS 1 PRECEDING) FROM w WINDOW w1 AS (ORDER BY v ROWS 2 PRECEDING)",
"cannot copy window \"w1\" because it has a frame clause",
),
(
"SELECT sum(v) OVER (w1) FROM w WINDOW w1 AS (ORDER BY v ROWS 2 PRECEDING)",
"cannot copy window \"w1\" because it has a frame clause",
),
(
"SELECT sum(v) OVER w1 FROM w WINDOW w1 AS (PARTITION BY g), w1 AS (PARTITION BY g)",
"window \"w1\" is already defined",
),
(
"SELECT sum(v) OVER nosuch FROM w",
"window \"nosuch\" does not exist",
),
] {
let got = err(&mut e, sql);
assert!(got.contains(want), "want {want:?} in {got:?}");
}
}
#[test]
fn window_calls_rejected_in_where_and_having() {
let mut e = seeded();
let got = err(
&mut e,
"SELECT rank() OVER (ORDER BY v) FROM w WHERE rank() OVER (ORDER BY v) = 1",
);
assert!(
got.contains("window functions are not allowed in WHERE"),
"{got}"
);
assert!(!got.contains("rewrite bug"), "no internal wording: {got}");
let got = err(
&mut e,
"SELECT id FROM w GROUP BY id HAVING row_number() OVER () = 1",
);
assert!(
got.contains("window functions are not allowed in HAVING"),
"{got}"
);
}
#[test]
fn impossible_frames_are_rejected_not_silently_empty() {
let mut e = seeded();
for (sql, want) in [
(
"SELECT sum(v) OVER (ORDER BY v ROWS BETWEEN UNBOUNDED FOLLOWING AND CURRENT ROW) FROM w",
"frame start cannot be UNBOUNDED FOLLOWING",
),
(
"SELECT sum(v) OVER (ORDER BY v RANGE BETWEEN 1 PRECEDING AND UNBOUNDED PRECEDING) FROM w",
"frame end cannot be UNBOUNDED PRECEDING",
),
(
"SELECT sum(v) OVER (ORDER BY v ROWS BETWEEN CURRENT ROW AND 1 PRECEDING) FROM w",
"frame starting from current row cannot have preceding rows",
),
(
"SELECT sum(v) OVER (ORDER BY v ROWS BETWEEN 1 FOLLOWING AND CURRENT ROW) FROM w",
"frame starting from following row cannot have preceding rows",
),
(
"SELECT sum(v) OVER (ORDER BY v ROWS BETWEEN 1 FOLLOWING AND 1 PRECEDING) FROM w",
"frame starting from following row cannot have preceding rows",
),
] {
let got = err(&mut e, sql);
assert!(got.contains(want), "want {want:?} in {got:?}");
}
assert_eq!(
col1(
&mut e,
"SELECT sum(v) OVER (ORDER BY v ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) \
FROM w ORDER BY 1"
),
[
"BigInt(30)",
"BigInt(50)",
"BigInt(70)",
"BigInt(85)",
"BigInt(100)",
"BigInt(110)",
"BigInt(115)"
]
);
}
#[test]
fn range_offset_refusals_name_pgs_two_cases() {
let mut e = seeded();
let got = err(
&mut e,
"SELECT sum(v) OVER (ORDER BY g RANGE BETWEEN 1 PRECEDING AND CURRENT ROW) FROM w",
);
assert!(
got.contains("RANGE with offset PRECEDING/FOLLOWING is not supported for column type text"),
"{got}"
);
let got = err(
&mut e,
"SELECT sum(v) OVER (ORDER BY v, g RANGE BETWEEN 1 PRECEDING AND CURRENT ROW) FROM w",
);
assert!(
got.contains("RANGE with offset PRECEDING/FOLLOWING requires exactly one ORDER BY column"),
"{got}"
);
}