Re: [HACKERS] revised sample SRF C function; proposed SRF API - Mailing list pgsql-patches

From Joe Conway
Subject Re: [HACKERS] revised sample SRF C function; proposed SRF API
Date
Msg-id 3D013D98.7060108@joeconway.com
Whole thread Raw
List pgsql-patches
Tom Lane wrote:
> Definitely better.  I'd suggest also thinking about whether the
> same/similar macros can support functions that return a set of a
> scalar (non-tuple) datatype.  In my mind, the cleanest design would
> be some base macros that support functions-returning-set (of anything),
> and if you want to return a set of scalar then you just use these
> directly (handing a Datum to FUNC_RETURN_NEXT).  If you want to return
> a set of tuples then there are a couple extra steps that you need to
> do to build a tupdesc, build a tuple, and convert the tuple to Datum
> (which at the moment you do by putting it into a slot, but I think we
> ought to change that soon).  If it were really clean then the macros
> supporting these extra steps would also work without the SRF macros,
> so that you could use 'em in a function returning a single tuple.

As promised on HACKERS, here's the Composite and SRF function API patch.
Included is a new builtin guc function, exported as show_all_vars(). In
order to avoid creating a new bootstrapped relation, I made the pg_proc
entry specify 0 as the function return type, and then fixed it in
initdb.sh as follows:

$ECHO_N "setting return type for composite returning functions... "$ECHO_C

"$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null <<EOF
CREATE VIEW pg__guc AS \
     SELECT \
             ''::text as varname, \
             ''::text as varval;

UPDATE pg_proc SET \
     prorettype = (SELECT oid FROM pg_type WHERE typname = 'pg__guc') \
     WHERE \
           proname = 'show_all_vars';

EOF
if [ "$?" -ne 0 ]; then
     exit_nicely
fi
echo "ok"

Any concerns with this approach? Is it too much of a hack, or
preferrable to adding new bootstrapped relations solely for this purpose?

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    7 Jun 2002 22:03:52 -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    7 Jun 2002 16:43:51 -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    7 Jun 2002 16:43:51 -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    7 Jun 2002 16:43:51 -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    7 Jun 2002 16:43:51 -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, TupleTableSlot *slot)
+ {
+     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 = slot;
+         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/backend/utils/misc/guc.c
===================================================================
RCS file: /opt/src/cvs/pgsql/src/backend/utils/misc/guc.c,v
retrieving revision 1.69
diff -c -r1.69 guc.c
*** src/backend/utils/misc/guc.c    17 May 2002 20:32:29 -0000    1.69
--- src/backend/utils/misc/guc.c    7 Jun 2002 16:43:51 -0000
***************
*** 21,28 ****
--- 21,30 ----

  #include "utils/guc.h"

+ #include "funcapi.h"
  #include "access/xlog.h"
  #include "catalog/namespace.h"
+ #include "catalog/pg_type.h"
  #include "commands/async.h"
  #include "commands/variable.h"
  #include "fmgr.h"
***************
*** 824,830 ****


  static int guc_var_compare(const void *a, const void *b);
! static void _ShowOption(struct config_generic *record);


  /*
--- 826,833 ----


  static int guc_var_compare(const void *a, const void *b);
! static char *_ShowOption(struct config_generic *record);
! static char *GetNextGUCConfig(int varnum, char **varname);


  /*
***************
*** 2204,2215 ****
  ShowGUCConfigOption(const char *name)
  {
      struct config_generic *record;

      record = find_option(name);
      if (record == NULL)
          elog(ERROR, "Option '%s' is not recognized", name);

!     _ShowOption(record);
  }

  /*
--- 2207,2224 ----
  ShowGUCConfigOption(const char *name)
  {
      struct config_generic *record;
+     char *val;

      record = find_option(name);
      if (record == NULL)
          elog(ERROR, "Option '%s' is not recognized", name);

!     val = _ShowOption(record);
!     if(val != NULL)
!     {
!         elog(INFO, "%s is %s", record->name, val);
!         pfree(val);
!     }
  }

  /*
***************
*** 2219,2239 ****
  ShowAllGUCConfig(void)
  {
      int            i;

      for (i = 0; i < num_guc_variables; i++)
      {
          struct config_generic *conf = guc_variables[i];

          if ((conf->flags & GUC_NO_SHOW_ALL) == 0)
!             _ShowOption(conf);
      }
  }

! static void
  _ShowOption(struct config_generic *record)
  {
      char        buffer[256];
      const char *val;

      switch (record->vartype)
      {
--- 2228,2274 ----
  ShowAllGUCConfig(void)
  {
      int            i;
+     char       *val;

      for (i = 0; i < num_guc_variables; i++)
      {
          struct config_generic *conf = guc_variables[i];

          if ((conf->flags & GUC_NO_SHOW_ALL) == 0)
!         {
!             val = _ShowOption(conf);
!             if(val != NULL)
!             {
!                 elog(INFO, "%s is %s", conf->name, val);
!                 pfree(val);
!             }
!         }
      }
  }

! /*
!  * SHOW X or SHOW ALL command
!  */
! static char *
! GetNextGUCConfig(int varnum, char **varname)
! {
!     struct config_generic *conf = guc_variables[varnum];
!
!     *varname = pstrdup(conf->name);
!
!     if ((conf->flags & GUC_NO_SHOW_ALL) == 0)
!         return _ShowOption(conf);
!     else
!         return NULL;
!
! }
!
! static char *
  _ShowOption(struct config_generic *record)
  {
      char        buffer[256];
      const char *val;
+     char       *retval;

      switch (record->vartype)
      {
***************
*** 2297,2303 ****
              break;
      }

!     elog(INFO, "%s is %s", record->name, val);
  }


--- 2332,2340 ----
              break;
      }

