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 FUNCTION ff(a int) RETURNS int LANGUAGE sql AS 'SELECT a + 1'")
.unwrap();
e.execute("CREATE FUNCTION g(a int, b text) RETURNS int LANGUAGE sql AS 'SELECT a'")
.unwrap();
e
}
#[test]
fn regproc_resolves_a_user_function() {
let mut e = fixture();
assert!(matches!(
first(&mut e, "SELECT 'ff'::regproc"),
Value::RegProc(_, _)
));
assert_eq!(
first(&mut e, "SELECT 'ff'::regproc::text"),
Value::text("ff")
);
assert_eq!(
err(&mut e, "SELECT 'nosuchfn'::regproc"),
"eval: type mismatch: function \"nosuchfn\" does not exist",
);
assert!(err(&mut e, "SELECT 'lower'::regproc").contains("more than one function named"),);
}
#[test]
fn regprocedure_renders_the_canonical_signature() {
let mut e = fixture();
assert_eq!(
first(&mut e, "SELECT 'ff(int)'::regprocedure::text"),
Value::text("ff(integer)"),
);
assert_eq!(
first(&mut e, "SELECT 'g(int,text)'::regprocedure::text"),
Value::text("g(integer,text)"),
);
assert_eq!(
err(&mut e, "SELECT 'g(text,int)'::regprocedure"),
"eval: type mismatch: function \"g(text,int)\" does not exist",
);
}
#[test]
fn to_regproc_and_to_regprocedure_answer_for_user_functions() {
let mut e = fixture();
assert_eq!(
first(&mut e, "SELECT to_regproc('ff')::text"),
Value::text("ff")
);
assert_eq!(
first(&mut e, "SELECT to_regproc('g')::text"),
Value::text("g")
);
assert_eq!(first(&mut e, "SELECT to_regproc('nosuchfn')"), Value::Null);
assert_eq!(
first(&mut e, "SELECT to_regprocedure('ff(integer)')::text"),
Value::text("ff(integer)"),
);
assert_eq!(
first(&mut e, "SELECT to_regprocedure('ff(text)')"),
Value::Null
);
}
#[test]
fn pg_get_functiondef_takes_a_regproc() {
let mut e = fixture();
let by_regproc = first(&mut e, "SELECT pg_get_functiondef('ff'::regproc)");
let Value::Text(def) = &by_regproc else {
panic!("{by_regproc:?}");
};
assert!(
def.contains("CREATE OR REPLACE FUNCTION public.ff(a integer)"),
"{def}"
);
assert_eq!(
first(
&mut e,
"SELECT pg_get_functiondef(oid) FROM pg_proc WHERE proname = 'ff'"
),
by_regproc,
);
assert_eq!(
first(&mut e, "SELECT pg_get_functiondef(999999)"),
Value::Null
);
}