guc.rsannotatedguc.rssource64 lines · 2.1 KB · raw

String settings validated when they are set. pgrx 0.19.2 made GUC hooks unsafe (#2348), so the hook lives here and callers supply the check as safe code through [StringCheck].

5use std::ffi::{CStr, CString, c_char, c_void};
7use pgrx::{GucCheckError, GucContext, GucFlags, GucRegistry, GucSetting, pg_guard, pg_sys};

Decides whether a proposed value is valid; Err carries the detail shown to the user. An unset value (NULL) is not checked.

11pub trait StringCheck {
12    fn check(value: &str) -> Result<(), String>;
13}

Defines a string setting whose every SET, config-file value and placeholder adopted at load passes C::check first. Postgres reports a refusal as invalid value for parameter "<name>" with the detail.

18pub fn define_checked_string<C: StringCheck>(
19    name: &'static CStr,
20    short_description: &'static CStr,
21    long_description: &'static CStr,
22    setting: &'static GucSetting<Option<CString>>,
23    context: GucContext,
24    flags: GucFlags,
25) {
26    // SAFETY: the check hook is #[pg_guard]ed, so a panic in `C::check`
27    // becomes an ERROR instead of unwinding into Postgres.
28    unsafe {
29        GucRegistry::define_string_guc_with_hooks(
30            name,
31            short_description,
32            long_description,
33            setting,
34            context,
35            flags,
36            Some(check_hook::<C>),
37            None,
38            None,
39        );
40    }
41}
43#[pg_guard]
44unsafe extern "C-unwind" fn check_hook<C: StringCheck>(
45    newval: *mut *mut c_char,
46    _extra: *mut *mut c_void,
47    _source: pg_sys::GucSource::Type,
48) -> bool {
49    // SAFETY: guc.c passes a valid pointer to the proposed value, which
50    // is NULL or a NUL-terminated string for the duration of the call.
51    let value = unsafe { *newval };
52    if value.is_null() {
53        return true;
54    }
55    let value = unsafe { CStr::from_ptr(value) }.to_string_lossy();
56    match C::check(&value) {
57        Ok(()) => true,
58        Err(detail) => {
59            // SAFETY: called from within a check hook, as `apply` requires.
60            unsafe { GucCheckError::default().with_detail(detail).apply() };
61            false
62        }
63    }
64}