!     retval = pstrdup(val);
!
!     return retval;
  }


***************
*** 2539,2541 ****
--- 2576,2672 ----

      return newarray;
  }
+
+
+ /*
+  * showguc_all - equiv to SHOW ALL command but implemented as
+  * an SRF.
+  */
+ Datum
+ showguc_all(PG_FUNCTION_ARGS)
+ {
+     FuncCallContext       *funcctx;
+     TupleDesc            tupdesc;
+     int                    call_cntr;
+     int                    max_calls;
+     TupleTableSlot       *slot;
+     AttInMetadata       *attinmeta;
+     struct config_generic **fctx;
+
+     /* stuff done only on the first call of the function */
+      if(SRF_IS_FIRSTPASS())
+      {
+         /*
+          * Build a tuple description for a pg__guc tuple
+          */
+         tupdesc = RelationNameGetTupleDesc("pg__guc");
+
+         /* allocate a slot for a tuple with this tupdesc */
+         slot = TupleDescGetSlot(tupdesc);
+
+         /* create a function context for cross-call persistence */
+          funcctx = SRF_FIRSTCALL_INIT(slot);
+
+         /*
+          * Generate attribute metadata needed later to produce tuples from raw
+          * C strings
+          */
+         attinmeta = TupleDescGetAttInMetadata(tupdesc);
+         funcctx->attinmeta = attinmeta;
+
+         /* total number of tuples to be returned */
+         funcctx->max_calls = num_guc_variables;
+
+         /* pointer to structure containing user defined context */
+         funcctx->fctx = (void *) guc_variables;
+     }
+
+     /* stuff done on every call of the function */
+      funcctx = SRF_PERCALL_SETUP(funcctx);
+
+     call_cntr = funcctx->call_cntr;
+     max_calls = funcctx->max_calls;
+     slot = funcctx->slot;
+     fctx = (struct config_generic **) funcctx->fctx;
+     attinmeta = funcctx->attinmeta;
+
+      if (call_cntr < max_calls)    /* do when there is more left to send */
+      {
+         char       *varname;
+         char       *varval;
+         char       **values;
+         HeapTuple    tuple;
+         Datum        result;
+
+         /*
+          * Get the next GUC variable name and value
+          */
+         varval = GetNextGUCConfig(call_cntr, &varname);
+
+         /*
+          * Prepare a values array for storage in our slot.
+          * This should be an array of C strings which will
+          * be processed later by the appropriate "in" functions.
+          */
+         values = (char **) palloc(2 * sizeof(char *));
+         values[0] = varname;
+         values[1] = varval;
+
+         /* build a tuple */
+         tuple = BuildTupleFromCStrings(attinmeta, values);
+
+         /* make the tuple into a datum */
+         result = TupleGetDatum(slot, tuple);
+
+         /* Clean up */
+         pfree(varname);
+         pfree(values);
+
+          SRF_RETURN_NEXT(funcctx, result);
+      }
+      else    /* do when there is no more left */
+      {
+          SRF_RETURN_DONE(funcctx);
+      }
+ }
+
Index: src/bin/initdb/initdb.sh
===================================================================
RCS file: /opt/src/cvs/pgsql/src/bin/initdb/initdb.sh,v
retrieving revision 1.155
diff -c -r1.155 initdb.sh
*** src/bin/initdb/initdb.sh    21 May 2002 19:06:00 -0000    1.155
--- src/bin/initdb/initdb.sh    7 Jun 2002 21:26:20 -0000
***************
*** 967,972 ****
--- 967,993 ----
      | "$PGPATH"/postgres $PGSQL_OPT template1 > /dev/null || exit_nicely
  echo "ok"

