use spg_engine::{Engine, QueryResult};
fn err_of(e: &mut Engine, sql: &str) -> String {
format!(
"{}",
e.execute(sql).expect_err(&format!("PG18 refuses: {sql}"))
)
}
fn seed(e: &mut Engine) {
e.execute("CREATE TABLE t704(i INT, b BIGINT, f FLOAT, d DATE, ts TIMESTAMP)")
.unwrap();
e.execute("INSERT INTO t704 VALUES (5, 5, 5, '2020-01-01', '2020-01-01 00:00:00')")
.unwrap();
}
#[test]
fn round704_a_bare_window_function_names_the_missing_over() {
let mut e = Engine::new();
seed(&mut e);
for func in ["lag(i)", "lead(i)", "row_number()", "rank()", "ntile(4)"] {
let err = err_of(&mut e, &format!("SELECT {func} FROM t704"));
assert!(err.contains("requires an OVER clause"), "{func}: {err}");
assert!(
!err.contains("does not exist"),
"{func} exists; the error must not deny it: {err}"
);
}
assert!(err_of(&mut e, "SELECT nosuchfn704(i) FROM t704").contains("does not exist"));
}
#[test]
fn round704_a_bad_numeric_literal_fails_as_a_value() {
let mut e = Engine::new();
seed(&mut e);
for (sql, want) in [
(
"SELECT i FROM t704 WHERE i = 'abc'",
"invalid input syntax for type integer: \"abc\"",
),
(
"SELECT i FROM t704 WHERE b > 'x'",
"invalid input syntax for type bigint: \"x\"",
),
(
"SELECT i FROM t704 WHERE f = 'y'",
"invalid input syntax for type double precision: \"y\"",
),
] {
let err = err_of(&mut e, sql);
assert!(err.contains(want), "{sql}\n got: {err}\n want: {want}");
assert!(!err.contains("operator does not exist"), "{sql}: {err}");
}
let n = match e
.execute("SELECT count(*) FROM t704 WHERE i = '5' AND i > ' 4 '")
.unwrap()
{
QueryResult::Rows { rows, .. } => spg_engine::eval::value_to_text(&rows[0].values[0]),
other => panic!("{other:?}"),
};
assert_eq!(n, "1");
}
#[test]
fn round704_substring_over_an_int_is_a_missing_overload() {
let mut e = Engine::new();
seed(&mut e);
assert!(
err_of(&mut e, "SELECT substring(i FROM 1 FOR 2) FROM t704")
.contains("function pg_catalog.substring(integer, integer, integer) does not exist"),
);
let got = match e.execute("SELECT substring('hello' FROM 2 FOR 3)").unwrap() {
QueryResult::Rows { rows, .. } => spg_engine::eval::value_to_text(&rows[0].values[0]),
other => panic!("{other:?}"),
};
assert_eq!(got, "ell");
}
#[test]
fn round704_a_temporal_literal_that_wont_lift_uses_the_input_functions_words() {
let mut e = Engine::new();
seed(&mut e);
assert!(
err_of(&mut e, "SELECT i FROM t704 WHERE d > 'notadate'")
.contains("invalid input syntax for type date: \"notadate\""),
);
assert!(
err_of(&mut e, "SELECT i FROM t704 WHERE ts > 'nope'")
.contains("invalid input syntax for type timestamp: \"nope\""),
);
}
#[test]
fn round705_an_unreferenced_window_definition_is_still_analysed() {
let mut e = Engine::new();
seed(&mut e);
let err = err_of(
&mut e,
"SELECT i FROM t704 WINDOW w AS (ORDER BY nosuch705)",
);
assert!(err.contains("nosuch705"), "{err}");
let n = match e
.execute("SELECT count(*) FROM t704 WINDOW w AS (PARTITION BY i ORDER BY b)")
.unwrap()
{
QueryResult::Rows { rows, .. } => spg_engine::eval::value_to_text(&rows[0].values[0]),
other => panic!("{other:?}"),
};
assert_eq!(n, "1");
let got = match e
.execute("SELECT row_number() OVER w FROM t704 WINDOW w AS (ORDER BY i)")
.unwrap()
{
QueryResult::Rows { rows, .. } => spg_engine::eval::value_to_text(&rows[0].values[0]),
other => panic!("{other:?}"),
};
assert_eq!(got, "1");
assert!(
err_of(
&mut e,
"SELECT row_number() OVER w FROM t704 WINDOW w AS (ORDER BY nosuch705)"
)
.contains("nosuch705")
);
}