Re: [Fwd: C&SRF API patch (was Re: [HACKERS] revised sample

Started by Bruce Momjianabout 24 years ago1 messagespatches
Jump to latest
#1Bruce Momjian
bruce@momjian.us

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:

Tom Lane wrote:

Joe Conway <mail@joeconway.com> writes:

Is the approach in my patch still too ugly to allow a builtin SRF (set
the function return type to 0 in pg_proc.h, create a view and fix the
pg_proc entry during initdb)?

Too ugly for my taste anyway ...

OK.

Here is a patch for Composite and Set returning function support. I made
two small changes to the API since last patch, which hopefully completes
the decoupling of composite function support from SRF specific support.
If there are no (further ;-)) objections, please apply. I'll send
another post with a patch for contrib/showguc.

Thanks,

Joe

Index: src/backend/access/common/tupdesc.c
===================================================================
RCS file: /opt/src/cvs/pgsql/src/backend/access/common/tupdesc.c,v
retrieving revision 1.78
diff -c -r1.78 tupdesc.c
*** src/backend/access/common/tupdesc.c	29 Mar 2002 19:05:59 -0000	1.78
--- src/backend/access/common/tupdesc.c	9 Jun 2002 21:00:31 -0000
***************
*** 19,24 ****
--- 19,27 ----

#include "postgres.h"

+ #include "funcapi.h"
+ #include "access/heapam.h"
+ #include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "nodes/parsenodes.h"
#include "parser/parse_type.h"
***************
*** 548,551 ****
--- 551,660 ----
desc->constr = NULL;
}
return desc;
+ }
+ 
+ 
+ /*
+  * RelationNameGetTupleDesc
+  *
+  * Given a (possibly qualified) relation name, build a TupleDesc.
+  */
+ TupleDesc
+ RelationNameGetTupleDesc(char *relname)
+ {
+ 	RangeVar   *relvar;
+ 	Relation	rel;
+ 	TupleDesc	tupdesc;
+ 	List	   *relname_list;
+ 
+ 	/* Open relation and get the tuple description */
+ 	relname_list = stringToQualifiedNameList(relname, "RelationNameGetTupleDesc");
+ 	relvar = makeRangeVarFromNameList(relname_list);
+ 	rel = heap_openrv(relvar, AccessShareLock);
+ 	tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
+ 	relation_close(rel, AccessShareLock);
+ 
+ 	return tupdesc;
+ }
+ 
+ /*
+  * TypeGetTupleDesc
+  *
+  * Given a type Oid, build a TupleDesc.
+  *
+  * If the type is composite, *and* a colaliases List is provided, *and*
+  * the List is of natts length, use the aliases instead of the relation
+  * attnames.
+  *
+  * If the type is a base type, a single item alias List is required.
+  */
+ TupleDesc
+ TypeGetTupleDesc(Oid typeoid, List *colaliases)
+ {
+ 	Oid			relid = typeidTypeRelid(typeoid);
+ 	TupleDesc	tupdesc;
+ 
+ 	/*
+ 	 * Build a suitable tupledesc representing the output rows
+ 	 */
+ 	if (OidIsValid(relid))
+ 	{
+ 		/* Composite data type, i.e. a table's row type */
+ 		Relation	rel;
+ 		int			natts;
+ 
+ 		rel = relation_open(relid, AccessShareLock);
+ 		tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
+ 		natts = tupdesc->natts;
+ 		relation_close(rel, AccessShareLock);
+ 
+ 		/* check to see if we've given column aliases */
+ 		if(colaliases != NIL)
+ 		{
+ 			char	   *label;
+ 			int			varattno;
+ 
+ 			/* does the List length match the number of attributes */
+ 			if (length(colaliases) != natts)
+ 				elog(ERROR, "TypeGetTupleDesc: number of aliases does not match number of attributes");
+ 
+ 			/* OK, use the aliases instead */
+ 			for (varattno = 0; varattno < natts; varattno++)
+ 			{
+ 				label = strVal(nth(varattno, colaliases));
+ 
+ 				if (label != NULL)
+ 					namestrcpy(&(tupdesc->attrs[varattno]->attname), label);
+ 				else
+ 					MemSet(NameStr(tupdesc->attrs[varattno]->attname), 0, NAMEDATALEN);
+ 			}
+ 		}
+ 	}
+ 	else
+ 	{
+ 		/* Must be a base data type, i.e. scalar */
+ 		char	   *attname;
+ 
+ 		/* the alias List is required for base types */
+ 		if (colaliases == NIL)
+ 			elog(ERROR, "TypeGetTupleDesc: no column alias was provided");
+ 
+ 		/* the alias List length must be 1 */
+ 		if (length(colaliases) != 1)
+ 			elog(ERROR, "TypeGetTupleDesc: number of aliases does not match number of attributes");
+ 
+ 		/* OK, get the column alias */
+ 		attname = strVal(lfirst(colaliases));
+ 
+ 		tupdesc = CreateTemplateTupleDesc(1);
+ 		TupleDescInitEntry(tupdesc,
+ 						   (AttrNumber) 1,
+ 						   attname,
+ 						   typeoid,
+ 						   -1,
+ 						   0,
+ 						   false);
+ 	}
+ 
+ 	return tupdesc;
}
Index: src/backend/executor/execTuples.c
===================================================================
RCS file: /opt/src/cvs/pgsql/src/backend/executor/execTuples.c,v
retrieving revision 1.51
diff -c -r1.51 execTuples.c
*** src/backend/executor/execTuples.c	21 Mar 2002 06:21:04 -0000	1.51
--- src/backend/executor/execTuples.c	9 Jun 2002 21:00:31 -0000
***************
*** 107,117 ****
*/
#include "postgres.h"

