use spg_engine::Engine;
use spg_storage::Value;
fn first(e: &mut Engine, sql: &str) -> Value<'static> {
match e.execute(sql).unwrap_or_else(|err| panic!("{sql}: {err}")) {
spg_engine::QueryResult::Rows { rows, .. } => rows
.first()
.and_then(|r| r.values.first())
.cloned()
.map(Value::into_owned)
.unwrap_or(Value::Null),
other => panic!("`{sql}` did not return rows: {other:?}"),
}
}
fn err(e: &mut Engine, sql: &str) -> String {
match e.execute(sql) {
Ok(v) => panic!("{sql}: expected an error, got {v:?}"),
Err(x) => format!("{x}"),
}
}
fn fixture() -> Engine {
let mut e = Engine::new();
e.execute("CREATE TABLE t (id INT)").unwrap();
e.execute("CREATE VIEW vv AS SELECT id FROM t").unwrap();
e
}
#[test]
fn relation_size_reads_the_regclass_spelling() {
let mut e = fixture();
e.execute("INSERT INTO t VALUES (1),(2)").unwrap();
let by_name = first(&mut e, "SELECT pg_relation_size('t')");
assert!(
matches!(by_name, Value::BigInt(n) if n > 0),
"a table with rows is not zero bytes: {by_name:?}"
);
assert_eq!(
first(&mut e, "SELECT pg_relation_size('t'::regclass)"),
by_name
);
let total = first(&mut e, "SELECT pg_total_relation_size('t'::regclass)");
assert!(matches!(total, Value::BigInt(n) if n > 0), "{total:?}");
}
#[test]
fn an_index_and_a_sequence_are_relations() {
let mut e = fixture();
e.execute("CREATE INDEX ix ON t (id)").unwrap();
e.execute("CREATE SEQUENCE sq").unwrap();
for name in ["ix", "sq"] {
let v = first(&mut e, &format!("SELECT '{name}'::regclass"));
assert!(matches!(v, Value::RegClass(_, _)), "{name}: {v:?}");
assert_eq!(
first(&mut e, &format!("SELECT '{name}'::regclass::text")),
Value::text(name),
);
}
assert_eq!(
first(&mut e, "SELECT pg_get_indexdef('ix'::regclass)"),
first(&mut e, "SELECT pg_get_indexdef('ix')"),
);
}
#[test]
fn pg_get_viewdef_accepts_a_regclass() {
let mut e = fixture();
let by_name = first(&mut e, "SELECT pg_get_viewdef('vv')");
let by_regclass = first(&mut e, "SELECT pg_get_viewdef('vv'::regclass)");
assert_eq!(by_regclass, by_name);
assert!(matches!(by_name, Value::Text(_)), "{by_name:?}");
assert_eq!(
first(&mut e, "SELECT pg_get_viewdef('public.vv'::regclass)"),
by_name,
);
}
#[test]
fn a_missing_relation_is_an_error_not_a_text_passthrough() {
let mut e = fixture();
assert_eq!(
err(&mut e, "SELECT 'nope'::regclass"),
"eval: type mismatch: relation \"nope\" does not exist",
);
assert!(
err(&mut e, "SELECT pg_get_viewdef('nope'::regclass)")
.contains("relation \"nope\" does not exist"),
);
}
#[test]
fn to_regclass_still_answers_null_for_a_miss() {
let mut e = fixture();
assert_eq!(first(&mut e, "SELECT to_regclass('nope')"), Value::Null);
}
#[test]
fn a_real_relation_keeps_its_oid() {
let mut e = fixture();
assert!(
matches!(first(&mut e, "SELECT 't'::regclass"), Value::RegClass(_, _)),
"a resolvable name keeps the oid half"
);
assert_eq!(
first(&mut e, "SELECT 't'::regclass::text"),
Value::text("t"),
);
}