use spg_engine::{Engine, QueryResult};
use spg_storage::Value;
fn scalar(e: &mut Engine, sql: &str) -> String {
use spg_engine::eval as f;
let r = e
.execute(sql)
.unwrap_or_else(|err| panic!("{sql}: {err:?}"));
let QueryResult::Rows { rows, .. } = r else {
panic!("{sql}: expected Rows");
};
match &rows[0].values[0] {
Value::Null => "NULL".into(),
Value::Bool(b) => if *b { "t" } else { "f" }.into(),
Value::SmallInt(n) => n.to_string(),
Value::Int(n) => n.to_string(),
Value::BigInt(n) => n.to_string(),
Value::Text(s) => s.to_string(),
Value::Float(x) => format!("{x}"),
Value::Numeric { scaled, scale, .. } => f::format_numeric(*scaled, *scale),
other => panic!("{sql}: unexpected {other:?}"),
}
}
#[test]
fn to_char_numeric_matches_pg() {
let mut e = Engine::new();
let cases: &[(&str, &str)] = &[
("SELECT to_char(1234.5,'9,999.99')", " 1,234.50"),
("SELECT to_char(1234.5,'FM9,999.99')", "1,234.5"),
("SELECT to_char(1234567.89,'9,999,999.99')", " 1,234,567.89"),
("SELECT to_char(1234.5,'999,999.99')", " 1,234.50"),
("SELECT to_char(-12.3,'999.99')", " -12.30"),
("SELECT to_char(-5,'9999.00')", " -5.00"),
("SELECT to_char(-0.5,'999.99')", " -.50"),
("SELECT to_char(-100,'999')", "-100"),
("SELECT to_char(100,'999')", " 100"),
("SELECT to_char(0.0001,'9.99')", " .00"),
("SELECT to_char(0.5,'9.99')", " .50"),
("SELECT to_char(0,'9.99')", " .00"),
("SELECT to_char(0,'9')", " 0"),
("SELECT to_char(0,'9999')", " 0"),
("SELECT to_char(0,'990')", " 0"),
("SELECT to_char(0.5,'990.99')", " 0.50"),
("SELECT to_char(1234.567,'9,999.99')", " 1,234.57"),
("SELECT to_char(2.5,'9')", " 3"),
("SELECT to_char(1.5,'9')", " 2"),
("SELECT to_char(1234,'0000')", " 1234"),
("SELECT to_char(5,'009')", " 005"),
("SELECT to_char(12,'0009')", " 0012"),
("SELECT to_char(1234.5,'999')", " ###"),
("SELECT to_char(-1234.5,'999')", "-###"),
("SELECT to_char(99.5,'99')", " ##"),
("SELECT to_char(1234.56,'999D99')", " ###.##"),
("SELECT to_char(1234.567,'999.99')", " ###.##"),
("SELECT to_char(1234.5,'S9999.99')", "+1234.50"),
("SELECT to_char(12.3,'S999.99')", " +12.30"),
("SELECT to_char(-12.3,'S999.99')", " -12.30"),
("SELECT to_char(0,'S999.99')", " +.00"),
("SELECT to_char(1000000,'9G999G999')", " 1,000,000"),
("SELECT to_char(1234.56,'999D99')", " ###.##"),
("SELECT to_char(12.34,'99D99')", " 12.34"),
("SELECT to_char(1000,'9G999')", " 1,000"),
("SELECT to_char(5,'FM9.99')", "5."),
("SELECT to_char(1234,'FM9999.99')", "1234."),
("SELECT to_char(0.5,'FM9.99')", ".5"),
("SELECT to_char(-0.5,'FM9.99')", "-.5"),
("SELECT to_char(0,'FM9.99')", "0."),
("SELECT to_char(0,'FM990.00')", "0.00"),
("SELECT to_char(-5,'FM9999.00')", "-5.00"),
("SELECT to_char(1234.5,'FM9G999D99')", "1,234.5"),
("SELECT to_char(1234.5,'FM9999')", "1235"),
("SELECT to_char(1234.5,'FM999')", "###"),
("SELECT to_char(0,'FM9')", "0"),
];
for (sql, want) in cases {
assert_eq!(scalar(&mut e, sql), *want, "{sql}");
}
}
#[test]
fn numeric_round_family_scale_zero() {
let mut e = Engine::new();
let cases: &[(&str, &str)] = &[
("SELECT round(2.5::numeric)", "3"),
("SELECT round(2.567::numeric)", "3"),
("SELECT round('-2.5'::numeric)", "-3"),
("SELECT round('-0.5'::numeric)", "-1"),
("SELECT round('-2.4'::numeric)", "-2"),
("SELECT round('-2.6'::numeric)", "-3"),
("SELECT trunc(2.7::numeric)", "2"),
("SELECT trunc('-2.7'::numeric)", "-2"),
("SELECT trunc('-2.9'::numeric)", "-2"),
("SELECT ceil(2.1::numeric)", "3"),
("SELECT ceil('-2.5'::numeric)", "-2"),
("SELECT ceil('-2.9'::numeric)", "-2"),
("SELECT ceil(2.0::numeric)", "2"),
("SELECT floor(2.9::numeric)", "2"),
("SELECT floor('-2.5'::numeric)", "-3"),
("SELECT floor('-2.1'::numeric)", "-3"),
("SELECT floor('-2.0'::numeric)", "-2"),
("SELECT round(2.45::numeric, 1)", "2.5"),
("SELECT round('-2.45'::numeric, 1)", "-2.5"),
("SELECT trunc(2.789::numeric, 2)", "2.78"),
];
for (sql, want) in cases {
assert_eq!(scalar(&mut e, sql), *want, "{sql}");
}
}
#[test]
fn numeric_to_int_cast_rounds() {
let mut e = Engine::new();
let cases: &[(&str, &str)] = &[
("SELECT (1.9::int)", "2"),
("SELECT (2.4::int)", "2"),
("SELECT (2.5::int)", "3"),
("SELECT (1.5::int)", "2"),
("SELECT (0.5::int)", "1"),
("SELECT (-1.5::int)", "-2"),
("SELECT (2.5::numeric::int)", "3"),
("SELECT (2.4::numeric::int)", "2"),
("SELECT (1.9::bigint)", "2"),
("SELECT (-2.5::bigint)", "-3"),
];
for (sql, want) in cases {
assert_eq!(scalar(&mut e, sql), *want, "{sql}");
}
}
#[test]
fn numeric_ops_unchanged() {
let mut e = Engine::new();
let cases: &[(&str, &str)] = &[
("SELECT mod(5,3)", "2"),
("SELECT mod(-5,3)", "-2"),
("SELECT mod(5,-3)", "2"),
("SELECT (5 % 3)", "2"),
("SELECT div(7,2)", "3"),
("SELECT div(-7,2)", "-3"),
("SELECT gcd(12,18)", "6"),
("SELECT lcm(4,6)", "12"),
("SELECT power(2,10)", "1024"),
("SELECT (5 >> 1)", "2"),
("SELECT (5 << 1)", "10"),
("SELECT (5 & 3)", "1"),
("SELECT (5 | 2)", "7"),
("SELECT (5 # 3)", "6"),
("SELECT round(1234.5678, 2)", "1234.57"),
("SELECT round(1234.5678, -2)", "1200"),
("SELECT trunc(1234.5678, 2)", "1234.56"),
("SELECT ceil(-2.5)", "-2"),
("SELECT floor(-2.5)", "-3"),
];
for (sql, want) in cases {
assert_eq!(scalar(&mut e, sql), *want, "{sql}");
}
}