#include "access/heapam.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"

- 
/* ----------------------------------------------------------------
*				  tuple table create/delete functions
* ----------------------------------------------------------------
--- 107,117 ----
*/
#include "postgres.h"

+ #include "funcapi.h"
#include "access/heapam.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"

/* ----------------------------------------------------------------
*				  tuple table create/delete functions
* ----------------------------------------------------------------
***************
*** 673,675 ****
--- 673,795 ----
return typeInfo;
}
+ 
+ /*
+  * TupleDescGetSlot - Initialize a slot based on the supplied
+  * tupledesc
+  */
+ TupleTableSlot *
+ TupleDescGetSlot(TupleDesc tupdesc)
+ {
+ 	TupleTableSlot	   *slot;
+ 
+ 	/* Make a standalone slot */
+ 	slot = MakeTupleTableSlot();
+ 
+ 	/* Bind the tuple description to the slot */
+ 	ExecSetSlotDescriptor(slot, tupdesc, true);
+ 
+ 	/* Return the slot */
+ 	return slot;
+ }
+ 
+ /*
+  * TupleDescGetAttInMetadata - Get a pointer to AttInMetadata based on the
+  * supplied TupleDesc. AttInMetadata can be used in conjunction with C strings
+  * to produce a properly formed tuple.
+  */
+ AttInMetadata *
+ TupleDescGetAttInMetadata(TupleDesc tupdesc)
+ {
+ 	int				natts;
+ 	int				i;
+ 	Oid				atttypeid;
+ 	Oid				attinfuncid;
+ 	Oid				attelem;
+ 	FmgrInfo	   *attinfuncinfo;
+ 	Oid			   *attelems;
+ 	int4		   *atttypmods;
+ 	AttInMetadata  *attinmeta;
+ 
+ 	attinmeta = (AttInMetadata *) palloc(sizeof(AttInMetadata));
+ 	natts = tupdesc->natts;
+ 
+ 	/*
+ 	 * Gather info needed later to call the "in" function for each attribute
+ 	 */
+ 	attinfuncinfo = (FmgrInfo *) palloc(natts * sizeof(FmgrInfo));
+ 	attelems = (Oid *) palloc(natts * sizeof(Oid));
+ 	atttypmods = (int4 *) palloc(natts * sizeof(int4));
+ 
+ 	for (i = 0; i < natts; i++)
+ 	{
+ 		atttypeid = tupdesc->attrs[i]->atttypid;
+ 		get_type_metadata(atttypeid, &attinfuncid, &attelem);
+ 
+ 		fmgr_info(attinfuncid, &attinfuncinfo[i]);
+ 		attelems[i] = attelem;
+ 		atttypmods[i] = tupdesc->attrs[i]->atttypmod;
+ 	}
+ 	attinmeta->tupdesc = tupdesc;
+ 	attinmeta->attinfuncs = attinfuncinfo;
+ 	attinmeta->attelems = attelems;
+ 	attinmeta->atttypmods = atttypmods;
+ 
+ 	return attinmeta;
+ }
+ 
+ /*
+  * BuildTupleFromCStrings - build a HeapTuple given user data in C string form.
+  * values is an array of C strings, one for each attribute of the return tuple.
+  */
+ HeapTuple
+ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
+ {
+ 	TupleDesc			tupdesc;
+ 	int					natts;
+ 	HeapTuple			tuple;
+ 	char			   *nulls;
+ 	int					i;
+ 	Datum			   *dvalues;
+ 	FmgrInfo			attinfuncinfo;
+ 	Oid					attelem;
+ 	int4				atttypmod;
+ 
+ 	tupdesc = attinmeta->tupdesc;
+ 	natts = tupdesc->natts;
+ 
+ 	dvalues = (Datum *) palloc(natts * sizeof(Datum));
+ 
+ 	/* Call the "in" function for each attribute */
+ 	for (i = 0; i < natts; i++)
+ 	{
+ 		if (values[i] != NULL)
+ 		{
+ 			attinfuncinfo = attinmeta->attinfuncs[i];
+ 			attelem = attinmeta->attelems[i];
+ 			atttypmod = attinmeta->atttypmods[i];
+ 
+ 			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;
+ }
+ 
Index: src/backend/utils/adt/regproc.c
===================================================================
RCS file: /opt/src/cvs/pgsql/src/backend/utils/adt/regproc.c,v
retrieving revision 1.68
diff -c -r1.68 regproc.c
*** src/backend/utils/adt/regproc.c	11 May 2002 00:24:16 -0000	1.68
--- src/backend/utils/adt/regproc.c	9 Jun 2002 21:00:31 -0000
***************
*** 37,44 ****
#include "utils/lsyscache.h"
#include "utils/syscache.h"
- 
- static List *stringToQualifiedNameList(const char *string, const char *caller);
static void parseNameAndArgTypes(const char *string, const char *caller,
const char *type0_spelling,
List **names, int *nargs, Oid *argtypes);
--- 37,42 ----
***************
*** 960,973 ****
}
- /*****************************************************************************
-  *	 SUPPORT ROUTINES														 *
-  *****************************************************************************/
- 
/*
* Given a C string, parse it into a qualified-name list.
*/
! static List *
stringToQualifiedNameList(const char *string, const char *caller)
{
char	   *rawname;
--- 958,967 ----
}
/*
* Given a C string, parse it into a qualified-name list.
*/
! List *
stringToQualifiedNameList(const char *string, const char *caller)
{
char	   *rawname;
***************
*** 996,1001 ****
--- 990,999 ----
return result;
}
+ 
+ /*****************************************************************************
+  *	 SUPPORT ROUTINES														 *
+  *****************************************************************************/
/*
* Given a C string, parse it into a qualified function or operator name
Index: src/backend/utils/fmgr/Makefile
===================================================================
RCS file: /opt/src/cvs/pgsql/src/backend/utils/fmgr/Makefile,v
retrieving revision 1.12
diff -c -r1.12 Makefile
*** src/backend/utils/fmgr/Makefile	16 Sep 2001 16:11:11 -0000	1.12
--- src/backend/utils/fmgr/Makefile	9 Jun 2002 21:00:31 -0000
***************
*** 12,18 ****
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global

! OBJS = dfmgr.o fmgr.o

override CPPFLAGS += -DPKGLIBDIR=\"$(pkglibdir)\" -DDLSUFFIX=\"$(DLSUFFIX)\"

--- 12,18 ----
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global

! OBJS = dfmgr.o fmgr.o funcapi.o

override CPPFLAGS += -DPKGLIBDIR=\"$(pkglibdir)\" -DDLSUFFIX=\"$(DLSUFFIX)\"

Index: src/backend/utils/fmgr/funcapi.c
===================================================================
RCS file: src/backend/utils/fmgr/funcapi.c
diff -N src/backend/utils/fmgr/funcapi.c
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- src/backend/utils/fmgr/funcapi.c	9 Jun 2002 22:55:47 -0000
***************
*** 0 ****
--- 1,122 ----
+ /*-------------------------------------------------------------------------
+  *
+  * funcapi.c
+  *	  Utility and convenience functions for fmgr functions that return
+  *	  sets and/or composite types.
+  *
+  * Copyright (c) 2002, PostgreSQL Global Development Group
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ #include "funcapi.h"
+ #include "catalog/pg_type.h"
+ #include "utils/syscache.h"
+ 
+ /*
+  * init_MultiFuncCall
+  * Create an empty FuncCallContext data structure
+  * and do some other basic Multi-function call setup
+  * and error checking
+  */
+ FuncCallContext *
+ init_MultiFuncCall(PG_FUNCTION_ARGS)
+ {
+ 	FuncCallContext *retval;
+ 
+ 	/*
+ 	 * Bail if we're called in the wrong context
+ 	 */
+ 	if (fcinfo->resultinfo == NULL || !IsA(fcinfo->resultinfo, ReturnSetInfo))
+ 		elog(ERROR, "function called in context that does not accept a set result");
+ 
+ 	if (fcinfo->flinfo->fn_extra == NULL)
+ 	{
+ 		/*
+ 		 * First call
+ 		 */
+ 		MemoryContext oldcontext;
+ 
+ 		/* switch to the appropriate memory context */
+ 		oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
+ 
+ 		/*
+ 		 * allocate space and zero it
+ 		 */
+ 		retval = (FuncCallContext *) palloc(sizeof(FuncCallContext));
+ 		MemSet(retval, 0, sizeof(FuncCallContext));
+ 
+ 		/*
+ 		 * initialize the elements
+ 		 */
+ 		retval->call_cntr = 0;
+ 		retval->max_calls = 0;
+ 		retval->slot = NULL;
+ 		retval->fctx = NULL;
+ 		retval->attinmeta = NULL;
+ 		retval->fmctx = fcinfo->flinfo->fn_mcxt;
+ 
+ 		/*
+ 		 * save the pointer for cross-call use
+ 		 */
+ 		fcinfo->flinfo->fn_extra = retval;
+ 
+ 		/* back to the original memory context */
+ 		MemoryContextSwitchTo(oldcontext);
+ 	}
+ 	else	/* second and subsequent calls */
+ 	{
+ 		elog(ERROR, "init_MultiFuncCall may not be called more than once");
+ 
+ 		/* never reached, but keep compiler happy */
+ 		retval = NULL;
+ 	}
+ 
+ 	return retval;
+ }
+ 
+ /*
+  * end_MultiFuncCall
+  * Clean up after init_MultiFuncCall
+  */
+ void
+ end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx)
+ {
+ 	MemoryContext oldcontext;
+ 
+ 	/* unbind from fcinfo */
+ 	fcinfo->flinfo->fn_extra = NULL;
+ 
+ 	/*
+ 	 * Caller is responsible to free up memory for individual
+ 	 * struct elements other than att_in_funcinfo and elements.
+ 	 */
+ 	oldcontext = MemoryContextSwitchTo(funcctx->fmctx);
+ 
+ 	if (funcctx->attinmeta != NULL)
+ 		pfree(funcctx->attinmeta);
+ 
+ 	pfree(funcctx);
+ 
+ 	MemoryContextSwitchTo(oldcontext);
+ }
+ 
+ void
+ get_type_metadata(Oid typeid, Oid *attinfuncid, Oid *attelem)
+ {
+ 	HeapTuple		typeTuple;
+ 	Form_pg_type	typtup;
+ 
+ 	typeTuple = SearchSysCache(TYPEOID,
+ 							   ObjectIdGetDatum(typeid),
+ 							   0, 0, 0);
+ 	if (!HeapTupleIsValid(typeTuple))
+ 		elog(ERROR, "get_type_metadata: Cache lookup of type %u failed", typeid);
+ 
+ 	typtup = (Form_pg_type) GETSTRUCT(typeTuple);
+ 
+ 	*attinfuncid = typtup->typinput;
+ 	*attelem = typtup->typelem;
+ 
+ 	ReleaseSysCache(typeTuple);
+ }
Index: src/include/funcapi.h
===================================================================
RCS file: src/include/funcapi.h
diff -N src/include/funcapi.h
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- src/include/funcapi.h	10 Jun 2002 00:22:42 -0000
***************
*** 0 ****
--- 1,197 ----
+ /*-------------------------------------------------------------------------
+  *
+  * funcapi.h
+  *	  Definitions for functions which return composite type and/or sets
+  *
+  * This file must be included by all Postgres modules that either define
+  * or call FUNCAPI-callable functions or macros.
+  *
+  *
+  * Copyright (c) 2002, PostgreSQL Global Development Group
+  *
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef FUNCAPI_H
+ #define FUNCAPI_H
+ 
+ #include "postgres.h"
+ 
+ #include "fmgr.h"
+ #include "access/htup.h"
+ #include "access/tupdesc.h"
+ #include "executor/executor.h"
+ #include "executor/tuptable.h"
+ 
+ /*
+  * All functions that can be called directly by fmgr must have this signature.
+  * (Other functions can be called by using a handler that does have this
+  * signature.)
+  */
+ 
+ 
+ /*-------------------------------------------------------------------------
+  *	Support to ease writing Functions returning composite types
+  *-------------------------------------------------------------------------
+  *
+  * This struct holds arrays of individual attribute information
+  * needed to create a tuple from raw C strings. It also requires
+  * a copy of the TupleDesc. The information carried here
+  * is derived from the TupleDesc, but it is stored here to
+  * avoid redundant cpu cycles on each call to an SRF.
+  */
+ typedef struct
+ {
+ 	/* full TupleDesc */
+ 	TupleDesc	   tupdesc;
+ 
+ 	/* pointer to array of attribute "type"in finfo */
+ 	FmgrInfo	   *attinfuncs;
+ 
+ 	/* pointer to array of attribute type typelem */
+ 	Oid			   *attelems;
+ 
+ 	/* pointer to array of attribute type typtypmod */
+ 	int4		   *atttypmods;
+ 
+ }	AttInMetadata;
+ 
+ /*-------------------------------------------------------------------------
+  *		Support struct to ease writing Set Returning Functions (SRFs)
+  *-------------------------------------------------------------------------
+  * 
+  * This struct holds function context for Set Returning Functions.
+  * Use fn_extra to hold a pointer to it across calls
+  */
+ typedef struct
+ {
+ 	/* Number of times we've been called before */
+ 	uint			call_cntr;
+ 
+ 	/* Maximum number of calls */
+ 	uint			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;
+ 
+ /*-------------------------------------------------------------------------
+  *	Support to ease writing Functions returning composite types
+  *
+  * External declarations:
+  * TupleDesc RelationNameGetTupleDesc(char *relname) - Use to get a TupleDesc
+  *		based on the function's return type relation.
+  * TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases) - Use to get a
+  *		TupleDesc based on the function's type oid. This can be used to get
+  *		a TupleDesc for a base (scalar), or composite (relation) type.
+  * TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc) - Initialize a slot
+  *		given a TupleDesc.
+  * AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc) - Get a pointer
+  *		to AttInMetadata based on the function's TupleDesc. AttInMetadata can
+  *		be used in conjunction with C strings to produce a properly formed
+  *		tuple. Store the metadata here for use across calls to avoid redundant
+  *		work.
+  * HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values) -
+  *		build a HeapTuple given user data in C string form. values is an array
+  *		of C strings, one for each attribute of the return tuple.
+  *
+  * Macro declarations:
+  * TupleGetDatum(TupleTableSlot *slot, HeapTuple tuple) - get a Datum
+  *		given a tuple and a slot.
+  */
+ 
+ /* from tupdesc.c */
+ extern TupleDesc RelationNameGetTupleDesc(char *relname);
+ extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
+ 
+ /* from execTuples.c */
+ extern TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc);
+ extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
+ extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
+ 
+ /* from funcapi.c */
+ extern void get_type_metadata(Oid typeid, Oid *attinfuncid, Oid *attelem);
+ 
+ #define TupleGetDatum(_slot, _tuple) \
+ 	PointerGetDatum(ExecStoreTuple(_tuple, _slot, InvalidBuffer, true))
+ 
+ /*-------------------------------------------------------------------------
+  *		Support for Set Returning Functions (SRFs)
+  *
+  * The basic API for SRFs looks something like:
+  *
+  * Datum
+  * my_Set_Returning_Function(PG_FUNCTION_ARGS)
+  * {
+  * 	FuncCallContext	   *funcctx;
+  * 	Datum				result;
+  * 	<user defined declarations>
+  * 
+  * 	if(SRF_IS_FIRSTPASS())
+  * 	{
+  * 		<user defined code>
+  * 		funcctx = SRF_FIRSTCALL_INIT();
+  * 		<if returning composite>
+  * 			<obtain slot>
+  * 			funcctx->slot = slot;
+  * 		<endif returning composite>
+  * 		<user defined code>
+  *  }
+  * 	<user defined code>
+  * 	funcctx = SRF_PERCALL_SETUP(funcctx);
+  * 	<user defined code>
+  * 
+  * 	if (funcctx->call_cntr < funcctx->max_calls)
+  * 	{
+  * 		<user defined code>
+  * 		<obtain result Datum>
+  * 		SRF_RETURN_NEXT(funcctx, result);
+  * 	}
+  * 	else
+  * 	{
+  * 		SRF_RETURN_DONE(funcctx);
+  * 	}
+  * }
+  *
+  */
+ 
+ /* 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; \
+ 		_funcctx->call_cntr++; \
+ 		rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
+ 		rsi->isDone = ExprMultipleResult; \
+ 		PG_RETURN_DATUM(_result); \
+ 	} while (0)
+ 
+ #define  SRF_RETURN_DONE(_funcctx) \
+ 	do { \
+ 		ReturnSetInfo	   *rsi; \
+ 		end_MultiFuncCall(fcinfo, _funcctx); \
+ 		rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
+ 		rsi->isDone = ExprEndResult; \
+ 		_funcctx->slot = NULL; \
+ 		PG_RETURN_NULL(); \
+ 	} while (0)
+ 
+ #endif   /* FUNCAPI_H */
Index: src/include/utils/builtins.h
===================================================================
RCS file: /opt/src/cvs/pgsql/src/include/utils/builtins.h,v
retrieving revision 1.182
diff -c -r1.182 builtins.h
*** src/include/utils/builtins.h	18 May 2002 21:38:41 -0000	1.182
--- src/include/utils/builtins.h	9 Jun 2002 21:00:31 -0000
***************
*** 341,346 ****
--- 341,347 ----
extern Datum regclassout(PG_FUNCTION_ARGS);
extern Datum regtypein(PG_FUNCTION_ARGS);
extern Datum regtypeout(PG_FUNCTION_ARGS);
+ extern List *stringToQualifiedNameList(const char *string, const char *caller);

/* ruleutils.c */
extern Datum pg_get_ruledef(PG_FUNCTION_ARGS);

---------------------------(end of broadcast)---------------------------
TIP 3: if posting/reading through Usenet, please send an appropriate
subscribe-nomail command to majordomo@postgresql.org so that your
message can get through to the mailing list cleanly

-- 
  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