BUG #5883: Error when mixing SPI_returntuple with returning regular HeapTuple - Mailing list pgsql-bugs

From
Subject BUG #5883: Error when mixing SPI_returntuple with returning regular HeapTuple
Date
Msg-id 201102141140.p1EBeeG8060816@wwwmaster.postgresql.org
Whole thread Raw
Responses Re: BUG #5883: Error when mixing SPI_returntuple with returning regular HeapTuple  (Tom Lane <tgl@sss.pgh.pa.us>)
List pgsql-bugs
The following bug has been logged online:

Bug reference:      5883
Logged by:
Email address:      vegard.bones@met.no
PostgreSQL version: 8.4.7
Operating system:   ubuntu lucid
Description:        Error when mixing SPI_returntuple with returning regular
HeapTuple
Details:

When creating a server-side C function, things go wrong when I (in the same
function) return some results via SPI_returntuple, and other results by
manually creating HeapTuples. This applies even if the source for both
returns are the same data in the same table.

I get an error message saying "rows returned by function are not all of the
same row type". In the attached example I would have expected to see the
same row twice.

Everything works as I expect if I try to do the same and stick to using only
one of the alternatives.


Example code:

SQL:

CREATE TABLE test (a int, b int);
INSERT INTO test VALUES (1, 2);
CREATE FUNCTION run_test() RETURNS SETOF test AS 'SOMEWHERE/something.so',
'run_test' LANGUAGE C VOLATILE;

SELECT * FROM run_test();


C:

#include <postgres.h>
#include <fmgr.h>
#include <funcapi.h>
#include <executor/spi.h>

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

static const char * query = "SELECT a, b FROM test";

static void get_data_alternative_a(Datum * data_out, bool * isnull)
{
    SPI_execute(query, true, 1);
    data_out[0] = SPI_getbinval(* SPI_tuptable->vals, SPI_tuptable->tupdesc, 1,
& isnull[0]);
    data_out[1] = SPI_getbinval(* SPI_tuptable->vals, SPI_tuptable->tupdesc, 2,
& isnull[1]);
}

static Datum get_data_alternative_b()
{
    SPI_execute(query, true, 1);
    HeapTupleHeader ret = SPI_returntuple(* SPI_tuptable->vals,
SPI_tuptable->tupdesc);
    return PointerGetDatum(ret);
}

PG_FUNCTION_INFO_V1(run_test);
Datum run_test(PG_FUNCTION_ARGS)
{
    FuncCallContext * funcctx;
    int * return_count = NULL;

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

        MemoryContext oldcontext =
MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
        TupleDesc tupdesc;
        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")));

        funcctx->tuple_desc = BlessTupleDesc(tupdesc);

        return_count = (int *) palloc(sizeof(int));
        MemoryContextSwitchTo(oldcontext);

        * return_count = 0;
        funcctx->user_fctx = (void*) return_count;
    }
    funcctx = SRF_PERCALL_SETUP();
    return_count = (int *) funcctx->user_fctx;


    Datum ret[2];
    bool isnull[2];
    switch ( (* return_count) ++ )
    {
    case 0:
//        SRF_RETURN_NEXT(funcctx, get_data_alternative_b());
        get_data_alternative_a(ret, isnull);
        break;
    case 1:
        SRF_RETURN_NEXT(funcctx, get_data_alternative_b());
//        get_data_alternative_a(ret, isnull);
        break;
    default:
        SPI_finish();
        SRF_RETURN_DONE(funcctx);
    }
    HeapTuple heap_tuple = heap_form_tuple(funcctx->tuple_desc, ret, isnull);
    Datum packed_ret = HeapTupleGetDatum(heap_tuple);
    SRF_RETURN_NEXT(funcctx, packed_ret);
}

pgsql-bugs by date:

Previous
From: Jaime Casanova
Date:
Subject: Re:
Next
From: dba
Date:
Subject: Array issue....