postgres_extension/executor/
spi.rs

1
2use std::ffi::{CString,CStr};
3use crate::utils::memutils::MemoryContext;
4use libc::*;
5
6/* Plans are opaque structs for standard users of SPI */
7pub type SPIPlanPtr = *mut _SPI_plan;
8pub type HeapTuple = *mut HeapTupleData;
9pub type TupleDesc = *mut TupleDescData;
10pub type SubTransactionId = u32;
11
12#[repr(C)]
13pub struct TupleDescData { pub natts: c_int, _private: [u8; 0] }
14#[repr(C)]
15pub struct HeapTupleData { _private: [u8; 0] }
16#[repr(C)]
17pub struct _SPI_plan { _private: [u8; 0] }
18#[repr(C)]
19pub struct slist_node { _private: [u8; 0] }
20
21#[repr(C)]
22pub struct SPITupleTable
23{
24    tuptabcxt: MemoryContext,	/* memory context of result table */
25    alloced: u64,		/* # of alloced vals */
26    free: u64,			/* # of free vals */
27    pub tupdesc: TupleDesc,		/* tuple descriptor */
28    pub vals: *mut HeapTuple,			/* tuples */
29    next: *mut slist_node,			/* link for internal bookkeeping */
30    subid: SubTransactionId,		/* subxact in which tuptable was created */
31}
32
33pub const SPI_ERROR_CONNECT: i32 = -1;
34pub const SPI_ERROR_COPY: i32 = -2;
35pub const SPI_ERROR_OPUNKNOWN: i32 = -3;
36pub const SPI_ERROR_UNCONNECTED: i32 = -4;
37pub const SPI_ERROR_CURSOR: i32 = -5;	/* not used anymore */
38pub const SPI_ERROR_ARGUMENT: i32 = -6;
39pub const SPI_ERROR_PARAM: i32 = -7;
40pub const SPI_ERROR_TRANSACTION: i32 = -8;
41pub const SPI_ERROR_NOATTRIBUTE: i32 = -9;
42pub const SPI_ERROR_NOOUTFUNC: i32 = -10;
43pub const SPI_ERROR_TYPUNKNOWN: i32 = -11;
44pub const SPI_ERROR_REL_DUPLICATE: i32 = -12;
45pub const SPI_ERROR_REL_NOT_FOUND: i32 = -13;
46
47pub const SPI_OK_CONNECT: i32 = 1;
48pub const SPI_OK_FINISH: i32 = 2;
49pub const SPI_OK_FETCH: i32 = 3;
50pub const SPI_OK_UTILITY: i32 = 4;
51pub const SPI_OK_SELECT: i32 = 5;
52pub const SPI_OK_SELINTO: i32 = 6;
53pub const SPI_OK_INSERT: i32 = 7;
54pub const SPI_OK_DELETE: i32 = 8;
55pub const SPI_OK_UPDATE: i32 = 9;
56pub const SPI_OK_CURSOR: i32 = 10;
57pub const SPI_OK_INSERT_RETURNING: i32 = 11;
58pub const SPI_OK_DELETE_RETURNING: i32 = 12;
59pub const SPI_OK_UPDATE_RETURNING: i32 = 13;
60pub const SPI_OK_REWRITTEN: i32 = 14;
61pub const SPI_OK_REL_REGISTER: i32 = 15;
62pub const SPI_OK_REL_UNREGISTER: i32 = 16;
63pub const SPI_OK_TD_REGISTER: i32 = 17;
64pub const SPI_OPT_NONATOMIC: i32 = (1 << 0);
65
66pub mod c {
67    use libc::*;
68    use super::*;
69    extern {
70        pub static SPI_processed: u64;
71        pub static SPI_tuptable: *mut SPITupleTable;
72        pub static SPI_result: c_int;
73
74        pub fn SPI_connect() -> c_int;
75        pub fn SPI_connect_ext(options: c_int) -> c_int;
76        pub fn SPI_finish() -> c_int;
77        pub fn SPI_execute(src: *const c_char, read_only: bool,
78                           tcount: c_long) -> c_int;
79        /*
80        pub fn SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
81	bool read_only, long tcount) -> c_int;
82        pub fn SPI_execute_plan_with_paramlist(SPIPlanPtr plan,
83	ParamListInfo params,
84	bool read_only, long tcount) -> c_int;
85         */
86        pub fn SPI_exec(src: *const c_char, tcount: c_long) -> c_int;
87        /*
88        pub fn SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls,
89	long tcount) -> c_int;
90        pub fn SPI_execute_snapshot(SPIPlanPtr plan,
91	Datum *Values, const char *Nulls,
92	Snapshot snapshot,
93	Snapshot crosscheck_snapshot,
94	bool read_only, bool fire_triggers,
95        tcount: c_long) -> c_int;
96        pub fn SPI_execute_with_args(const char *src,
97	int nargs, Oid *argtypes,
98	Datum *Values, const char *Nulls,
99	bool read_only, long tcount) -> int;
100        pub fn SPI_prepare(const char *src, int nargs, Oid *argtypes) -> SPIPlanPtr;
101        pub fn SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
102	int cursorOptions) -> SPIPlanPtr;
103        pub fn SPI_prepare_params(const char *src,
104	ParserSetupHook parserSetup,
105	void *parserSetupArg,
106	int cursorOptions) -> SPIPlanPtr;
107        pub fn SPI_keepplan(SPIPlanPtr plan) -> int;
108        pub fn SPI_saveplan(SPIPlanPtr plan) -> SPIPlanPtr;
109        pub fn SPI_freeplan(SPIPlanPtr plan) -> int;
110
111        pub fn SPI_getargtypeid(SPIPlanPtr plan, int argIndex) -> Oid;
112        pub fn SPI_getargcount(SPIPlanPtr plan) -> int;
113        pub fn SPI_is_cursor_plan(SPIPlanPtr plan) -> bool;
114        pub fn SPI_plan_is_valid(SPIPlanPtr plan) -> bool;
115        pub fn SPI_result_code_string(int code) -> *const c_char;
116
117        pub fn SPI_plan_get_plan_sources(SPIPlanPtr plan) -> *mut List;
118        pub fn SPI_plan_get_cached_plan(SPIPlanPtr plan) -> *mut CachedPlan;
119
120        pub fn SPI_copytuple(HeapTuple tuple) -> HeapTuple;
121        pub fn SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc) -> HeapTupleHeader;
122        pub fn SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
123	int *attnum, Datum *Values, const char *Nulls) -> HeapTuple;
124        pub fn SPI_fnumber(TupleDesc tupdesc, const char *fname) -> int;
125        pub fn SPI_fname(TupleDesc tupdesc, int fnumber) -> *const c_char;
126         */
127        pub fn SPI_getvalue(tuple: HeapTuple, tupdesc: TupleDesc,
128                            fnumber: c_int) -> *const c_char;
129        /*
130        pub fn SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull) -> Datum;
131        pub fn SPI_gettype(TupleDesc tupdesc, int fnumber) -> *const c_char
132        pub fn SPI_gettypeid(TupleDesc tupdesc, int fnumber) -> Oid;
133        pub fn SPI_getrelname(Relation rel) -> *const c_char;
134        pub fn SPI_getnspname(Relation rel) -> *const c_char;
135        pub fn SPI_palloc(Size size) -> *mut c_void;
136        pub fn SPI_repalloc(void *pointer, Size size) -> *mut c_void;
137        pub fn SPI_pfree(void *pointer);
138        pub fn SPI_datumTransfer(Datum value, bool typByVal, int typLen) -> Datum;
139        pub fn SPI_freetuple(HeapTuple pointer) -> void;
140        */
141        pub fn SPI_freetuptable(tuptable: *mut SPITupleTable);
142        /*
143        pub fn SPI_cursor_open(const char *name, SPIPlanPtr plan,
144	Datum *Values, const char *Nulls, bool read_only) -> Portal;
145        pub fn SPI_cursor_open_with_args(const char *name,
146	const char *src,
147	int nargs, Oid *argtypes,
148	Datum *Values, const char *Nulls,
149	bool read_only, int cursorOptions) -> Portal;
150        pub fn SPI_cursor_open_with_paramlist(const char *name, SPIPlanPtr plan,
151	ParamListInfo params, bool read_only) -> Portal;
152        pub fn SPI_cursor_find(const char *name) -> Portal;
153        pub fn SPI_cursor_fetch(Portal portal, bool forward, long count) -> void;
154        pub fn SPI_cursor_move(Portal portal, bool forward, long count) -> void;
155        pub fn SPI_scroll_cursor_fetch(Portal, FetchDirection direction, long count) -> void;
156        pub fn SPI_scroll_cursor_move(Portal, FetchDirection direction, long count) -> void;
157        pub fn SPI_cursor_close(Portal portal) -> void;
158
159        pub fn SPI_register_relation(EphemeralNamedRelation enr) -> int;
160        pub fn SPI_unregister_relation(const char *name) -> int;
161        pub fn SPI_register_trigger_data(tdata: *mut TriggerData) -> c_int;
162
163        pub fn SPI_start_transaction();
164        pub fn SPI_commit();
165        pub fn SPI_rollback();
166
167        pub fn SPICleanup();
168        pub fn AtEOXact_SPI(isCommit: bool);
169        pub fn AtEOSubXact_SPI(isCommit: bool, mySubid: SubTransactionId);
170        pub fn SPI_inside_nonatomic_context() -> bool;
171         */
172    }
173}
174
175
176/*
177
178  let spi = spi_connect(options);
179  let res1 = spi.exec("select * from foo");
180  let res2 = spi.exec("select * from bar");
181  spi.freeresult(res2);
182  let res3 = spi.exec("select * from baz");
183  drop(spi);
184 */
185
186pub struct SPIConnection;
187
188pub struct SPIResult<'a> {
189    pub status: i32,
190    processed: u64,
191    tuptable: &'a mut SPITupleTable,
192}
193
194impl SPIConnection {
195    pub fn execute(&self, query: &str, readonly: bool) -> Result<SPIResult,i32> {
196        let query_cstring = CString::new(query).unwrap();
197        let query_ptr = query_cstring.as_ptr();
198        unsafe {
199            let status = c::SPI_execute(query_ptr, readonly, 0);
200            if status >= 0 {
201                return Ok(SPIResult {
202                    status: status,
203                    processed: c::SPI_processed,
204                    tuptable: &mut *c::SPI_tuptable,
205                })
206            } else {
207                return Err(status);
208            }
209        }
210    }
211}
212
213impl Drop for SPIConnection {
214    fn drop(&mut self) {
215        unsafe {
216            c::SPI_finish();
217        }
218    }
219}
220
221impl<'a> SPIResult<'a> {
222    pub fn tuples(&self) -> &'a [&HeapTupleData] {
223        unsafe {
224            let vals: *const &HeapTupleData = (*self.tuptable).vals
225                as *const &HeapTupleData;
226            return std::slice::from_raw_parts(vals, self.processed as usize);
227        }
228    }
229    pub fn tupdesc(&self) -> &'a TupleDescData {
230        unsafe {
231            return &*(*self.tuptable).tupdesc;
232        }
233    }
234}
235
236impl<'a> Drop for SPIResult<'a> {
237    fn drop(&mut self) {
238        unsafe {
239            c::SPI_freetuptable(self.tuptable);
240        }
241    }
242}
243
244pub fn spi_connect() -> SPIConnection {
245    unsafe {
246        c::SPI_connect();
247    }
248    return SPIConnection {};
249}
250
251pub fn spi_getvalue(tuple: &HeapTupleData,
252                    tupdesc: &TupleDescData,
253                    attno: c_int) -> String {
254    unsafe {
255        let tuple_ptr: HeapTuple = tuple as *const HeapTupleData
256            as *mut HeapTupleData;
257        let tupdesc_ptr: TupleDesc = tupdesc as *const TupleDescData
258            as *mut TupleDescData;
259        let val_ptr = c::SPI_getvalue(tuple_ptr, tupdesc_ptr, attno);
260        let val_str = CStr::from_ptr(val_ptr).to_str().unwrap();
261        return CString::new(val_str).unwrap().into_string().unwrap();
262    };
263}