Reputation: 1956
on PostgreSQL I implemented the following stored procedure in C:
extern "C" DLLEXPORT Datum
selectServeralRows(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx;
int call_cntr;
int max_calls;
TupleDesc tupdesc;
AttInMetadata *attinmeta;
/* stuff done only on the first call of the function */
if (SRF_IS_FIRSTCALL())
{
MemoryContext oldcontext;
/* create a function context for cross-call persistence */
funcctx = SRF_FIRSTCALL_INIT();
/* switch to memory context appropriate for multiple function calls */
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
/* total number of tuples to be returned */
funcctx->max_calls = 1;
/* Build a tuple descriptor for our result type */
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("function returning record called in context "
"that cannot accept type record")));
/*
* generate attribute metadata needed later to produce tuples from raw
* C strings
*/
attinmeta = TupleDescGetAttInMetadata(tupdesc);
funcctx->attinmeta = attinmeta;
MemoryContextSwitchTo(oldcontext);
}
/* stuff done on every call of the function */
funcctx = SRF_PERCALL_SETUP();
if (funcctx->call_cntr < funcctx->max_calls) /* do when there is more left to send */
{
Datum* val = (Datum*)palloc(2 * sizeof(Datum));
HeapTuple tuple;
Datum result;
bool nulls[2]={false,false};
char * n = new char[2];
n[0] = '1';
n[1] = '\0';
char * m = new char[2];
m[0] = '2';
n[1] = '\0';
val[0] = CStringGetTextDatum(m);
val[1] = CStringGetTextDatum(n);
/* build a tuple */
tuple = heap_form_tuple(tupdesc, val, nulls);
/* make the tuple into a datum */
result = TupleGetDatum(funcctx->slot, tuple);
/* clean up (this is not really necessary) */
SRF_RETURN_NEXT(funcctx, result);
}
else
SRF_RETURN_DONE(funcctx);
}
The code is almost the same as here: http://www.postgresql.org/docs/8.4/static/xfunc-c.html
If the procedure return just one row everything is fine and a table with one row is as desired. But if one line is changed like this
/* total number of tuples to be returned */
funcctx->max_calls = 2;
the execution of the query crashes with this message:
Program received signal EXC_BAD_ACCESS, Could not access memory. Reason: KERN_INVALID_ADDRESS at address: 0x000000000000041a 0x0000000100002d8b in heap_form_tuple ()
Stepping through the code shows that nothing until the crash is an invalid pointer so I am a bit clueless. Is there anything else which I have overseen?
EDIT: The function is called like this in psql:
select (selectServeralRows()).*
EDIT: SQL Definition of the function:
CREATE OR REPLACE FUNCTION selectServeralRows()
RETURNS TABLE(k character varying(20), j character varying(20)) AS
'/opt/local/lib/postgresql84/Debug/libSeveralRows', 'selectServeralRows'
LANGUAGE c STABLE STRICT;
Upvotes: 1
Views: 581
Reputation: 44240
It appears you have omitted a
tuple = BuildTupleFromCStrings(funcctx->attinmeta, val);
call before the TupleGetDatum(...)
call. The tuple variable is still uninitialised, except on the first call.
Also, Datum* val = (Datum*)palloc(2 * sizeof(Datum));
should probably be
char **val;
val = palloc (2 * sizeof *val);
And the n and m arrays could just as well be;
char n[2] ="1", m[2] = "2";
val[0] = n;
val[1] = m;
And you can free the memory after the call to heap_form_tuple(tupdesc, val, nulls);
pfree(val);
IMHO 'val' could also be an automatic ("stack") variable, just like n[] and m[].
Upvotes: 2