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; + * + * + * if(SRF_IS_FIRSTPASS()) + * { + * + * funcctx = SRF_FIRSTCALL_INIT(); + * + * + * funcctx->slot = slot; + * + * + * } + * + * funcctx = SRF_PERCALL_SETUP(funcctx); + * + * + * if (funcctx->call_cntr < funcctx->max_calls) + * { + * + * + * 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);