aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2005-03-31 22:46:33 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2005-03-31 22:46:33 +0000
commit47888fe84227aaf3decffc7204554bdec54d2b29 (patch)
tree73703aa272d2b9899626002190f0fbd3b1e579fb /src/backend/utils
parentfb13881f423193a8342e0fe098f581e511b09d67 (diff)
downloadpostgresql-47888fe84227aaf3decffc7204554bdec54d2b29.tar.gz
postgresql-47888fe84227aaf3decffc7204554bdec54d2b29.zip
First phase of OUT-parameters project. We can now define and use SQL
functions with OUT parameters. The various PLs still need work, as does pg_dump. Rudimentary docs and regression tests included.
Diffstat (limited to 'src/backend/utils')
-rw-r--r--src/backend/utils/cache/lsyscache.c38
-rw-r--r--src/backend/utils/fmgr/fmgr.c30
-rw-r--r--src/backend/utils/fmgr/funcapi.c643
3 files changed, 664 insertions, 47 deletions
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 0b40a20b251..3abbf65fa48 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.121 2005/03/29 00:17:11 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.122 2005/03/31 22:46:14 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
@@ -1544,42 +1544,6 @@ get_typtype(Oid typid)
}
/*
- * get_type_func_class
- *
- * Given the type OID, obtain its TYPEFUNC classification.
- *
- * This is intended to centralize a bunch of formerly ad-hoc code for
- * classifying types. The categories used here are useful for deciding
- * how to handle functions returning the datatype.
- */
-TypeFuncClass
-get_type_func_class(Oid typid)
-{
- switch (get_typtype(typid))
- {
- case 'c':
- return TYPEFUNC_COMPOSITE;
- case 'b':
- case 'd':
- return TYPEFUNC_SCALAR;
- case 'p':
- if (typid == RECORDOID)
- return TYPEFUNC_RECORD;
- /*
- * We treat VOID and CSTRING as legitimate scalar datatypes,
- * mostly for the convenience of the JDBC driver (which wants
- * to be able to do "SELECT * FROM foo()" for all legitimately
- * user-callable functions).
- */
- if (typid == VOIDOID || typid == CSTRINGOID)
- return TYPEFUNC_SCALAR;
- return TYPEFUNC_OTHER;
- }
- /* shouldn't get here, probably */
- return TYPEFUNC_OTHER;
-}
-
-/*
* get_typ_typrelid
*
* Given the type OID, get the typrelid (InvalidOid if not a complex
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index a8bb5fc0ac5..0e9716de013 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.92 2005/03/29 03:01:31 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.93 2005/03/31 22:46:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -403,7 +403,7 @@ fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
* We want to raise an error here only if the info function returns
* something bogus.
*
- * This function is broken out of fmgr_info_C_lang() so that ProcedureCreate()
+ * This function is broken out of fmgr_info_C_lang so that fmgr_c_validator
* can validate the information record for a function not yet entered into
* pg_proc.
*/
@@ -576,8 +576,8 @@ fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo,
/*
- * Specialized lookup routine for ProcedureCreate(): given the alleged name
- * of an internal function, return the OID of the function.
+ * Specialized lookup routine for fmgr_internal_validator: given the alleged
+ * name of an internal function, return the OID of the function.
* If the name is not recognized, return InvalidOid.
*/
Oid
@@ -1869,10 +1869,6 @@ get_fn_expr_rettype(FmgrInfo *flinfo)
Oid
get_fn_expr_argtype(FmgrInfo *flinfo, int argnum)
{
- Node *expr;
- List *args;
- Oid argtype;
-
/*
* can't return anything useful if we have no FmgrInfo or if its
* fn_expr node has not been initialized
@@ -1880,7 +1876,23 @@ get_fn_expr_argtype(FmgrInfo *flinfo, int argnum)
if (!flinfo || !flinfo->fn_expr)
return InvalidOid;
- expr = flinfo->fn_expr;
+ return get_call_expr_argtype(flinfo->fn_expr, argnum);
+}
+
+/*
+ * Get the actual type OID of a specific function argument (counting from 0),
+ * but working from the calling expression tree instead of FmgrInfo
+ *
+ * Returns InvalidOid if information is not available
+ */
+Oid
+get_call_expr_argtype(Node *expr, int argnum)
+{
+ List *args;
+ Oid argtype;
+
+ if (expr == NULL)
+ return InvalidOid;
if (IsA(expr, FuncExpr))
args = ((FuncExpr *) expr)->args;
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 2c3845b7bf4..160847de1e0 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -7,17 +7,37 @@
* Copyright (c) 2002-2005, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.18 2005/01/01 05:43:08 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.19 2005/03/31 22:46:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include "access/heapam.h"
#include "funcapi.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_expr.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/syscache.h"
+#include "utils/typcache.h"
+
static void shutdown_MultiFuncCall(Datum arg);
+static TypeFuncClass internal_get_result_type(Oid funcid,
+ Node *call_expr,
+ ReturnSetInfo *rsinfo,
+ Oid *resultTypeId,
+ TupleDesc *resultTupleDesc);
+static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc,
+ oidvector *declared_args,
+ Node *call_expr);
+static TypeFuncClass get_type_func_class(Oid typid);
+
/*
* init_MultiFuncCall
@@ -156,3 +176,624 @@ shutdown_MultiFuncCall(Datum arg)
pfree(funcctx);
}
+
+
+/*
+ * get_call_result_type
+ * Given a function's call info record, determine the kind of datatype
+ * it is supposed to return. If resultTypeId isn't NULL, *resultTypeId
+ * receives the actual datatype OID (this is mainly useful for scalar
+ * result types). If resultTupleDesc isn't NULL, *resultTupleDesc
+ * receives a pointer to a TupleDesc when the result is of a composite
+ * type, or NULL when it's a scalar result. NB: the tupledesc should
+ * be copied if it is to be accessed over a long period.
+ *
+ * One hard case that this handles is resolution of actual rowtypes for
+ * functions returning RECORD (from either the function's OUT parameter
+ * list, or a ReturnSetInfo context node). TYPEFUNC_RECORD is returned
+ * only when we couldn't resolve the actual rowtype for lack of information.
+ *
+ * The other hard case that this handles is resolution of polymorphism.
+ * We will never return ANYELEMENT or ANYARRAY, either as a scalar result
+ * type or as a component of a rowtype.
+ *
+ * This function is relatively expensive --- in a function returning set,
+ * try to call it only the first time through.
+ */
+TypeFuncClass
+get_call_result_type(FunctionCallInfo fcinfo,
+ Oid *resultTypeId,
+ TupleDesc *resultTupleDesc)
+{
+ return internal_get_result_type(fcinfo->flinfo->fn_oid,
+ fcinfo->flinfo->fn_expr,
+ (ReturnSetInfo *) fcinfo->resultinfo,
+ resultTypeId,
+ resultTupleDesc);
+}
+
+/*
+ * get_expr_result_type
+ * As above, but work from a calling expression node tree
+ */
+TypeFuncClass
+get_expr_result_type(Node *expr,
+ Oid *resultTypeId,
+ TupleDesc *resultTupleDesc)
+{
+ TypeFuncClass result;
+
+ if (expr && IsA(expr, FuncExpr))
+ result = internal_get_result_type(((FuncExpr *) expr)->funcid,
+ expr,
+ NULL,
+ resultTypeId,
+ resultTupleDesc);
+ else
+ {
+ /* handle as a generic expression; no chance to resolve RECORD */
+ Oid typid = exprType(expr);
+
+ if (resultTypeId)
+ *resultTypeId = typid;
+ if (resultTupleDesc)
+ *resultTupleDesc = NULL;
+ result = get_type_func_class(typid);
+ if (result == TYPEFUNC_COMPOSITE && resultTupleDesc)
+ *resultTupleDesc = lookup_rowtype_tupdesc(typid, -1);
+ }
+
+ return result;
+}
+
+/*
+ * get_expr_result_type
+ * As above, but work from a function's OID only
+ *
+ * This will not be able to resolve pure-RECORD results nor polymorphism.
+ */
+TypeFuncClass
+get_func_result_type(Oid functionId,
+ Oid *resultTypeId,
+ TupleDesc *resultTupleDesc)
+{
+ return internal_get_result_type(functionId,
+ NULL,
+ NULL,
+ resultTypeId,
+ resultTupleDesc);
+}
+
+/*
+ * internal_get_result_type -- workhorse code implementing all the above
+ *
+ * funcid must always be supplied. call_expr and rsinfo can be NULL if not
+ * available. We will return TYPEFUNC_RECORD, and store NULL into
+ * *resultTupleDesc, if we cannot deduce the complete result rowtype from
+ * the available information.
+ */
+static TypeFuncClass
+internal_get_result_type(Oid funcid,
+ Node *call_expr,
+ ReturnSetInfo *rsinfo,
+ Oid *resultTypeId,
+ TupleDesc *resultTupleDesc)
+{
+ TypeFuncClass result;
+ HeapTuple tp;
+ Form_pg_proc procform;
+ Oid rettype;
+ TupleDesc tupdesc;
+
+ /* First fetch the function's pg_proc row to inspect its rettype */
+ tp = SearchSysCache(PROCOID,
+ ObjectIdGetDatum(funcid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for function %u", funcid);
+ procform = (Form_pg_proc) GETSTRUCT(tp);
+
+ rettype = procform->prorettype;
+
+ /* Check for OUT parameters defining a RECORD result */
+ tupdesc = build_function_result_tupdesc_t(tp);
+ if (tupdesc)
+ {
+ /*
+ * It has OUT parameters, so it's basically like a regular
+ * composite type, except we have to be able to resolve any
+ * polymorphic OUT parameters.
+ */
+ if (resultTypeId)
+ *resultTypeId = rettype;
+
+ if (resolve_polymorphic_tupdesc(tupdesc,
+ &procform->proargtypes,
+ call_expr))
+ {
+ if (tupdesc->tdtypeid == RECORDOID &&
+ tupdesc->tdtypmod < 0)
+ assign_record_type_typmod(tupdesc);
+ if (resultTupleDesc)
+ *resultTupleDesc = tupdesc;
+ result = TYPEFUNC_COMPOSITE;
+ }
+ else
+ {
+ if (resultTupleDesc)
+ *resultTupleDesc = NULL;
+ result = TYPEFUNC_RECORD;
+ }
+
+ ReleaseSysCache(tp);
+
+ return result;
+ }
+
+ /*
+ * If scalar polymorphic result, try to resolve it.
+ */
+ if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
+ {
+ Oid newrettype = exprType(call_expr);
+
+ if (newrettype == InvalidOid) /* this probably should not happen */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("could not determine actual result type for function \"%s\" declared to return type %s",
+ NameStr(procform->proname),
+ format_type_be(rettype))));
+ rettype = newrettype;
+ }
+
+ if (resultTypeId)
+ *resultTypeId = rettype;
+ if (resultTupleDesc)
+ *resultTupleDesc = NULL; /* default result */
+
+ /* Classify the result type */
+ result = get_type_func_class(rettype);
+ switch (result)
+ {
+ case TYPEFUNC_COMPOSITE:
+ if (resultTupleDesc)
+ *resultTupleDesc = lookup_rowtype_tupdesc(rettype, -1);
+ /* Named composite types can't have any polymorphic columns */
+ break;
+ case TYPEFUNC_SCALAR:
+ break;
+ case TYPEFUNC_RECORD:
+ /* We must get the tupledesc from call context */
+ if (rsinfo && IsA(rsinfo, ReturnSetInfo) &&
+ rsinfo->expectedDesc != NULL)
+ {
+ result = TYPEFUNC_COMPOSITE;
+ if (resultTupleDesc)
+ *resultTupleDesc = rsinfo->expectedDesc;
+ /* Assume no polymorphic columns here, either */
+ }
+ break;
+ default:
+ break;
+ }
+
+ ReleaseSysCache(tp);
+
+ return result;
+}
+
+/*
+ * Given the result tuple descriptor for a function with OUT parameters,
+ * replace any polymorphic columns (ANYELEMENT/ANYARRAY) with correct data
+ * types deduced from the input arguments. Returns TRUE if able to deduce
+ * all types, FALSE if not.
+ */
+static bool
+resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
+ Node *call_expr)
+{
+ int natts = tupdesc->natts;
+ int nargs = declared_args->dim1;
+ bool have_anyelement_result = false;
+ bool have_anyarray_result = false;
+ Oid anyelement_type = InvalidOid;
+ Oid anyarray_type = InvalidOid;
+ int i;
+
+ /* See if there are any polymorphic outputs; quick out if not */
+ for (i = 0; i < natts; i++)
+ {
+ switch (tupdesc->attrs[i]->atttypid)
+ {
+ case ANYELEMENTOID:
+ have_anyelement_result = true;
+ break;
+ case ANYARRAYOID:
+ have_anyarray_result = true;
+ break;
+ default:
+ break;
+ }
+ }
+ if (!have_anyelement_result && !have_anyarray_result)
+ return true;
+
+ /*
+ * Otherwise, extract actual datatype(s) from input arguments. (We assume
+ * the parser already validated consistency of the arguments.)
+ */
+ if (!call_expr)
+ return false; /* no hope */
+
+ for (i = 0; i < nargs; i++)
+ {
+ switch (declared_args->values[i])
+ {
+ case ANYELEMENTOID:
+ if (!OidIsValid(anyelement_type))
+ anyelement_type = get_call_expr_argtype(call_expr, i);
+ break;
+ case ANYARRAYOID:
+ if (!OidIsValid(anyarray_type))
+ anyarray_type = get_call_expr_argtype(call_expr, i);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* If nothing found, parser messed up */
+ if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))
+ return false;
+
+ /* If needed, deduce one polymorphic type from the other */
+ if (have_anyelement_result && !OidIsValid(anyelement_type))
+ anyelement_type = resolve_generic_type(ANYELEMENTOID,
+ anyarray_type,
+ ANYARRAYOID);
+ if (have_anyarray_result && !OidIsValid(anyarray_type))
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+
+ /* And finally replace the tuple column types as needed */
+ for (i = 0; i < natts; i++)
+ {
+ switch (tupdesc->attrs[i]->atttypid)
+ {
+ case ANYELEMENTOID:
+ TupleDescInitEntry(tupdesc, i+1,
+ NameStr(tupdesc->attrs[i]->attname),
+ anyelement_type,
+ -1,
+ 0);
+ break;
+ case ANYARRAYOID:
+ TupleDescInitEntry(tupdesc, i+1,
+ NameStr(tupdesc->attrs[i]->attname),
+ anyarray_type,
+ -1,
+ 0);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * get_type_func_class
+ * Given the type OID, obtain its TYPEFUNC classification.
+ *
+ * This is intended to centralize a bunch of formerly ad-hoc code for
+ * classifying types. The categories used here are useful for deciding
+ * how to handle functions returning the datatype.
+ */
+static TypeFuncClass
+get_type_func_class(Oid typid)
+{
+ switch (get_typtype(typid))
+ {
+ case 'c':
+ return TYPEFUNC_COMPOSITE;
+ case 'b':
+ case 'd':
+ return TYPEFUNC_SCALAR;
+ case 'p':
+ if (typid == RECORDOID)
+ return TYPEFUNC_RECORD;
+ /*
+ * We treat VOID and CSTRING as legitimate scalar datatypes,
+ * mostly for the convenience of the JDBC driver (which wants
+ * to be able to do "SELECT * FROM foo()" for all legitimately
+ * user-callable functions).
+ */
+ if (typid == VOIDOID || typid == CSTRINGOID)
+ return TYPEFUNC_SCALAR;
+ return TYPEFUNC_OTHER;
+ }
+ /* shouldn't get here, probably */
+ return TYPEFUNC_OTHER;
+}
+
+
+/*
+ * build_function_result_tupdesc_t
+ *
+ * Given a pg_proc row for a function, return a tuple descriptor for the
+ * result rowtype, or NULL if the function does not have OUT parameters.
+ *
+ * Note that this does not handle resolution of ANYELEMENT/ANYARRAY types;
+ * that is deliberate.
+ */
+TupleDesc
+build_function_result_tupdesc_t(HeapTuple procTuple)
+{
+ Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(procTuple);
+ Datum proallargtypes;
+ Datum proargmodes;
+ Datum proargnames;
+ bool isnull;
+
+ /* Return NULL if the function isn't declared to return RECORD */
+ if (procform->prorettype != RECORDOID)
+ return NULL;
+
+ /* If there are no OUT parameters, return NULL */
+ if (heap_attisnull(procTuple, Anum_pg_proc_proallargtypes) ||
+ heap_attisnull(procTuple, Anum_pg_proc_proargmodes))
+ return NULL;
+
+ /* Get the data out of the tuple */
+ proallargtypes = SysCacheGetAttr(PROCOID, procTuple,
+ Anum_pg_proc_proallargtypes,
+ &isnull);
+ Assert(!isnull);
+ proargmodes = SysCacheGetAttr(PROCOID, procTuple,
+ Anum_pg_proc_proargmodes,
+ &isnull);
+ Assert(!isnull);
+ proargnames = SysCacheGetAttr(PROCOID, procTuple,
+ Anum_pg_proc_proargnames,
+ &isnull);
+ if (isnull)
+ proargnames = PointerGetDatum(NULL); /* just to be sure */
+
+ return build_function_result_tupdesc_d(proallargtypes,
+ proargmodes,
+ proargnames);
+}
+
+/*
+ * build_function_result_tupdesc_d
+ *
+ * Build a RECORD function's tupledesc from the pg_proc proallargtypes,
+ * proargmodes, and proargnames arrays. This is split out for the
+ * convenience of ProcedureCreate, which needs to be able to compute the
+ * tupledesc before actually creating the function.
+ *
+ * Returns NULL if there are not at least two OUT or INOUT arguments.
+ */
+TupleDesc
+build_function_result_tupdesc_d(Datum proallargtypes,
+ Datum proargmodes,
+ Datum proargnames)
+{
+ TupleDesc desc;
+ ArrayType *arr;
+ int numargs;
+ Oid *argtypes;
+ char *argmodes;
+ Datum *argnames = NULL;
+ Oid *outargtypes;
+ char **outargnames;
+ int numoutargs;
+ int nargnames;
+ int i;
+
+ /* Can't have output args if columns are null */
+ if (proallargtypes == PointerGetDatum(NULL) ||
+ proargmodes == PointerGetDatum(NULL))
+ return NULL;
+
+ /*
+ * We expect the arrays to be 1-D arrays of the right types; verify that.
+ * For the OID and char arrays, we don't need to use deconstruct_array()
+ * since the array data is just going to look like a C array of values.
+ */
+ arr = DatumGetArrayTypeP(proallargtypes); /* ensure not toasted */
+ numargs = ARR_DIMS(arr)[0];
+ if (ARR_NDIM(arr) != 1 ||
+ numargs < 0 ||
+ ARR_ELEMTYPE(arr) != OIDOID)
+ elog(ERROR, "proallargtypes is not a 1-D Oid array");
+ argtypes = (Oid *) ARR_DATA_PTR(arr);
+ arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */
+ if (ARR_NDIM(arr) != 1 ||
+ ARR_DIMS(arr)[0] != numargs ||
+ ARR_ELEMTYPE(arr) != CHAROID)
+ elog(ERROR, "proargmodes is not a 1-D char array");
+ argmodes = (char *) ARR_DATA_PTR(arr);
+ if (proargnames != PointerGetDatum(NULL))
+ {
+ arr = DatumGetArrayTypeP(proargnames); /* ensure not toasted */
+ if (ARR_NDIM(arr) != 1 ||
+ ARR_DIMS(arr)[0] != numargs ||
+ ARR_ELEMTYPE(arr) != TEXTOID)
+ elog(ERROR, "proargnames is not a 1-D text array");
+ deconstruct_array(arr, TEXTOID, -1, false, 'i',
+ &argnames, &nargnames);
+ Assert(nargnames == numargs);
+ }
+
+ /* zero elements probably shouldn't happen, but handle it gracefully */
+ if (numargs <= 0)
+ return NULL;
+
+ /* extract output-argument types and names */
+ outargtypes = (Oid *) palloc(numargs * sizeof(Oid));
+ outargnames = (char **) palloc(numargs * sizeof(char *));
+ numoutargs = 0;
+ for (i = 0; i < numargs; i++)
+ {
+ char *pname;
+
+ if (argmodes[i] == PROARGMODE_IN)
+ continue;
+ Assert(argmodes[i] == PROARGMODE_OUT ||
+ argmodes[i] == PROARGMODE_INOUT);
+ outargtypes[numoutargs] = argtypes[i];
+ if (argnames)
+ pname = DatumGetCString(DirectFunctionCall1(textout, argnames[i]));
+ else
+ pname = NULL;
+ if (pname == NULL || pname[0] == '\0')
+ {
+ /* Parameter is not named, so gin up a column name */
+ pname = (char *) palloc(32);
+ snprintf(pname, 32, "column%d", numoutargs + 1);
+ }
+ outargnames[numoutargs] = pname;
+ numoutargs++;
+ }
+
+ /*
+ * If there is no output argument, or only one, the function does not
+ * return tuples.
+ */
+ if (numoutargs < 2)
+ return NULL;
+
+ desc = CreateTemplateTupleDesc(numoutargs, false);
+ for (i = 0; i < numoutargs; i++)
+ {
+ TupleDescInitEntry(desc, i+1,
+ outargnames[i],
+ outargtypes[i],
+ -1,
+ 0);
+ }
+
+ return desc;
+}
+
+
+/*
+ * RelationNameGetTupleDesc
+ *
+ * Given a (possibly qualified) relation name, build a TupleDesc.
+ */
+TupleDesc
+RelationNameGetTupleDesc(const char *relname)
+{
+ RangeVar *relvar;
+ Relation rel;
+ TupleDesc tupdesc;
+ List *relname_list;
+
+ /* Open relation and copy the tuple description */
+ relname_list = stringToQualifiedNameList(relname, "RelationNameGetTupleDesc");
+ relvar = makeRangeVarFromNameList(relname_list);
+ rel = relation_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. (NB: this usage is deprecated since it may result in
+ * creation of unnecessary transient record types.)
+ *
+ * If the type is a base type, a single item alias List is required.
+ */
+TupleDesc
+TypeGetTupleDesc(Oid typeoid, List *colaliases)
+{
+ TypeFuncClass functypclass = get_type_func_class(typeoid);
+ TupleDesc tupdesc = NULL;
+
+ /*
+ * Build a suitable tupledesc representing the output rows
+ */
+ if (functypclass == TYPEFUNC_COMPOSITE)
+ {
+ /* Composite data type, e.g. a table's row type */
+ tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(typeoid, -1));
+
+ if (colaliases != NIL)
+ {
+ int natts = tupdesc->natts;
+ int varattno;
+
+ /* does the list length match the number of attributes? */
+ if (list_length(colaliases) != natts)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("number of aliases does not match number of columns")));
+
+ /* OK, use the aliases instead */
+ for (varattno = 0; varattno < natts; varattno++)
+ {
+ char *label = strVal(list_nth(colaliases, varattno));
+
+ if (label != NULL)
+ namestrcpy(&(tupdesc->attrs[varattno]->attname), label);
+ }
+
+ /* The tuple type is now an anonymous record type */
+ tupdesc->tdtypeid = RECORDOID;
+ tupdesc->tdtypmod = -1;
+ }
+ }
+ else if (functypclass == TYPEFUNC_SCALAR)
+ {
+ /* Base data type, i.e. scalar */
+ char *attname;
+
+ /* the alias list is required for base types */
+ if (colaliases == NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("no column alias was provided")));
+
+ /* the alias list length must be 1 */
+ if (list_length(colaliases) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("number of aliases does not match number of columns")));
+
+ /* OK, get the column alias */
+ attname = strVal(linitial(colaliases));
+
+ tupdesc = CreateTemplateTupleDesc(1, false);
+ TupleDescInitEntry(tupdesc,
+ (AttrNumber) 1,
+ attname,
+ typeoid,
+ -1,
+ 0);
+ }
+ else if (functypclass == TYPEFUNC_RECORD)
+ {
+ /* XXX can't support this because typmod wasn't passed in ... */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("could not determine row description for function returning record")));
+ }
+ else
+ {
+ /* crummy error message, but parser should have caught this */
+ elog(ERROR, "function in FROM has unsupported return type");
+ }
+
+ return tupdesc;
+}