I am trying to understand how to write C functions to create extensions for PostgreSQl but so far I am lost.
I want to write a very basic function that takes an integer and increments it 10 times. I am planning to use this as an example for Set Returning Functions and data persistence across calls using memory contexts. My problem here is data persistence between calls: the function is called once for each result and the memory is flushed between each calls, meaning that my integer disappears and the final result is incorrect.
Here is what I wrote so far:
/**
* Function that returns a set of integers (argument +10, step 1)
* Arguments:
* - int32 i = the original number to increment
*/
PG_FUNCTION_INFO_V1(addTen);
Datum addTen(PG_FUNCTION_ARGS) {
int32 i;
FuncCallContext *funcctx;
int call_cntr;
int max_calls;
// Code executed only on first call of the function
if (SRF_IS_FIRSTCALL()) {
MemoryContext oldcontext;
// Initializing the function context for cross call persistence
funcctx = SRF_FIRSTCALL_INIT();
// Context memory for multi calls
oldcontext = MemoryContextSwitchTo(funcctx -> multi_call_memory_ctx);
// Getting argument (original integer)
if (PG_ARGISNULL(0))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("Table cannot be NULL")));
i = (int32) palloc(sizeof(int32));
i = PG_GETARG_INT32(0);
// Alloacting space to save the integer and make it persistent between calls
funcctx->user_fctx = &i;
// Maximum number of calls
funcctx -> max_calls = 10;
MemoryContextSwitchTo(oldcontext);
}
// Code executed on each call (first one included)
// Retrieving values from function context
funcctx = SRF_PERCALL_SETUP();
call_cntr = funcctx->call_cntr;
max_calls = funcctx -> max_calls;
if (call_cntr < max_calls) {
int32* temp = funcctx->user_fctx;
i = *temp;
SRF_RETURN_NEXT(funcctx, i + call_cntr);
} else { // Done
SRF_RETURN_DONE(funcctx);
}
}
As you can see, this is a very silly function. I could simply use PG_GETARG_INT32(0) on each call and voila, it would work. But I really want to understand how I am supposed to keep data between calls and this simple example seems to be a good way to do it.
What I tried here was to use the user_fctx field of the function context to get my integer back on each call. The problem is that it is a pointer, and the integer it points is erased between calls. How should I tell Postgres not to erase my integer, or where should I store it ?