I can return multiple strings w/o problem. But if I tried to return multiple bytea rows. It only return 10 rows with empty data. Please see the code below.
Also, when I compile it, I had warning: test.c:121: warning: assignment makes pointer from integer without a cast The line is: tuple = heap_form_tuple( tupdesc, &dtvalues, &isNull ); Strange.. compiled in a linux box. If I use: tuple = BuildTupleFromCStrings(attinmeta, values); it can work but it's string and I want to use bytea. Also, do I need to free char** values or let postgresql do the job? Thanks Billow ============================================================================================ /************************************************************ -- select * from test(1,2,'asdfdsaf') as (id bytea); CREATE OR REPLACE FUNCTION test(int,int,text) RETURNS setof record AS 'gr_indexsearch.so', 'test' LANGUAGE 'C' IMMUTABLE CALLED ON NULL INPUT; *************************************************************/ // PostgreSQL includes #include "postgres.h" #include "fmgr.h" // Tuple building functions and macros #include "funcapi.h" #include "utils/builtins.h" #include <string.h> #define _textout(str) DatumGetPointer(DirectFunctionCall1(textout, PointerGetDatum(str))) #ifndef SET_VARSIZE #define SET_VARSIZE(v,l) (VARATT_SIZEP(v) = (l)) #endif /* SortMem got renamed in PostgreSQL 8.0 */ #ifndef SortMem #define SortMem 16 * 1024 #endif #ifdef PG_MODULE_MAGIC PG_MODULE_MAGIC; #endif // forward declaration to keep compiler happy Datum c_complex_add( PG_FUNCTION_ARGS ); PG_FUNCTION_INFO_V1( test ); Datum test( PG_FUNCTION_ARGS ) { // things we need to deal with constructing our composite type TupleDesc tupdesc; HeapTuple tuple; Tuplestorestate *tupstore = NULL; ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; MemoryContext per_query_ctx; MemoryContext oldcontext; // Get arguments. If we declare our function as STRICT, then // this check is superfluous. if( PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2)) { PG_RETURN_NULL(); } // Get arguments: TimeStart and TimeEnd int32 TimeStart = PG_GETARG_INT32(0); int32 TimeEnd = PG_GETARG_INT32(1); // Get Search query char *query = _textout(PG_GETARG_TEXT_P(2)); /* check to see if caller supports us returning a tuplestore */ if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("materialize mode required, but it is not " \ "allowed in this context"))); /* let the caller know we're sending back a tuplestore */ rsinfo->returnMode = SFRM_Materialize; per_query_ctx = fcinfo->flinfo->fn_mcxt; oldcontext = MemoryContextSwitchTo(per_query_ctx); /* get the requested return tuple description */ tupdesc = rsinfo->expectedDesc; /* OK, use it */ AttInMetadata *attinmeta = TupleDescGetAttInMetadata(tupdesc); /* initialize our tuplestore */ tupstore = tuplestore_begin_heap(true, false, SortMem); char strtest[] = "This is a test!"; int strleng = strlen(strtest); int rows = 10; //char** values = (char **) palloc(rows * sizeof(char *)); bytea** values = (bytea **) palloc(rows * sizeof(bytea *)); Datum dtvalues; bool isNull; int i; for(i=0; i<rows; i++) { //values[i] = palloc(strleng * sizeof(char)); //strncpy(values[i], strtest, strleng); /* construct the tuple */ //tuple = BuildTupleFromCStrings(attinmeta, values); /* now store it */ //tuplestore_puttuple(tupstore, tuple); values[i] = (bytea *) palloc( strleng + VARHDRSZ ); SET_VARSIZE(values[i], strleng + VARHDRSZ); memcpy( VARDATA(values[i]), strtest, strleng ); dtvalues = PointerGetDatum(values[i]); tuple = heap_form_tuple( tupdesc, &dtvalues, &isNull ); /* now store it */ oldcontext = MemoryContextSwitchTo(per_query_ctx); tuplestore_puttuple(tupstore, tuple); MemoryContextSwitchTo(oldcontext); heap_freetuple(tuple); } tuplestore_donestoring(tupstore); /* now go build it */ rsinfo->setResult = tupstore; /* * SFRM_Materialize mode expects us to return a NULL Datum. The actual * tuples are in our tuplestore and passed back through rsinfo->setResult. * rsinfo->setDesc is set to the tuple description that we actually used * to build our tuples with, so the caller can verify we did what it was * expecting. */ rsinfo->setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); return (Datum) 0; }