ffi.rsannotatedffi.rssource197 lines · 6.7 KB · raw

Postgres helpers the scan needs that are static inline or macros in the headers, so bindgen does not carry them, and thin iterators over List. Each mirrors the PG18 definition named in its comment.

5use std::ffi::c_void;
7use pgrx::{pg_guard, pg_sys};

ExecProcNode (executor.h).

Safety

node is an initialized plan state of the running executor.

13pub unsafe fn exec_proc_node(node: *mut pg_sys::PlanState) -> *mut pg_sys::TupleTableSlot {
14    unsafe {
15        if !(*node).chgParam.is_null() {
16            pg_sys::ExecReScan(node);
17        }
18        (*node).ExecProcNode.expect("an initialized plan state has ExecProcNode")(node)
19    }
20}

TupIsNull (tuptable.h).

Safety

slot is NULL or a valid slot.

26pub unsafe fn tup_is_null(slot: *mut pg_sys::TupleTableSlot) -> bool {
27    slot.is_null() || unsafe { (*slot).tts_flags as u32 & pg_sys::TTS_FLAG_EMPTY != 0 }
28}

ExecClearTuple (tuptable.h).

Safety

slot is a valid slot.

34pub unsafe fn exec_clear_tuple(slot: *mut pg_sys::TupleTableSlot) -> *mut pg_sys::TupleTableSlot {
35    unsafe { (*(*slot).tts_ops).clear.expect("slot ops have clear")(slot) };
36    slot
37}

slot_getallattrs (tuptable.h).

Safety

slot is a valid, non-empty slot.

43pub unsafe fn slot_getallattrs(slot: *mut pg_sys::TupleTableSlot) {
44    unsafe {
45        let natts = (*(*slot).tts_tupleDescriptor).natts as i16;
46        if (*slot).tts_nvalid < natts {
47            pg_sys::slot_getsomeattrs_int(slot, natts as i32);
48        }
49    }
50}

ExecCopySlotMinimalTuple (tuptable.h): a copy in the current memory context.

Safety

slot is a valid, non-empty slot.

57pub unsafe fn copy_minimal_tuple(slot: *mut pg_sys::TupleTableSlot) -> pg_sys::MinimalTuple {
58    let copy = unsafe { (*(*slot).tts_ops).copy_minimal_tuple.expect("slot ops copy") };
59    // PG18 added `extra`, bytes reserved before the tuple; none here.
60    #[cfg(feature = "pg17")]
61    return unsafe { copy(slot) };
62    #[cfg(not(feature = "pg17"))]
63    return unsafe { copy(slot, 0) };
64}

ExecEvalExprSwitchContext (executor.h).

Safety

state was initialized for a plan node of the running executor, and econtext is that node's expression context.

71pub unsafe fn eval_expr(state: *mut pg_sys::ExprState, econtext: *mut pg_sys::ExprContext) -> Option<pg_sys::Datum> {
72    unsafe {
73        let old = pg_sys::CurrentMemoryContext;
74        pg_sys::CurrentMemoryContext = (*econtext).ecxt_per_tuple_memory;
75        let mut isnull = false;
76        let datum = (*state).evalfunc.expect("an initialized ExprState has evalfunc")(state, econtext, &mut isnull);
77        pg_sys::CurrentMemoryContext = old;
78        (!isnull).then_some(datum)
79    }
80}

ResetExprContext (executor.h).

Safety

econtext is a live expression context.

86pub unsafe fn reset_expr_context(econtext: *mut pg_sys::ExprContext) {
87    unsafe { pg_sys::MemoryContextReset((*econtext).ecxt_per_tuple_memory) };
88}

InvokeFunctionExecuteHook (objectaccess.h).

Safety

Called inside a transaction.

94pub unsafe fn invoke_function_execute_hook(funcid: pg_sys::Oid) {
95    unsafe {
96        let hook = pg_sys::object_access_hook;
97        if hook.is_some() {
98            pg_sys::RunFunctionExecuteHook(funcid);
99        }
100    }
101}

