use spg_engine::{Engine, QueryResult};
fn one(e: &mut Engine, sql: &str) -> String {
match e.execute(sql).unwrap_or_else(|err| panic!("{sql}: {err}")) {
QueryResult::Rows { rows, .. } => rows
.first()
.map(|r| {
r.values
.iter()
.map(|v| match v {
spg_storage::Value::Text(t) => t.to_string(),
spg_storage::Value::Bool(b) => String::from(if *b { "t" } else { "f" }),
other => format!("{other:?}"),
})
.collect::<Vec<_>>()
.join("|")
})
.unwrap_or_default(),
other => panic!("expected rows from {sql}, got {other:?}"),
}
}
fn rendered(e: &mut Engine, expr: &str) -> String {
one(e, &format!("SELECT ({expr})::text"))
}
fn err(e: &mut Engine, sql: &str) -> String {
match e.execute(sql) {
Err(x) => format!("{x}"),
Ok(other) => panic!("expected {sql} to be refused, got {other:?}"),
}
}
#[test]
fn the_two_infinities_parse_and_render() {
let mut e = Engine::new();
for (input, want) in [
("'infinity'::interval", "infinity"),
("'-infinity'::interval", "-infinity"),
("'+infinity'::interval", "infinity"),
("'Infinity'::interval", "infinity"),
("'INFINITY'::interval", "infinity"),
("'@ infinity'::interval", "infinity"),
("'infinity '::interval", "infinity"),
("'infinity'::text::interval", "infinity"),
] {
assert_eq!(rendered(&mut e, input), want, "{input}");
}
}
#[test]
fn the_abbreviation_is_not_accepted() {
let mut e = Engine::new();
let got = err(&mut e, "SELECT 'inf'::interval");
assert!(
got.contains("invalid input syntax for type interval"),
"{got}"
);
}
#[test]
fn they_sit_outside_every_finite_interval() {
let mut e = Engine::new();
for (expr, want) in [
("'infinity'::interval = 'infinity'::interval", "t"),
("'infinity'::interval > '100 years'::interval", "t"),
("'-infinity'::interval < '-100 years'::interval", "t"),
("'infinity'::interval >= 'infinity'::interval", "t"),
("'infinity'::interval <> '1 day'::interval", "t"),
("isfinite('infinity'::interval)", "f"),
("isfinite('-infinity'::interval)", "f"),
("isfinite('1 day'::interval)", "t"),
] {
assert_eq!(one(&mut e, &format!("SELECT {expr}")), want, "{expr}");
}
}
#[test]
fn arithmetic_answers_what_postgresql_answers() {
let mut e = Engine::new();
for (expr, want) in [
("-('infinity'::interval)", "-infinity"),
("-('-infinity'::interval)", "infinity"),
("'infinity'::interval * 2", "infinity"),
("'infinity'::interval * -1", "-infinity"),
("'-infinity'::interval * -2", "infinity"),
("'infinity'::interval / 2", "infinity"),
("'infinity'::interval + '1 day'::interval", "infinity"),
("'1 day'::interval + 'infinity'::interval", "infinity"),
("'1 day'::interval - 'infinity'::interval", "-infinity"),
("'-infinity'::interval + '-infinity'::interval", "-infinity"),
("justify_days('infinity'::interval)", "infinity"),
("justify_hours('infinity'::interval)", "infinity"),
("justify_interval('-infinity'::interval)", "-infinity"),
] {
assert_eq!(rendered(&mut e, expr), want, "{expr}");
}
}
#[test]
fn an_indeterminate_form_is_refused() {
let mut e = Engine::new();
for expr in [
"SELECT 'infinity'::interval - 'infinity'::interval",
"SELECT '-infinity'::interval + 'infinity'::interval",
"SELECT 'infinity'::interval * 0",
] {
let got = err(&mut e, expr);
assert!(got.contains("interval out of range"), "{expr}: {got}");
}
}
#[test]
fn a_timestamp_shifted_by_one_becomes_infinite() {
let mut e = Engine::new();
assert_eq!(
rendered(&mut e, "'2020-01-01'::timestamp + 'infinity'::interval"),
"infinity"
);
assert_eq!(
rendered(&mut e, "'2020-01-01'::timestamp - 'infinity'::interval"),
"-infinity"
);
assert_eq!(
rendered(&mut e, "'2020-01-01'::date + 'infinity'::interval"),
"infinity"
);
assert_eq!(one(&mut e, "SELECT isfinite('infinity'::timestamp)"), "f");
}
#[test]
fn it_survives_storage_and_every_collection() {
let mut e = Engine::new();
e.execute("CREATE TABLE ivt (id int, iv interval, arr interval[])")
.unwrap();
e.execute(
"INSERT INTO ivt VALUES (1,'infinity','{infinity,1 day}'),\
(2,'-infinity','{-infinity}'),(3,'1 day','{1 day}')",
)
.unwrap();
assert_eq!(
one(&mut e, "SELECT iv::text FROM ivt WHERE id = 1"),
"infinity"
);
assert_eq!(
one(&mut e, "SELECT iv::text FROM ivt WHERE id = 2"),
"-infinity"
);
assert_eq!(
one(&mut e, "SELECT arr::text FROM ivt WHERE id = 1"),
"{infinity,\"1 day\"}"
);
assert_eq!(
one(&mut e, "SELECT (arr)[1]::text FROM ivt WHERE id = 1"),
"infinity"
);
assert_eq!(one(&mut e, "SELECT id FROM ivt ORDER BY iv"), "Int(2)");
assert_eq!(
one(&mut e, "SELECT min(iv)::text, max(iv)::text FROM ivt"),
"-infinity|infinity"
);
assert_eq!(
one(&mut e, "SELECT count(*) FROM ivt WHERE isfinite(iv)"),
"BigInt(1)"
);
assert_eq!(
one(&mut e, "SELECT id FROM ivt WHERE iv = 'infinity'"),
"Int(1)"
);
assert_eq!(
one(&mut e, "SELECT id FROM ivt WHERE iv > '1 year'"),
"Int(1)"
);
}