Unstable C Function - Mailing list pgsql-general

From Ian Campbell
Subject Unstable C Function
Date
Msg-id CA+0FpjWetUft4k5k9UMZOjkKeHwO9gULP7cDLzdy+aUAkcK0nQ@mail.gmail.com
Whole thread Raw
Responses Re: Unstable C Function  (Tom Lane <tgl@sss.pgh.pa.us>)
List 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);   }
}

pgsql-general by date:

Previous
From: MEERA
Date:
Subject: Re: PgSQL versions supported on ubuntu 16 and debian 8
Next
From: PHANIKUMAR G
Date:
Subject: postgres failed to start from services manager on windows 2008 r2