+
+ $ECHO_N "setting return type for composite returning functions... "$ECHO_C
+
+ "$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null <<EOF
+ CREATE VIEW pg__guc AS \
+     SELECT \
+             ''::text as varname, \
+             ''::text as varval;
+
+ UPDATE pg_proc SET \
+     prorettype = (SELECT oid FROM pg_type WHERE typname = 'pg__guc') \
+     WHERE \
+           proname = 'show_all_vars';
+
+ EOF
+ if [ "$?" -ne 0 ]; then
+     exit_nicely
+ fi
+ echo "ok"
+
+
  # Set most system catalogs and built-in functions as world-accessible.
  # Some objects may require different permissions by default, so we
  # make sure we don't overwrite privilege sets that have already been
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    7 Jun 2002 22:53:17 -0000
***************
*** 0 ****
--- 1,193 ----
+ /*-------------------------------------------------------------------------
+  *
+  * 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 FuncCallContext *init_MultiFuncCall(PG_FUNCTION_ARGS, TupleTableSlot *slot);
+ extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
+ 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>
+  *         <obtain slot>
+  *         funcctx = SRF_FIRSTCALL_INIT(slot);
+  *         <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, TupleTableSlot *slot);
+ extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
+
+ #define SRF_IS_FIRSTPASS() (fcinfo->flinfo->fn_extra == NULL)
+ #define SRF_FIRSTCALL_INIT(_slot) init_MultiFuncCall(fcinfo,_slot)
+ #define SRF_PERCALL_SETUP(_funcctx) fcinfo->flinfo->fn_extra;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/catalog/catversion.h
===================================================================
RCS file: /opt/src/cvs/pgsql/src/include/catalog/catversion.h,v
retrieving revision 1.133
diff -c -r1.133 catversion.h
*** src/include/catalog/catversion.h    22 May 2002 17:21:01 -0000    1.133
--- src/include/catalog/catversion.h    7 Jun 2002 16:43:51 -0000
***************
*** 53,58 ****
   */

  /*                            yyyymmddN */
! #define CATALOG_VERSION_NO    200205221

  #endif
--- 53,58 ----
   */

  /*                            yyyymmddN */
