How to write a c-function to return multiple bytea rows
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;
}
I don't know if it's the proximate source of your problem but your
MemoryContextSwitchTo() calls are mixed up. You're reusing the same oldcontext
variable for both the switch you're doing in the main body and the switch
you're doing in the inner loop. At the very least you should use two different
oldcontext variables, currently I think you're leaving the memory context set
to the per_query_ctx.
But I think in this situation you're going to end up just doing the whole
thing in per_query_ctx anyways. The only part you can avoid that for is the
pallocing of the heap_form_tuple args and the actual heap_form_tuple call. The
tuple will be copied when you call tuplestore_puttuple (which should be done
in the same context the tuplestore was created in).
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Get trained by Bruce Momjian - ask me about EnterpriseDB's PostgreSQL training!
"Billow Gao" <billowgy@gmail.com> writes:
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 );
That part is because you didn't #include access/heapam.h, where
heap_form_tuple is declared. If you didn't get a warning about it
not being declared, you do not have warnings turned up high enough.
regards, tom lane