Running as a relation's owner, as a SECURITY DEFINER function does.

The answer cache is readable and writable only by its owner: a role that could write it could forge answers, as jev.mock_response would (contract Cost and safety). A role granted EXECUTE on jev_prob must still use it, so the scan reaches it as the owner.

8use std::ffi::CStr;
10use pgrx::{pg_sys, spi::Spi};

Runs f as the owner of relation. The previous user is restored on return and on unwind; an ERROR's abort restores it too.

14pub fn as_owner_of<T>(relation: &CStr, f: impl FnOnce() -> T) -> Result<T, String> {
15    let name = relation.to_str().map_err(|e| e.to_string())?;
16    let owner = Spi::get_one_with_args::<pg_sys::Oid>(
17        "SELECT relowner FROM pg_class WHERE oid = to_regclass($1)",
18        &[name.into()],
19    )
20    .map_err(|e| e.to_string())?
21    .ok_or_else(|| format!("{name} does not exist"))?;
22    let mut previous = pg_sys::InvalidOid;
23    let mut context = 0;
24    // SAFETY: the pair a SECURITY DEFINER call makes (fmgr.c), on the
25    // backend thread; `Restore` undoes it exactly once.
26    unsafe {
27        pg_sys::GetUserIdAndSecContext(&mut previous, &mut context);
28        pg_sys::SetUserIdAndSecContext(owner, context | pg_sys::SECURITY_LOCAL_USERID_CHANGE as i32);
29    }
30    let _restore = Restore { previous, context };
31    Ok(f())
32}
34struct Restore {
35    previous: pg_sys::Oid,
36    context: i32,
37}
38
39impl Drop for Restore {
40    fn drop(&mut self) {
41        // SAFETY: restores what `as_owner_of` read.
42        unsafe { pg_sys::SetUserIdAndSecContext(self.previous, self.context) }
43    }
44}