use spg_engine::{Engine, QueryResult};
fn one(e: &mut Engine, sql: &str) -> String {
let r = e
.execute(sql)
.unwrap_or_else(|err| panic!("{sql}: {err:?}"));
let QueryResult::Rows { rows, .. } = r else {
panic!("expected Rows from {sql}");
};
assert_eq!(rows.len(), 1, "{sql}");
spg_engine::eval::value_to_text(&rows[0].values[0])
}
fn err(e: &mut Engine, sql: &str) -> String {
match e.execute(sql) {
Ok(_) => panic!("{sql}: expected an error"),
Err(x) => format!("{x}")
.replace("eval: type mismatch: ", "")
.replace("unsupported: ", ""),
}
}
#[test]
fn a_precision_past_i128_is_accepted() {
let mut e = Engine::new();
assert_eq!(one(&mut e, "SELECT 1.5::numeric(50,10)"), "1.5000000000");
assert_eq!(
one(
&mut e,
"SELECT 12345678901234567890123456789012345678901234567.5::numeric(50,2)",
),
"12345678901234567890123456789012345678901234567.50",
);
}
#[test]
fn a_declared_scale_no_i128_can_hold_still_pads() {
let mut e = Engine::new();
let got = one(&mut e, "SELECT 1.5::numeric(1000,999)");
assert!(got.starts_with("1.5"), "{got}");
assert_eq!(got.len(), "1.".len() + 999, "999 fractional digits");
assert!(got[2..].bytes().skip(1).all(|b| b == b'0'), "{got}");
}
#[test]
fn the_typmod_bounds_are_pgs_and_are_reported_pgs_way() {
let mut e = Engine::new();
assert_eq!(
err(&mut e, "SELECT 1.5::numeric(1001)"),
"NUMERIC precision 1001 must be between 1 and 1000",
);
assert_eq!(
err(&mut e, "SELECT 1.5::numeric(0)"),
"NUMERIC precision 0 must be between 1 and 1000",
);
assert_eq!(
err(&mut e, "SELECT 1.5::numeric(10,1001)"),
"NUMERIC scale 1001 must be between -1000 and 1000",
);
}
#[test]
fn the_declared_precision_still_binds() {
let mut e = Engine::new();
assert_eq!(
err(
&mut e,
"SELECT 123456789012345678901234567890123456789012345678901.5::numeric(50,2)",
),
"numeric field overflow DETAIL: A field with precision 50, scale 2 must round to an \
absolute value less than 10^48.",
);
}
#[test]
fn a_wide_column_round_trips_and_enforces() {
let mut e = Engine::new();
e.execute("CREATE TABLE t (a numeric(50,10))").unwrap();
e.execute("INSERT INTO t VALUES (1234567890123456789012345678901234567890.1234567890)")
.unwrap();
assert_eq!(
one(&mut e, "SELECT a FROM t"),
"1234567890123456789012345678901234567890.1234567890",
);
assert!(
e.execute("INSERT INTO t VALUES (12345678901234567890123456789012345678901.1)")
.is_err(),
);
assert_eq!(one(&mut e, "SELECT count(*) FROM t"), "1");
}
#[test]
fn information_schema_reports_the_wide_typmod() {
let mut e = Engine::new();
e.execute("CREATE TABLE t (a numeric(50,10), b numeric(1000,500), c numeric)")
.unwrap();
let r = e
.execute(
"SELECT column_name, numeric_precision, numeric_scale \
FROM information_schema.columns WHERE table_name = 't' ORDER BY ordinal_position",
)
.unwrap();
let QueryResult::Rows { rows, .. } = r else {
panic!("expected Rows");
};
let got: Vec<String> = rows
.iter()
.map(|row| {
row.values
.iter()
.map(|v| match v {
spg_storage::Value::Null => String::new(),
other => spg_engine::eval::value_to_text(other),
})
.collect::<Vec<_>>()
.join("|")
})
.collect();
assert_eq!(got, vec!["a|50|10", "b|1000|500", "c||"]);
}