IsA(node, tag) (nodes.h).

Safety

node is NULL or points to a Node.

107pub unsafe fn is_a(node: *const c_void, tag: pg_sys::NodeTag) -> bool {
108    !node.is_null() && unsafe { (*(node as *const pg_sys::Node)).type_ } == tag
109}

list_length (pg_list.h).

Safety

list is NIL or a List.

115pub unsafe fn len(list: *mut pg_sys::List) -> usize {
116    if list.is_null() { 0 } else { unsafe { (*list).length as usize } }
117}

The pointer elements of list (NIL is empty).

Safety

list is NIL or a pointer List whose elements are *mut T, and it is not modified while the iterator lives.

124pub unsafe fn ptrs<T>(list: *mut pg_sys::List) -> impl Iterator<Item = *mut T> {
125    (0..unsafe { len(list) }).map(move |i| unsafe { (*(*list).elements.add(i)).ptr_value as *mut T })
126}

The integer elements of an IntList (NIL is empty).

Safety

As [ptrs], for an IntList.

132pub unsafe fn ints(list: *mut pg_sys::List) -> impl Iterator<Item = i32> {
133    (0..unsafe { len(list) }).map(move |i| unsafe { (*(*list).elements.add(i)).int_value })
134}

list with Ts appended.

Safety

list is NIL or a pointer List; the pointers stay valid as long as it.

140pub unsafe fn append<T>(mut list: *mut pg_sys::List, items: impl IntoIterator<Item = *mut T>) -> *mut pg_sys::List {
141    for item in items {
142        list = unsafe { pg_sys::lappend(list, item.cast()) };
143    }
144    list
145}

Visits every node of an expression tree, depth first, as expression_tree_walker does; visit returns false to skip a node's children. visit must not raise an ERROR.

Safety

node is NULL or an expression tree.

153pub unsafe fn walk(node: *mut pg_sys::Node, visit: &mut dyn FnMut(*mut pg_sys::Node) -> bool) {
154    #[pg_guard]
155    unsafe extern "C-unwind" fn walker(node: *mut pg_sys::Node, context: *mut c_void) -> bool {
156        if node.is_null() {
157            return false;
158        }
159        // SAFETY: `context` is the `&mut dyn FnMut` passed below, alive
160        // for the whole walk.
161        let visit = unsafe { &mut *(context as *mut &mut dyn FnMut(*mut pg_sys::Node) -> bool) };
162        if visit(node) {
163            unsafe { pg_sys::expression_tree_walker_impl(node, Some(walker), context) };
164        }
165        false
166    }
167    let mut visit = visit;
168    unsafe { walker(node, (&raw mut visit).cast()) };
169}

Rebuilds an expression tree, as expression_tree_mutator does: replace returns a node to put in place of one (its children are not visited), or None to copy it and visit its children. replace must not raise an ERROR.

Safety

node is NULL or an expression tree.

178pub unsafe fn mutate(
179    node: *mut pg_sys::Node,
180    replace: &mut dyn FnMut(*mut pg_sys::Node) -> Option<*mut pg_sys::Node>,
181) -> *mut pg_sys::Node {
182    #[pg_guard]
183    unsafe extern "C-unwind" fn mutator(node: *mut pg_sys::Node, context: *mut c_void) -> *mut pg_sys::Node {
184        if node.is_null() {
185            return node;
186        }
187        // SAFETY: `context` is the `&mut dyn FnMut` passed below, alive
188        // for the whole walk.
189        let replace = unsafe { &mut *(context as *mut &mut dyn FnMut(*mut pg_sys::Node) -> Option<*mut pg_sys::Node>) };
190        match replace(node) {
191            Some(new) => new,
192            None => unsafe { pg_sys::expression_tree_mutator_impl(node, Some(mutator), context) },
193        }
194    }
195    let mut replace = replace;
196    unsafe { mutator(node, (&raw mut replace).cast()) }
197}