Unstable C Function

From: Ian Campbell <ianrc72(at)gmail(dot)com>
To: pgsql-general(at)postgresql(dot)org
Subject: Unstable C Function
Date: 2016-09-19 18:26:56
Message-ID: CA+0FpjWetUft4k5k9UMZOjkKeHwO9gULP7cDLzdy+aUAkcK0nQ@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-general

I'm running PG 9.5 on Win 10 64-bit. I'm compiling C under VS 2016.

I am forming a function that will evolve into a somewhat complex beast. To
test out my initial efforts, the function accepts an array of int4 (this
works fine and the code for processing it is not shown here). The function
then grabs a few rows from a table, pushes the values into a FIFO, then
pulls them out and renders the results. This approach is strategic to how
the function will operate when complete.

The function works fine on first call, sometimes more, then either resets
the connection or throws this on any further calls:

ERROR: cache lookup failed for type 0 SQL state: XX000

I noticed that removing references to the FIFO improves stability, but
doesn't solve it. Here is my code as lean as I can get it for question
purposes:

PG:

CREATE TABLE md_key(
id serial NOT NULL PRIMARY KEY,
pid int4 NOT NULL,
key integer NOT NULL,
vals int4[]);…

CREATE OR REPLACE FUNCTION md_key_query(int4[])
RETURNS TABLE (
id int4,
vals int4[]) AS E'\RoctPG', --abreviated for question
'md_key_query'
LANGUAGE c IMMUTABLE STRICT;…
select * from md_key_query(array[1,2,3,4]::int4[])

C:

PG_FUNCTION_INFO_V1(md_key_query);

typedef struct
{
Datum id;
Datum vals;
} MdKeyNode;

typedef struct fifoAry
{
MdKeyNode nodes[32];
struct fifoAry *next;
int32 readpos;
int32 writepos;
} FifoAry;

typedef struct
{
FifoAry *fifo;
FifoAry *tail;
FifoAry *head;
uint32 nodescount;
Datum *retvals[2];
bool *retnulls[2];
} CtxArgs;

inline void push(CtxArgs *args, Datum id, Datum vals)
{
if (args->head->writepos == 32)
args->head = args->head->next = (FifoAry*)palloc0(sizeof(FifoAry));

MdKeyNode *node = &(args->head->nodes[args->head->writepos++]);
node->id = id;
node->vals = vals;
args->nodescount++;
}

inline MdKeyNode* pop(CtxArgs *args){// if (!args->nodescount)//
return NULL;
if (args->tail->readpos == 32)
args->tail = args->tail->next;

args->nodescount--;

return &(args->tail->nodes[args->tail->readpos++]);}
// use STRICT in the caller wrapper to ensure a null isn't passed in
PGMODULEEXPORT Datum md_key_query(PG_FUNCTION_ARGS){
uint32 i;
FuncCallContext *funcctx;
HeapTuple tuple;
MdKeyNode *node;
CtxArgs *args;

if (SRF_IS_FIRSTCALL())
{
funcctx = SRF_FIRSTCALL_INIT();

MemoryContext oldcontext =
MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
ArrayType *a = PG_GETARG_ARRAYTYPE_P(0);
Datum *in_datums;
bool *in_nulls;
bool fieldNull;
SPITupleTable *tuptable = SPI_tuptable;
int32 ret;
uint32 proc;

if (get_call_result_type(fcinfo, NULL, &funcctx->tuple_desc)
!= TYPEFUNC_COMPOSITE)
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("function returning record called in context that cannot accept
type record")));

deconstruct_array(a, INT4OID, 4, true, 'i', &in_datums,
&in_nulls, &ret);

if (!ret)
PG_RETURN_NULL();

(SPI_connect();

// initialize and set the cross-call structure
funcctx->user_fctx = args = (CtxArgs*)palloc0(sizeof(CtxArgs));
args->fifo = args->tail = args->head =
(FifoAry*)palloc0(sizeof(FifoAry));
args->retvals = (Datum*)palloc(sizeof(Datum) * 2);
args->retnulls = (bool*)palloc0(sizeof(bool) * 2);

BlessTupleDesc(funcctx->tuple_desc);
// do some work here

// this is simply a test to see if this function is behaving as expected
ret = SPI_execute("select id, vals from public.md_key where
vals is not null limit 64", true, 0);

if (ret <= 0)
ereport(ERROR, (errcode(ERRCODE_SQL_ROUTINE_EXCEPTION),
errmsg("could not execute SQL")));

proc = SPI_processed;

if (proc > 0)
{
TupleDesc tupdesc = SPI_tuptable->tupdesc;
SPITupleTable *tuptable = SPI_tuptable;

for (i = 0; i < proc; i++)
{
tuple = tuptable->vals[i];
push(args, SPI_getbinval(tuple, tupdesc, 1,
&fieldNull), SPI_getbinval(tuple, tupdesc, 2, &fieldNull));
}
}

SPI_finish();
MemoryContextSwitchTo(oldcontext);
}

funcctx = SRF_PERCALL_SETUP();
args = funcctx->user_fctx;

if (args->nodescount > 0)
{
node = pop(args);
args->retvals[0] = node->id;
args->retvals[1] = node->vals;
tuple = heap_form_tuple(funcctx->tuple_desc, args->retvals,
args->retnulls);
SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
}
else
{
SRF_RETURN_DONE(funcctx);
}}

Responses

Browse pgsql-general by date

  From Date Subject
Next Message kbrannen 2016-09-19 18:46:25 pg_restore question
Previous Message Paul Jungwirth 2016-09-19 18:05:45 Re: journaling / time travel