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
Attachments:
tablefunc-fix.2002.07.09.1.patchtext/plain; name=tablefunc-fix.2002.07.09.1.patchDownload+87-72
Import Notes
Reference msg id not found: 20020708144753.GA1767@klamath.dyndns.orgReference msg id not found: 3D29C7DA.9090901@joeconway.comReference msg id not found: 20020708194002.GA3617@klamath.dyndns.orgReference msg id not found: 3D2A17A8.2060901@joeconway.comReference msg id not found: 20020709143827.GA10278@klamath.dyndns.org
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