! #define CATALOG_VERSION_NO    200206061

  #endif
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /opt/src/cvs/pgsql/src/include/catalog/pg_proc.h,v
retrieving revision 1.240
diff -c -r1.240 pg_proc.h
*** src/include/catalog/pg_proc.h    24 May 2002 18:57:56 -0000    1.240
--- src/include/catalog/pg_proc.h    7 Jun 2002 17:48:25 -0000
***************
*** 2871,2876 ****
--- 2871,2879 ----
  DATA(insert OID = 2072 (  date_mi_interval    PGNSP PGUID 14 f f f t f i 2 1114 "1082 1186" 100 0 0 100  "select
cast($1as timestamp without time zone) - $2;" - _null_ )); 
  DESCR("subtract");

+ DATA(insert OID = 2073 (  show_all_vars        PGNSP PGUID 12 f f f t t s 0 0 "0" 100 0 0 100    showguc_all - _null_
));
+ DESCR("show all GUC variable values");
+
  /* Aggregates (moved here from pg_aggregate for 7.3) */

  DATA(insert OID = 2100 (  avg                PGNSP PGUID 12 t f f f f i 1 1700 "20" 100 0 0 100  aggregate_dummy -
_null_)); 
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    7 Jun 2002 16:43:52 -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);
***************
*** 630,634 ****
--- 631,638 ----
  /* quote.c */
  extern Datum quote_ident(PG_FUNCTION_ARGS);
  extern Datum quote_literal(PG_FUNCTION_ARGS);
+
+ /* guc.c */
+ extern Datum showguc_all(PG_FUNCTION_ARGS);

  #endif   /* BUILTINS_H */
Index: src/test/regress/expected/rules.out
===================================================================
RCS file: /opt/src/cvs/pgsql/src/test/regress/expected/rules.out,v
retrieving revision 1.53
diff -c -r1.53 rules.out
*** src/test/regress/expected/rules.out    3 May 2002 00:32:19 -0000    1.53
--- src/test/regress/expected/rules.out    7 Jun 2002 21:30:58 -0000
***************
*** 1267,1272 ****
--- 1267,1273 ----
           viewname         |




definition



                                                                                                  

--------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
   iexit                    | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih,
rampr WHERE (ih.thepath ## r.thepath); 
+  pg__guc                  | SELECT ''::text AS varname, ''::text AS varval;
   pg_indexes               | SELECT c.relname AS tablename, i.relname AS indexname, pg_get_indexdef(x.indexrelid) AS
indexdefFROM pg_index x, pg_class c, pg_class i WHERE ((((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char")) AND
(c.oid= x.indrelid)) AND (i.oid = x.indexrelid)); 
   pg_rules                 | SELECT n.nspname AS schemaname, c.relname AS tablename, r.rulename, pg_get_ruledef(r.oid)
ASdefinition FROM ((pg_rewrite r JOIN pg_class c ON ((c.oid = r.ev_class))) LEFT JOIN pg_namespace n ON ((n.oid =
c.relnamespace)))WHERE (r.rulename <> '_RETURN'::name); 
   pg_stat_activity         | SELECT d.oid AS datid, d.datname, pg_stat_get_backend_pid(s.backendid) AS procpid,
pg_stat_get_backend_userid(s.backendid)AS usesysid, u.usename, pg_stat_get_backend_activity(s.backendid) AS
current_queryFROM pg_database d, (SELECT pg_stat_get_backend_idset() AS backendid) s, pg_shadow u WHERE
((pg_stat_get_backend_dbid(s.backendid)= d.oid) AND (pg_stat_get_backend_userid(s.backendid) = u.usesysid)); 
***************
*** 1304,1310 ****
   shoelace_obsolete        | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len,
shoelace.sl_unit,shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE
(shoe.slcolor= shoelace.sl_color)))); 
   street                   | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ##
r.thepath);
   toyemp                   | SELECT emp.name, emp.age, emp."location", (12 * emp.salary) AS annualsal FROM emp;
! (38 rows)

  SELECT tablename, rulename, definition FROM pg_rules
      ORDER BY tablename, rulename;
--- 1305,1311 ----
   shoelace_obsolete        | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len,
shoelace.sl_unit,shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE
(shoe.slcolor= shoelace.sl_color)))); 
   street                   | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ##
r.thepath);
   toyemp                   | SELECT emp.name, emp.age, emp."location", (12 * emp.salary) AS annualsal FROM emp;
! (39 rows)

  SELECT tablename, rulename, definition FROM pg_rules
      ORDER BY tablename, rulename;

pgsql-patches by date:

Previous
From: Peter Eisentraut
Date:
Subject: Re: guc.c and postgresql.conf.sample constistency checker
Next
From: Bruce Momjian
Date:
Subject: Re: Make factorial(0::int2) return 1, as per spec.