1

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;
14
  • 1
    Note: there is no new operator in C. Commented Apr 29, 2012 at 13:42
  • I see a bug in line: Datum* val = (Datum*)palloc(2 + sizeof(Datum)); probably you would to use * instead + Commented Apr 29, 2012 at 14:01
  • @wildplasser, actually this is compiled with gcc. Nevertheless I changed it to use malloc and it had the same effect. Commented Apr 29, 2012 at 14:11
  • @PavelStehule Thanks. I changed that one too, but it didn't help at the end. Commented Apr 29, 2012 at 14:11
  • Why don't you use palloc insted of malloc? BTW: the palloc(2 + sizeof(Datum)); looks suspect, too. Commented Apr 29, 2012 at 14:17

1 Answer 1

2

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[].

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.