Обсуждение: Table Function API patch (was Re: another SRF question)
Neil Conway wrote:
> On Mon, Jul 08, 2002 at 03:52:24PM -0700, Joe Conway wrote:
>>Nice catch. Here's (I think) a proper fix. This assumes that if
>>(values[i] != NULL), the datum is the output of the appropriate "in"
>>function, otherwise the datum is NULL. It saves the extra pass through
>>the dvalues array too.
>>
>>I thought you might want to try it out now without waiting for the patch
>>to get into cvs. I tested it with a contrived test function and it
>>worked as desired.
>
>
> Great, seems to work fine here. Thanks for fixing that for me.
>
Here is a patch for the Table Function API. It fixes a bug found by Neil
Conway (BuildTupleFromCStrings sets NULL for pass-by-value types when
intended value is 0). It also implements some other improvements
suggested by Neil.
If there are no objections, please apply.
Thanks,
Joe
Index: src/backend/executor/execTuples.c
===================================================================
RCS file: /opt/src/cvs/pgsql/src/backend/executor/execTuples.c,v
retrieving revision 1.53
diff -c -r1.53 execTuples.c
*** src/backend/executor/execTuples.c 20 Jun 2002 20:29:27 -0000 1.53
--- src/backend/executor/execTuples.c 8 Jul 2002 22:25:14 -0000
***************
*** 759,764 ****
--- 759,765 ----
natts = tupdesc->natts;
dvalues = (Datum *) palloc(natts * sizeof(Datum));
+ nulls = (char *) palloc(natts * sizeof(char));
/* Call the "in" function for each attribute */
for (i = 0; i < natts; i++)
***************
*** 772,793 ****
dvalues[i] = FunctionCall3(&attinfuncinfo, CStringGetDatum(values[i]),
ObjectIdGetDatum(attelem),
Int32GetDatum(atttypmod));
}
else
dvalues[i] = PointerGetDatum(NULL);
}
/*
* Form a tuple
*/
- nulls = (char *) palloc(natts * sizeof(char));
- for (i = 0; i < natts; i++)
- {
- if (DatumGetPointer(dvalues[i]) != NULL)
- nulls[i] = ' ';
- else
- nulls[i] = 'n';
- }
tuple = heap_formtuple(tupdesc, dvalues, nulls);
return tuple;
--- 773,790 ----
dvalues[i] = FunctionCall3(&attinfuncinfo, CStringGetDatum(values[i]),
ObjectIdGetDatum(attelem),
Int32GetDatum(atttypmod));
+ nulls[i] = ' ';
}
else
+ {
dvalues[i] = PointerGetDatum(NULL);
+ nulls[i] = 'n';
+ }
}
/*
* Form a tuple
*/
tuple = heap_formtuple(tupdesc, dvalues, nulls);
return tuple;
Index: src/backend/utils/fmgr/funcapi.c
===================================================================
RCS file: /opt/src/cvs/pgsql/src/backend/utils/fmgr/funcapi.c,v
retrieving revision 1.1
diff -c -r1.1 funcapi.c
*** src/backend/utils/fmgr/funcapi.c 20 Jun 2002 20:37:00 -0000 1.1
--- src/backend/utils/fmgr/funcapi.c 9 Jul 2002 18:21:15 -0000
***************
*** 52,58 ****
retval->call_cntr = 0;
retval->max_calls = 0;
retval->slot = NULL;
! retval->fctx = NULL;
retval->attinmeta = NULL;
retval->fmctx = fcinfo->flinfo->fn_mcxt;
--- 52,58 ----
retval->call_cntr = 0;
retval->max_calls = 0;
retval->slot = NULL;
! retval->user_fctx = NULL;
retval->attinmeta = NULL;
retval->fmctx = fcinfo->flinfo->fn_mcxt;
***************
*** 71,76 ****
--- 71,93 ----
/* never reached, but keep compiler happy */
retval = NULL;
}
+
+ return retval;
+ }
+
+ /*
+ * per_MultiFuncCall
+ *
+ * Do Multi-function per-call setup
+ */
+ FuncCallContext *
+ per_MultiFuncCall(PG_FUNCTION_ARGS)
+ {
+ FuncCallContext *retval = (FuncCallContext *) fcinfo->flinfo->fn_extra;
+
+ /* make sure we start with a fresh slot */
+ if(retval->slot != NULL)
+ ExecClearTuple(retval->slot);
return retval;
}
Index: src/include/funcapi.h
===================================================================
RCS file: /opt/src/cvs/pgsql/src/include/funcapi.h,v
retrieving revision 1.2
diff -c -r1.2 funcapi.h
*** src/include/funcapi.h 22 Jun 2002 04:08:07 -0000 1.2
--- src/include/funcapi.h 9 Jul 2002 18:17:27 -0000
***************
*** 65,86 ****
*/
typedef struct
{
! /* Number of times we've been called before */
uint32 call_cntr;
! /* Maximum number of calls */
uint32 max_calls;
! /* pointer to result slot */
TupleTableSlot *slot;
! /* pointer to misc context info */
! void *fctx;
!
! /* pointer to struct containing arrays of attribute type input metainfo */
AttInMetadata *attinmeta;
! /* memory context used to initialize structure */
MemoryContext fmctx;
} FuncCallContext;
--- 65,121 ----
*/
typedef struct
{
! /*
! * Number of times we've been called before.
! *
! * call_cntr is initialized to 0 for you by SRF_FIRSTCALL_INIT(), and
! * incremented for you every time SRF_RETURN_NEXT() is called.
! */
uint32 call_cntr;
! /*
! * OPTIONAL maximum number of calls
! *
! * max_calls is here for convenience ONLY and setting it is OPTIONAL.
! * If not set, you must provide alternative means to know when the
! * function is done.
! */
uint32 max_calls;
! /*
! * OPTIONAL pointer to result slot
! *
! * slot is for use when returning tuples (i.e. composite data types)
! * and is not needed when returning base (i.e. scalar) data types.
! */
TupleTableSlot *slot;
! /*
! * OPTIONAL pointer to misc user provided context info
! *
! * user_fctx is for use as a pointer to your own struct to retain
! * arbitrary context information between calls for your function.
! */
! void *user_fctx;
!
! /*
! * OPTIONAL pointer to struct containing arrays of attribute type input
! * metainfo
! *
! * attinmeta is for use when returning tuples (i.e. composite data types)
! * and is not needed when returning base (i.e. scalar) data types. It
! * is ONLY needed if you intend to use BuildTupleFromCStrings() to create
! * the return tuple.
! */
AttInMetadata *attinmeta;
! /*
! * memory context used to initialize structure
! *
! * fmctx is set by SRF_FIRSTCALL_INIT() for you, and used by
! * SRF_RETURN_DONE() for cleanup. It is primarily for internal use
! * by the API.
! */
MemoryContext fmctx;
} FuncCallContext;
***************
*** 137,143 ****
* Datum result;
* <user defined declarations>
*
! * if(SRF_IS_FIRSTPASS())
* {
* <user defined code>
* funcctx = SRF_FIRSTCALL_INIT();
--- 172,178 ----
* Datum result;
* <user defined declarations>
*
! * if(SRF_IS_FIRSTCALL())
* {
* <user defined code>
* funcctx = SRF_FIRSTCALL_INIT();
***************
*** 148,154 ****
* <user defined code>
* }
* <user defined code>
! * funcctx = SRF_PERCALL_SETUP(funcctx);
* <user defined code>
*
* if (funcctx->call_cntr < funcctx->max_calls)
--- 183,189 ----
* <user defined code>
* }
* <user defined code>
! * funcctx = SRF_PERCALL_SETUP();
* <user defined code>
*
* if (funcctx->call_cntr < funcctx->max_calls)
***************
*** 167,180 ****
/* from funcapi.c */
extern FuncCallContext *init_MultiFuncCall(PG_FUNCTION_ARGS);
extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
! #define SRF_IS_FIRSTPASS() (fcinfo->flinfo->fn_extra == NULL)
#define SRF_FIRSTCALL_INIT() init_MultiFuncCall(fcinfo)
! #define SRF_PERCALL_SETUP(_funcctx) \
! fcinfo->flinfo->fn_extra; \
! if(_funcctx->slot != NULL) \
! ExecClearTuple(_funcctx->slot)
#define SRF_RETURN_NEXT(_funcctx, _result) \
do { \
ReturnSetInfo *rsi; \
--- 202,213 ----
/* from funcapi.c */
extern FuncCallContext *init_MultiFuncCall(PG_FUNCTION_ARGS);
+ extern FuncCallContext *per_MultiFuncCall(PG_FUNCTION_ARGS);
extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
! #define SRF_IS_FIRSTCALL() (fcinfo->flinfo->fn_extra == NULL)
#define SRF_FIRSTCALL_INIT() init_MultiFuncCall(fcinfo)
! #define SRF_PERCALL_SETUP() per_MultiFuncCall(fcinfo)
#define SRF_RETURN_NEXT(_funcctx, _result) \
do { \
ReturnSetInfo *rsi; \
Your patch has been added to the PostgreSQL unapplied patches list at:
http://candle.pha.pa.us/cgi-bin/pgpatches
I will try to apply it within the next 48 hours.
---------------------------------------------------------------------------
Joe Conway wrote:
> Neil Conway wrote:
> > On Mon, Jul 08, 2002 at 03:52:24PM -0700, Joe Conway wrote:
> >>Nice catch. Here's (I think) a proper fix. This assumes that if
> >>(values[i] != NULL), the datum is the output of the appropriate "in"
> >>function, otherwise the datum is NULL. It saves the extra pass through
> >>the dvalues array too.
> >>
> >>I thought you might want to try it out now without waiting for the patch
> >>to get into cvs. I tested it with a contrived test function and it
> >>worked as desired.
> >
> >
> > Great, seems to work fine here. Thanks for fixing that for me.
> >
>
> Here is a patch for the Table Function API. It fixes a bug found by Neil
> Conway (BuildTupleFromCStrings sets NULL for pass-by-value types when
> intended value is 0). It also implements some other improvements
> suggested by Neil.
>
> If there are no objections, please apply.
>
> Thanks,
>
> Joe
>
>
> Index: src/backend/executor/execTuples.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql/src/backend/executor/execTuples.c,v
> retrieving revision 1.53
> diff -c -r1.53 execTuples.c
> *** src/backend/executor/execTuples.c 20 Jun 2002 20:29:27 -0000 1.53
> --- src/backend/executor/execTuples.c 8 Jul 2002 22:25:14 -0000
> ***************
> *** 759,764 ****
> --- 759,765 ----
> natts = tupdesc->natts;
>
> dvalues = (Datum *) palloc(natts * sizeof(Datum));
> + nulls = (char *) palloc(natts * sizeof(char));
>
> /* Call the "in" function for each attribute */
> for (i = 0; i < natts; i++)
> ***************
> *** 772,793 ****
> dvalues[i] = FunctionCall3(&attinfuncinfo, CStringGetDatum(values[i]),
> ObjectIdGetDatum(attelem),
> Int32GetDatum(atttypmod));
> }
> else
> dvalues[i] = PointerGetDatum(NULL);
> }
>
> /*
> * Form a tuple
> */
> - nulls = (char *) palloc(natts * sizeof(char));
> - for (i = 0; i < natts; i++)
> - {
> - if (DatumGetPointer(dvalues[i]) != NULL)
> - nulls[i] = ' ';
> - else
> - nulls[i] = 'n';
> - }
> tuple = heap_formtuple(tupdesc, dvalues, nulls);
>
> return tuple;
> --- 773,790 ----
> dvalues[i] = FunctionCall3(&attinfuncinfo, CStringGetDatum(values[i]),
> ObjectIdGetDatum(attelem),
> Int32GetDatum(atttypmod));
> + nulls[i] = ' ';
> }
> else
> + {
> dvalues[i] = PointerGetDatum(NULL);
> + nulls[i] = 'n';
> + }
> }
>
> /*
> * Form a tuple
> */
> tuple = heap_formtuple(tupdesc, dvalues, nulls);
>
> return tuple;
> Index: src/backend/utils/fmgr/funcapi.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql/src/backend/utils/fmgr/funcapi.c,v
> retrieving revision 1.1
> diff -c -r1.1 funcapi.c
> *** src/backend/utils/fmgr/funcapi.c 20 Jun 2002 20:37:00 -0000 1.1
> --- src/backend/utils/fmgr/funcapi.c 9 Jul 2002 18:21:15 -0000
> ***************
> *** 52,58 ****
> retval->call_cntr = 0;
> retval->max_calls = 0;
> retval->slot = NULL;
> ! retval->fctx = NULL;
> retval->attinmeta = NULL;
> retval->fmctx = fcinfo->flinfo->fn_mcxt;
>
> --- 52,58 ----
> retval->call_cntr = 0;
> retval->max_calls = 0;
> retval->slot = NULL;
> ! retval->user_fctx = NULL;
> retval->attinmeta = NULL;
> retval->fmctx = fcinfo->flinfo->fn_mcxt;
>
> ***************
> *** 71,76 ****
> --- 71,93 ----
> /* never reached, but keep compiler happy */
> retval = NULL;
> }
> +
> + return retval;
> + }
> +
> + /*
> + * per_MultiFuncCall
> + *
> + * Do Multi-function per-call setup
> + */
> + FuncCallContext *
> + per_MultiFuncCall(PG_FUNCTION_ARGS)
> + {
> + FuncCallContext *retval = (FuncCallContext *) fcinfo->flinfo->fn_extra;
> +
> + /* make sure we start with a fresh slot */
> + if(retval->slot != NULL)
> + ExecClearTuple(retval->slot);
>
> return retval;
> }
> Index: src/include/funcapi.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql/src/include/funcapi.h,v
> retrieving revision 1.2
> diff -c -r1.2 funcapi.h
> *** src/include/funcapi.h 22 Jun 2002 04:08:07 -0000 1.2
> --- src/include/funcapi.h 9 Jul 2002 18:17:27 -0000
> ***************
> *** 65,86 ****
> */
> typedef struct
> {
> ! /* Number of times we've been called before */
> uint32 call_cntr;
>
> ! /* Maximum number of calls */
> uint32 max_calls;
>
> ! /* pointer to result slot */
> TupleTableSlot *slot;
>
> ! /* pointer to misc context info */
> ! void *fctx;
> !
> ! /* pointer to struct containing arrays of attribute type input metainfo */
> AttInMetadata *attinmeta;
>
> ! /* memory context used to initialize structure */
> MemoryContext fmctx;
>
> } FuncCallContext;
> --- 65,121 ----
> */
> typedef struct
> {
> ! /*
> ! * Number of times we've been called before.
> ! *
> ! * call_cntr is initialized to 0 for you by SRF_FIRSTCALL_INIT(), and
> ! * incremented for you every time SRF_RETURN_NEXT() is called.
> ! */
> uint32 call_cntr;
>
> ! /*
> ! * OPTIONAL maximum number of calls
> ! *
> ! * max_calls is here for convenience ONLY and setting it is OPTIONAL.
> ! * If not set, you must provide alternative means to know when the
> ! * function is done.
> ! */
> uint32 max_calls;
>
> ! /*
> ! * OPTIONAL pointer to result slot
> ! *
> ! * slot is for use when returning tuples (i.e. composite data types)
> ! * and is not needed when returning base (i.e. scalar) data types.
> ! */
> TupleTableSlot *slot;
>
> ! /*
> ! * OPTIONAL pointer to misc user provided context info
> ! *
> ! * user_fctx is for use as a pointer to your own struct to retain
> ! * arbitrary context information between calls for your function.
> ! */
> ! void *user_fctx;
> !
> ! /*
> ! * OPTIONAL pointer to struct containing arrays of attribute type input
> ! * metainfo
> ! *
> ! * attinmeta is for use when returning tuples (i.e. composite data types)
> ! * and is not needed when returning base (i.e. scalar) data types. It
> ! * is ONLY needed if you intend to use BuildTupleFromCStrings() to create
> ! * the return tuple.
> ! */
> AttInMetadata *attinmeta;
>
> ! /*
> ! * memory context used to initialize structure
> ! *
> ! * fmctx is set by SRF_FIRSTCALL_INIT() for you, and used by
> ! * SRF_RETURN_DONE() for cleanup. It is primarily for internal use
> ! * by the API.
> ! */
> MemoryContext fmctx;
>
> } FuncCallContext;
> ***************
> *** 137,143 ****
> * Datum result;
> * <user defined declarations>
> *
> ! * if(SRF_IS_FIRSTPASS())
> * {
> * <user defined code>
> * funcctx = SRF_FIRSTCALL_INIT();
> --- 172,178 ----
> * Datum result;
> * <user defined declarations>
> *
> ! * if(SRF_IS_FIRSTCALL())
> * {
> * <user defined code>
> * funcctx = SRF_FIRSTCALL_INIT();
> ***************
> *** 148,154 ****
> * <user defined code>
> * }
> * <user defined code>
> ! * funcctx = SRF_PERCALL_SETUP(funcctx);
> * <user defined code>
> *
> * if (funcctx->call_cntr < funcctx->max_calls)
> --- 183,189 ----
> * <user defined code>
> * }
> * <user defined code>
> ! * funcctx = SRF_PERCALL_SETUP();
> * <user defined code>
> *
> * if (funcctx->call_cntr < funcctx->max_calls)
> ***************
> *** 167,180 ****
>
> /* from funcapi.c */
> extern FuncCallContext *init_MultiFuncCall(PG_FUNCTION_ARGS);
> extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
>
> ! #define SRF_IS_FIRSTPASS() (fcinfo->flinfo->fn_extra == NULL)
> #define SRF_FIRSTCALL_INIT() init_MultiFuncCall(fcinfo)
> ! #define SRF_PERCALL_SETUP(_funcctx) \
> ! fcinfo->flinfo->fn_extra; \
> ! if(_funcctx->slot != NULL) \
> ! ExecClearTuple(_funcctx->slot)
> #define SRF_RETURN_NEXT(_funcctx, _result) \
> do { \
> ReturnSetInfo *rsi; \
> --- 202,213 ----
>
> /* from funcapi.c */
> extern FuncCallContext *init_MultiFuncCall(PG_FUNCTION_ARGS);
> + extern FuncCallContext *per_MultiFuncCall(PG_FUNCTION_ARGS);
> extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
>
> ! #define SRF_IS_FIRSTCALL() (fcinfo->flinfo->fn_extra == NULL)
> #define SRF_FIRSTCALL_INIT() init_MultiFuncCall(fcinfo)
> ! #define SRF_PERCALL_SETUP() per_MultiFuncCall(fcinfo)
> #define SRF_RETURN_NEXT(_funcctx, _result) \
> do { \
> ReturnSetInfo *rsi; \
>
> ---------------------------(end of broadcast)---------------------------
> TIP 1: subscribe and unsubscribe commands go to majordomo@postgresql.org
--
Bruce Momjian | http://candle.pha.pa.us
pgman@candle.pha.pa.us | (610) 853-3000
+ If your life is a hard drive, | 830 Blythe Avenue
+ Christ can be your backup. | Drexel Hill, Pennsylvania 19026
Patch applied. Thanks.
---------------------------------------------------------------------------
Joe Conway wrote:
> Neil Conway wrote:
> > On Mon, Jul 08, 2002 at 03:52:24PM -0700, Joe Conway wrote:
> >>Nice catch. Here's (I think) a proper fix. This assumes that if
> >>(values[i] != NULL), the datum is the output of the appropriate "in"
> >>function, otherwise the datum is NULL. It saves the extra pass through
> >>the dvalues array too.
> >>
> >>I thought you might want to try it out now without waiting for the patch
> >>to get into cvs. I tested it with a contrived test function and it
> >>worked as desired.
> >
> >
> > Great, seems to work fine here. Thanks for fixing that for me.
> >
>
> Here is a patch for the Table Function API. It fixes a bug found by Neil
> Conway (BuildTupleFromCStrings sets NULL for pass-by-value types when
> intended value is 0). It also implements some other improvements
> suggested by Neil.
>
> If there are no objections, please apply.
>
> Thanks,
>
> Joe
>
>
> Index: src/backend/executor/execTuples.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql/src/backend/executor/execTuples.c,v
> retrieving revision 1.53
> diff -c -r1.53 execTuples.c
> *** src/backend/executor/execTuples.c 20 Jun 2002 20:29:27 -0000 1.53
> --- src/backend/executor/execTuples.c 8 Jul 2002 22:25:14 -0000
> ***************
> *** 759,764 ****
> --- 759,765 ----
> natts = tupdesc->natts;
>
> dvalues = (Datum *) palloc(natts * sizeof(Datum));
> + nulls = (char *) palloc(natts * sizeof(char));
>
> /* Call the "in" function for each attribute */
> for (i = 0; i < natts; i++)
> ***************
> *** 772,793 ****
> dvalues[i] = FunctionCall3(&attinfuncinfo, CStringGetDatum(values[i]),
> ObjectIdGetDatum(attelem),
> Int32GetDatum(atttypmod));
> }
> else
> dvalues[i] = PointerGetDatum(NULL);
> }
>
> /*
> * Form a tuple
> */
> - nulls = (char *) palloc(natts * sizeof(char));
> - for (i = 0; i < natts; i++)
> - {
> - if (DatumGetPointer(dvalues[i]) != NULL)
> - nulls[i] = ' ';
> - else
> - nulls[i] = 'n';
> - }
> tuple = heap_formtuple(tupdesc, dvalues, nulls);
>
> return tuple;
> --- 773,790 ----
> dvalues[i] = FunctionCall3(&attinfuncinfo, CStringGetDatum(values[i]),
> ObjectIdGetDatum(attelem),
> Int32GetDatum(atttypmod));
> + nulls[i] = ' ';
> }
> else
> + {
> dvalues[i] = PointerGetDatum(NULL);
> + nulls[i] = 'n';
> + }
> }
>
> /*
> * Form a tuple
> */
> tuple = heap_formtuple(tupdesc, dvalues, nulls);
>
> return tuple;
> Index: src/backend/utils/fmgr/funcapi.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql/src/backend/utils/fmgr/funcapi.c,v
> retrieving revision 1.1
> diff -c -r1.1 funcapi.c
> *** src/backend/utils/fmgr/funcapi.c 20 Jun 2002 20:37:00 -0000 1.1
> --- src/backend/utils/fmgr/funcapi.c 9 Jul 2002 18:21:15 -0000
> ***************
> *** 52,58 ****
> retval->call_cntr = 0;
> retval->max_calls = 0;
> retval->slot = NULL;
> ! retval->fctx = NULL;
> retval->attinmeta = NULL;
> retval->fmctx = fcinfo->flinfo->fn_mcxt;
>
> --- 52,58 ----
> retval->call_cntr = 0;
> retval->max_calls = 0;
> retval->slot = NULL;
> ! retval->user_fctx = NULL;
> retval->attinmeta = NULL;
> retval->fmctx = fcinfo->flinfo->fn_mcxt;
>
> ***************
> *** 71,76 ****
> --- 71,93 ----
> /* never reached, but keep compiler happy */
> retval = NULL;
> }
> +
> + return retval;
> + }
> +
> + /*
> + * per_MultiFuncCall
> + *
> + * Do Multi-function per-call setup
> + */
> + FuncCallContext *
> + per_MultiFuncCall(PG_FUNCTION_ARGS)
> + {
> + FuncCallContext *retval = (FuncCallContext *) fcinfo->flinfo->fn_extra;
> +
> + /* make sure we start with a fresh slot */
> + if(retval->slot != NULL)
> + ExecClearTuple(retval->slot);
>
> return retval;
> }
> Index: src/include/funcapi.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql/src/include/funcapi.h,v
> retrieving revision 1.2
> diff -c -r1.2 funcapi.h
> *** src/include/funcapi.h 22 Jun 2002 04:08:07 -0000 1.2
> --- src/include/funcapi.h 9 Jul 2002 18:17:27 -0000
> ***************
> *** 65,86 ****
> */
> typedef struct
> {
> ! /* Number of times we've been called before */
> uint32 call_cntr;
>
> ! /* Maximum number of calls */
> uint32 max_calls;
>
> ! /* pointer to result slot */
> TupleTableSlot *slot;
>
> ! /* pointer to misc context info */
> ! void *fctx;
> !
> ! /* pointer to struct containing arrays of attribute type input metainfo */
> AttInMetadata *attinmeta;
>
> ! /* memory context used to initialize structure */
> MemoryContext fmctx;
>
> } FuncCallContext;
> --- 65,121 ----
> */
> typedef struct
> {
> ! /*
> ! * Number of times we've been called before.
> ! *
> ! * call_cntr is initialized to 0 for you by SRF_FIRSTCALL_INIT(), and
> ! * incremented for you every time SRF_RETURN_NEXT() is called.
> ! */
> uint32 call_cntr;
>
> ! /*
> ! * OPTIONAL maximum number of calls
> ! *
> ! * max_calls is here for convenience ONLY and setting it is OPTIONAL.
> ! * If not set, you must provide alternative means to know when the
> ! * function is done.
> ! */
> uint32 max_calls;
>
> ! /*
> ! * OPTIONAL pointer to result slot
> ! *
> ! * slot is for use when returning tuples (i.e. composite data types)
> ! * and is not needed when returning base (i.e. scalar) data types.
> ! */
> TupleTableSlot *slot;
>
> ! /*
> ! * OPTIONAL pointer to misc user provided context info
> ! *
> ! * user_fctx is for use as a pointer to your own struct to retain
> ! * arbitrary context information between calls for your function.
> ! */
> ! void *user_fctx;
> !
> ! /*
> ! * OPTIONAL pointer to struct containing arrays of attribute type input
> ! * metainfo
> ! *
> ! * attinmeta is for use when returning tuples (i.e. composite data types)
> ! * and is not needed when returning base (i.e. scalar) data types. It
> ! * is ONLY needed if you intend to use BuildTupleFromCStrings() to create
> ! * the return tuple.
> ! */
> AttInMetadata *attinmeta;
>
> ! /*
> ! * memory context used to initialize structure
> ! *
> ! * fmctx is set by SRF_FIRSTCALL_INIT() for you, and used by
> ! * SRF_RETURN_DONE() for cleanup. It is primarily for internal use
> ! * by the API.
> ! */
> MemoryContext fmctx;
>
> } FuncCallContext;
> ***************
> *** 137,143 ****
> * Datum result;
> * <user defined declarations>
> *
> ! * if(SRF_IS_FIRSTPASS())
> * {
> * <user defined code>
> * funcctx = SRF_FIRSTCALL_INIT();
> --- 172,178 ----
> * Datum result;
> * <user defined declarations>
> *
> ! * if(SRF_IS_FIRSTCALL())
> * {
> * <user defined code>
> * funcctx = SRF_FIRSTCALL_INIT();
> ***************
> *** 148,154 ****
> * <user defined code>
> * }
> * <user defined code>
> ! * funcctx = SRF_PERCALL_SETUP(funcctx);
> * <user defined code>
> *
> * if (funcctx->call_cntr < funcctx->max_calls)
> --- 183,189 ----
> * <user defined code>
> * }
> * <user defined code>
> ! * funcctx = SRF_PERCALL_SETUP();
> * <user defined code>
> *
> * if (funcctx->call_cntr < funcctx->max_calls)
> ***************
> *** 167,180 ****
>
> /* from funcapi.c */
> extern FuncCallContext *init_MultiFuncCall(PG_FUNCTION_ARGS);
> extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
>
> ! #define SRF_IS_FIRSTPASS() (fcinfo->flinfo->fn_extra == NULL)
> #define SRF_FIRSTCALL_INIT() init_MultiFuncCall(fcinfo)
> ! #define SRF_PERCALL_SETUP(_funcctx) \
> ! fcinfo->flinfo->fn_extra; \
> ! if(_funcctx->slot != NULL) \
> ! ExecClearTuple(_funcctx->slot)
> #define SRF_RETURN_NEXT(_funcctx, _result) \
> do { \
> ReturnSetInfo *rsi; \
> --- 202,213 ----
>
> /* from funcapi.c */
> extern FuncCallContext *init_MultiFuncCall(PG_FUNCTION_ARGS);
> + extern FuncCallContext *per_MultiFuncCall(PG_FUNCTION_ARGS);
> extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
>
> ! #define SRF_IS_FIRSTCALL() (fcinfo->flinfo->fn_extra == NULL)
> #define SRF_FIRSTCALL_INIT() init_MultiFuncCall(fcinfo)
> ! #define SRF_PERCALL_SETUP() per_MultiFuncCall(fcinfo)
> #define SRF_RETURN_NEXT(_funcctx, _result) \
> do { \
> ReturnSetInfo *rsi; \
>
> ---------------------------(end of broadcast)---------------------------
> TIP 1: subscribe and unsubscribe commands go to majordomo@postgresql.org
--
Bruce Momjian | http://candle.pha.pa.us
pgman@candle.pha.pa.us | (610) 853-3000
+ If your life is a hard drive, | 830 Blythe Avenue
+ Christ can be your backup. | Drexel Hill, Pennsylvania 19026