Header And Logo

PostgreSQL
| The world's most advanced open source database.

funcapi.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * funcapi.c
00004  *    Utility and convenience functions for fmgr functions that return
00005  *    sets and/or composite types.
00006  *
00007  * Copyright (c) 2002-2013, PostgreSQL Global Development Group
00008  *
00009  * IDENTIFICATION
00010  *    src/backend/utils/fmgr/funcapi.c
00011  *
00012  *-------------------------------------------------------------------------
00013  */
00014 #include "postgres.h"
00015 
00016 #include "access/htup_details.h"
00017 #include "catalog/namespace.h"
00018 #include "catalog/pg_proc.h"
00019 #include "catalog/pg_type.h"
00020 #include "funcapi.h"
00021 #include "nodes/nodeFuncs.h"
00022 #include "parser/parse_coerce.h"
00023 #include "utils/array.h"
00024 #include "utils/builtins.h"
00025 #include "utils/lsyscache.h"
00026 #include "utils/memutils.h"
00027 #include "utils/rel.h"
00028 #include "utils/syscache.h"
00029 #include "utils/typcache.h"
00030 
00031 
00032 static void shutdown_MultiFuncCall(Datum arg);
00033 static TypeFuncClass internal_get_result_type(Oid funcid,
00034                          Node *call_expr,
00035                          ReturnSetInfo *rsinfo,
00036                          Oid *resultTypeId,
00037                          TupleDesc *resultTupleDesc);
00038 static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc,
00039                             oidvector *declared_args,
00040                             Node *call_expr);
00041 static TypeFuncClass get_type_func_class(Oid typid);
00042 
00043 
00044 /*
00045  * init_MultiFuncCall
00046  * Create an empty FuncCallContext data structure
00047  * and do some other basic Multi-function call setup
00048  * and error checking
00049  */
00050 FuncCallContext *
00051 init_MultiFuncCall(PG_FUNCTION_ARGS)
00052 {
00053     FuncCallContext *retval;
00054 
00055     /*
00056      * Bail if we're called in the wrong context
00057      */
00058     if (fcinfo->resultinfo == NULL || !IsA(fcinfo->resultinfo, ReturnSetInfo))
00059         ereport(ERROR,
00060                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00061                  errmsg("set-valued function called in context that cannot accept a set")));
00062 
00063     if (fcinfo->flinfo->fn_extra == NULL)
00064     {
00065         /*
00066          * First call
00067          */
00068         ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
00069         MemoryContext multi_call_ctx;
00070 
00071         /*
00072          * Create a suitably long-lived context to hold cross-call data
00073          */
00074         multi_call_ctx = AllocSetContextCreate(fcinfo->flinfo->fn_mcxt,
00075                                                "SRF multi-call context",
00076                                                ALLOCSET_SMALL_MINSIZE,
00077                                                ALLOCSET_SMALL_INITSIZE,
00078                                                ALLOCSET_SMALL_MAXSIZE);
00079 
00080         /*
00081          * Allocate suitably long-lived space and zero it
00082          */
00083         retval = (FuncCallContext *)
00084             MemoryContextAllocZero(multi_call_ctx,
00085                                    sizeof(FuncCallContext));
00086 
00087         /*
00088          * initialize the elements
00089          */
00090         retval->call_cntr = 0;
00091         retval->max_calls = 0;
00092         retval->slot = NULL;
00093         retval->user_fctx = NULL;
00094         retval->attinmeta = NULL;
00095         retval->tuple_desc = NULL;
00096         retval->multi_call_memory_ctx = multi_call_ctx;
00097 
00098         /*
00099          * save the pointer for cross-call use
00100          */
00101         fcinfo->flinfo->fn_extra = retval;
00102 
00103         /*
00104          * Ensure we will get shut down cleanly if the exprcontext is not run
00105          * to completion.
00106          */
00107         RegisterExprContextCallback(rsi->econtext,
00108                                     shutdown_MultiFuncCall,
00109                                     PointerGetDatum(fcinfo->flinfo));
00110     }
00111     else
00112     {
00113         /* second and subsequent calls */
00114         elog(ERROR, "init_MultiFuncCall cannot be called more than once");
00115 
00116         /* never reached, but keep compiler happy */
00117         retval = NULL;
00118     }
00119 
00120     return retval;
00121 }
00122 
00123 /*
00124  * per_MultiFuncCall
00125  *
00126  * Do Multi-function per-call setup
00127  */
00128 FuncCallContext *
00129 per_MultiFuncCall(PG_FUNCTION_ARGS)
00130 {
00131     FuncCallContext *retval = (FuncCallContext *) fcinfo->flinfo->fn_extra;
00132 
00133     /*
00134      * Clear the TupleTableSlot, if present.  This is for safety's sake: the
00135      * Slot will be in a long-lived context (it better be, if the
00136      * FuncCallContext is pointing to it), but in most usage patterns the
00137      * tuples stored in it will be in the function's per-tuple context. So at
00138      * the beginning of each call, the Slot will hold a dangling pointer to an
00139      * already-recycled tuple.  We clear it out here.
00140      *
00141      * Note: use of retval->slot is obsolete as of 8.0, and we expect that it
00142      * will always be NULL.  This is just here for backwards compatibility in
00143      * case someone creates a slot anyway.
00144      */
00145     if (retval->slot != NULL)
00146         ExecClearTuple(retval->slot);
00147 
00148     return retval;
00149 }
00150 
00151 /*
00152  * end_MultiFuncCall
00153  * Clean up after init_MultiFuncCall
00154  */
00155 void
00156 end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx)
00157 {
00158     ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
00159 
00160     /* Deregister the shutdown callback */
00161     UnregisterExprContextCallback(rsi->econtext,
00162                                   shutdown_MultiFuncCall,
00163                                   PointerGetDatum(fcinfo->flinfo));
00164 
00165     /* But use it to do the real work */
00166     shutdown_MultiFuncCall(PointerGetDatum(fcinfo->flinfo));
00167 }
00168 
00169 /*
00170  * shutdown_MultiFuncCall
00171  * Shutdown function to clean up after init_MultiFuncCall
00172  */
00173 static void
00174 shutdown_MultiFuncCall(Datum arg)
00175 {
00176     FmgrInfo   *flinfo = (FmgrInfo *) DatumGetPointer(arg);
00177     FuncCallContext *funcctx = (FuncCallContext *) flinfo->fn_extra;
00178 
00179     /* unbind from flinfo */
00180     flinfo->fn_extra = NULL;
00181 
00182     /*
00183      * Delete context that holds all multi-call data, including the
00184      * FuncCallContext itself
00185      */
00186     MemoryContextDelete(funcctx->multi_call_memory_ctx);
00187 }
00188 
00189 
00190 /*
00191  * get_call_result_type
00192  *      Given a function's call info record, determine the kind of datatype
00193  *      it is supposed to return.  If resultTypeId isn't NULL, *resultTypeId
00194  *      receives the actual datatype OID (this is mainly useful for scalar
00195  *      result types).  If resultTupleDesc isn't NULL, *resultTupleDesc
00196  *      receives a pointer to a TupleDesc when the result is of a composite
00197  *      type, or NULL when it's a scalar result.
00198  *
00199  * One hard case that this handles is resolution of actual rowtypes for
00200  * functions returning RECORD (from either the function's OUT parameter
00201  * list, or a ReturnSetInfo context node).  TYPEFUNC_RECORD is returned
00202  * only when we couldn't resolve the actual rowtype for lack of information.
00203  *
00204  * The other hard case that this handles is resolution of polymorphism.
00205  * We will never return polymorphic pseudotypes (ANYELEMENT etc), either
00206  * as a scalar result type or as a component of a rowtype.
00207  *
00208  * This function is relatively expensive --- in a function returning set,
00209  * try to call it only the first time through.
00210  */
00211 TypeFuncClass
00212 get_call_result_type(FunctionCallInfo fcinfo,
00213                      Oid *resultTypeId,
00214                      TupleDesc *resultTupleDesc)
00215 {
00216     return internal_get_result_type(fcinfo->flinfo->fn_oid,
00217                                     fcinfo->flinfo->fn_expr,
00218                                     (ReturnSetInfo *) fcinfo->resultinfo,
00219                                     resultTypeId,
00220                                     resultTupleDesc);
00221 }
00222 
00223 /*
00224  * get_expr_result_type
00225  *      As above, but work from a calling expression node tree
00226  */
00227 TypeFuncClass
00228 get_expr_result_type(Node *expr,
00229                      Oid *resultTypeId,
00230                      TupleDesc *resultTupleDesc)
00231 {
00232     TypeFuncClass result;
00233 
00234     if (expr && IsA(expr, FuncExpr))
00235         result = internal_get_result_type(((FuncExpr *) expr)->funcid,
00236                                           expr,
00237                                           NULL,
00238                                           resultTypeId,
00239                                           resultTupleDesc);
00240     else if (expr && IsA(expr, OpExpr))
00241         result = internal_get_result_type(get_opcode(((OpExpr *) expr)->opno),
00242                                           expr,
00243                                           NULL,
00244                                           resultTypeId,
00245                                           resultTupleDesc);
00246     else
00247     {
00248         /* handle as a generic expression; no chance to resolve RECORD */
00249         Oid         typid = exprType(expr);
00250 
00251         if (resultTypeId)
00252             *resultTypeId = typid;
00253         if (resultTupleDesc)
00254             *resultTupleDesc = NULL;
00255         result = get_type_func_class(typid);
00256         if (result == TYPEFUNC_COMPOSITE && resultTupleDesc)
00257             *resultTupleDesc = lookup_rowtype_tupdesc_copy(typid, -1);
00258     }
00259 
00260     return result;
00261 }
00262 
00263 /*
00264  * get_func_result_type
00265  *      As above, but work from a function's OID only
00266  *
00267  * This will not be able to resolve pure-RECORD results nor polymorphism.
00268  */
00269 TypeFuncClass
00270 get_func_result_type(Oid functionId,
00271                      Oid *resultTypeId,
00272                      TupleDesc *resultTupleDesc)
00273 {
00274     return internal_get_result_type(functionId,
00275                                     NULL,
00276                                     NULL,
00277                                     resultTypeId,
00278                                     resultTupleDesc);
00279 }
00280 
00281 /*
00282  * internal_get_result_type -- workhorse code implementing all the above
00283  *
00284  * funcid must always be supplied.  call_expr and rsinfo can be NULL if not
00285  * available.  We will return TYPEFUNC_RECORD, and store NULL into
00286  * *resultTupleDesc, if we cannot deduce the complete result rowtype from
00287  * the available information.
00288  */
00289 static TypeFuncClass
00290 internal_get_result_type(Oid funcid,
00291                          Node *call_expr,
00292                          ReturnSetInfo *rsinfo,
00293                          Oid *resultTypeId,
00294                          TupleDesc *resultTupleDesc)
00295 {
00296     TypeFuncClass result;
00297     HeapTuple   tp;
00298     Form_pg_proc procform;
00299     Oid         rettype;
00300     TupleDesc   tupdesc;
00301 
00302     /* First fetch the function's pg_proc row to inspect its rettype */
00303     tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
00304     if (!HeapTupleIsValid(tp))
00305         elog(ERROR, "cache lookup failed for function %u", funcid);
00306     procform = (Form_pg_proc) GETSTRUCT(tp);
00307 
00308     rettype = procform->prorettype;
00309 
00310     /* Check for OUT parameters defining a RECORD result */
00311     tupdesc = build_function_result_tupdesc_t(tp);
00312     if (tupdesc)
00313     {
00314         /*
00315          * It has OUT parameters, so it's basically like a regular composite
00316          * type, except we have to be able to resolve any polymorphic OUT
00317          * parameters.
00318          */
00319         if (resultTypeId)
00320             *resultTypeId = rettype;
00321 
00322         if (resolve_polymorphic_tupdesc(tupdesc,
00323                                         &procform->proargtypes,
00324                                         call_expr))
00325         {
00326             if (tupdesc->tdtypeid == RECORDOID &&
00327                 tupdesc->tdtypmod < 0)
00328                 assign_record_type_typmod(tupdesc);
00329             if (resultTupleDesc)
00330                 *resultTupleDesc = tupdesc;
00331             result = TYPEFUNC_COMPOSITE;
00332         }
00333         else
00334         {
00335             if (resultTupleDesc)
00336                 *resultTupleDesc = NULL;
00337             result = TYPEFUNC_RECORD;
00338         }
00339 
00340         ReleaseSysCache(tp);
00341 
00342         return result;
00343     }
00344 
00345     /*
00346      * If scalar polymorphic result, try to resolve it.
00347      */
00348     if (IsPolymorphicType(rettype))
00349     {
00350         Oid         newrettype = exprType(call_expr);
00351 
00352         if (newrettype == InvalidOid)   /* this probably should not happen */
00353             ereport(ERROR,
00354                     (errcode(ERRCODE_DATATYPE_MISMATCH),
00355                      errmsg("could not determine actual result type for function \"%s\" declared to return type %s",
00356                             NameStr(procform->proname),
00357                             format_type_be(rettype))));
00358         rettype = newrettype;
00359     }
00360 
00361     if (resultTypeId)
00362         *resultTypeId = rettype;
00363     if (resultTupleDesc)
00364         *resultTupleDesc = NULL;    /* default result */
00365 
00366     /* Classify the result type */
00367     result = get_type_func_class(rettype);
00368     switch (result)
00369     {
00370         case TYPEFUNC_COMPOSITE:
00371             if (resultTupleDesc)
00372                 *resultTupleDesc = lookup_rowtype_tupdesc_copy(rettype, -1);
00373             /* Named composite types can't have any polymorphic columns */
00374             break;
00375         case TYPEFUNC_SCALAR:
00376             break;
00377         case TYPEFUNC_RECORD:
00378             /* We must get the tupledesc from call context */
00379             if (rsinfo && IsA(rsinfo, ReturnSetInfo) &&
00380                 rsinfo->expectedDesc != NULL)
00381             {
00382                 result = TYPEFUNC_COMPOSITE;
00383                 if (resultTupleDesc)
00384                     *resultTupleDesc = rsinfo->expectedDesc;
00385                 /* Assume no polymorphic columns here, either */
00386             }
00387             break;
00388         default:
00389             break;
00390     }
00391 
00392     ReleaseSysCache(tp);
00393 
00394     return result;
00395 }
00396 
00397 /*
00398  * Given the result tuple descriptor for a function with OUT parameters,
00399  * replace any polymorphic columns (ANYELEMENT etc) with correct data types
00400  * deduced from the input arguments. Returns TRUE if able to deduce all types,
00401  * FALSE if not.
00402  */
00403 static bool
00404 resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
00405                             Node *call_expr)
00406 {
00407     int         natts = tupdesc->natts;
00408     int         nargs = declared_args->dim1;
00409     bool        have_anyelement_result = false;
00410     bool        have_anyarray_result = false;
00411     bool        have_anyrange_result = false;
00412     bool        have_anynonarray = false;
00413     bool        have_anyenum = false;
00414     Oid         anyelement_type = InvalidOid;
00415     Oid         anyarray_type = InvalidOid;
00416     Oid         anyrange_type = InvalidOid;
00417     Oid         anycollation = InvalidOid;
00418     int         i;
00419 
00420     /* See if there are any polymorphic outputs; quick out if not */
00421     for (i = 0; i < natts; i++)
00422     {
00423         switch (tupdesc->attrs[i]->atttypid)
00424         {
00425             case ANYELEMENTOID:
00426                 have_anyelement_result = true;
00427                 break;
00428             case ANYARRAYOID:
00429                 have_anyarray_result = true;
00430                 break;
00431             case ANYNONARRAYOID:
00432                 have_anyelement_result = true;
00433                 have_anynonarray = true;
00434                 break;
00435             case ANYENUMOID:
00436                 have_anyelement_result = true;
00437                 have_anyenum = true;
00438                 break;
00439             case ANYRANGEOID:
00440                 have_anyrange_result = true;
00441                 break;
00442             default:
00443                 break;
00444         }
00445     }
00446     if (!have_anyelement_result && !have_anyarray_result &&
00447         !have_anyrange_result)
00448         return true;
00449 
00450     /*
00451      * Otherwise, extract actual datatype(s) from input arguments.  (We assume
00452      * the parser already validated consistency of the arguments.)
00453      */
00454     if (!call_expr)
00455         return false;           /* no hope */
00456 
00457     for (i = 0; i < nargs; i++)
00458     {
00459         switch (declared_args->values[i])
00460         {
00461             case ANYELEMENTOID:
00462             case ANYNONARRAYOID:
00463             case ANYENUMOID:
00464                 if (!OidIsValid(anyelement_type))
00465                     anyelement_type = get_call_expr_argtype(call_expr, i);
00466                 break;
00467             case ANYARRAYOID:
00468                 if (!OidIsValid(anyarray_type))
00469                     anyarray_type = get_call_expr_argtype(call_expr, i);
00470                 break;
00471             case ANYRANGEOID:
00472                 if (!OidIsValid(anyrange_type))
00473                     anyrange_type = get_call_expr_argtype(call_expr, i);
00474                 break;
00475             default:
00476                 break;
00477         }
00478     }
00479 
00480     /* If nothing found, parser messed up */
00481     if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
00482         !OidIsValid(anyrange_type))
00483         return false;
00484 
00485     /* If needed, deduce one polymorphic type from others */
00486     if (have_anyelement_result && !OidIsValid(anyelement_type))
00487     {
00488         if (OidIsValid(anyarray_type))
00489             anyelement_type = resolve_generic_type(ANYELEMENTOID,
00490                                                    anyarray_type,
00491                                                    ANYARRAYOID);
00492         if (OidIsValid(anyrange_type))
00493         {
00494             Oid         subtype = resolve_generic_type(ANYELEMENTOID,
00495                                                        anyrange_type,
00496                                                        ANYRANGEOID);
00497 
00498             /* check for inconsistent array and range results */
00499             if (OidIsValid(anyelement_type) && anyelement_type != subtype)
00500                 return false;
00501             anyelement_type = subtype;
00502         }
00503     }
00504 
00505     if (have_anyarray_result && !OidIsValid(anyarray_type))
00506         anyarray_type = resolve_generic_type(ANYARRAYOID,
00507                                              anyelement_type,
00508                                              ANYELEMENTOID);
00509 
00510     /*
00511      * We can't deduce a range type from other polymorphic inputs, because
00512      * there may be multiple range types for the same subtype.
00513      */
00514     if (have_anyrange_result && !OidIsValid(anyrange_type))
00515         return false;
00516 
00517     /* Enforce ANYNONARRAY if needed */
00518     if (have_anynonarray && type_is_array(anyelement_type))
00519         return false;
00520 
00521     /* Enforce ANYENUM if needed */
00522     if (have_anyenum && !type_is_enum(anyelement_type))
00523         return false;
00524 
00525     /*
00526      * Identify the collation to use for polymorphic OUT parameters. (It'll
00527      * necessarily be the same for both anyelement and anyarray.)  Note that
00528      * range types are not collatable, so any possible internal collation of a
00529      * range type is not considered here.
00530      */
00531     if (OidIsValid(anyelement_type))
00532         anycollation = get_typcollation(anyelement_type);
00533     else if (OidIsValid(anyarray_type))
00534         anycollation = get_typcollation(anyarray_type);
00535 
00536     if (OidIsValid(anycollation))
00537     {
00538         /*
00539          * The types are collatable, so consider whether to use a nondefault
00540          * collation.  We do so if we can identify the input collation used
00541          * for the function.
00542          */
00543         Oid         inputcollation = exprInputCollation(call_expr);
00544 
00545         if (OidIsValid(inputcollation))
00546             anycollation = inputcollation;
00547     }
00548 
00549     /* And finally replace the tuple column types as needed */
00550     for (i = 0; i < natts; i++)
00551     {
00552         switch (tupdesc->attrs[i]->atttypid)
00553         {
00554             case ANYELEMENTOID:
00555             case ANYNONARRAYOID:
00556             case ANYENUMOID:
00557                 TupleDescInitEntry(tupdesc, i + 1,
00558                                    NameStr(tupdesc->attrs[i]->attname),
00559                                    anyelement_type,
00560                                    -1,
00561                                    0);
00562                 TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
00563                 break;
00564             case ANYARRAYOID:
00565                 TupleDescInitEntry(tupdesc, i + 1,
00566                                    NameStr(tupdesc->attrs[i]->attname),
00567                                    anyarray_type,
00568                                    -1,
00569                                    0);
00570                 TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
00571                 break;
00572             case ANYRANGEOID:
00573                 TupleDescInitEntry(tupdesc, i + 1,
00574                                    NameStr(tupdesc->attrs[i]->attname),
00575                                    anyrange_type,
00576                                    -1,
00577                                    0);
00578                 /* no collation should be attached to a range type */
00579                 break;
00580             default:
00581                 break;
00582         }
00583     }
00584 
00585     return true;
00586 }
00587 
00588 /*
00589  * Given the declared argument types and modes for a function, replace any
00590  * polymorphic types (ANYELEMENT etc) with correct data types deduced from the
00591  * input arguments.  Returns TRUE if able to deduce all types, FALSE if not.
00592  * This is the same logic as resolve_polymorphic_tupdesc, but with a different
00593  * argument representation.
00594  *
00595  * argmodes may be NULL, in which case all arguments are assumed to be IN mode.
00596  */
00597 bool
00598 resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
00599                              Node *call_expr)
00600 {
00601     bool        have_anyelement_result = false;
00602     bool        have_anyarray_result = false;
00603     bool        have_anyrange_result = false;
00604     Oid         anyelement_type = InvalidOid;
00605     Oid         anyarray_type = InvalidOid;
00606     Oid         anyrange_type = InvalidOid;
00607     int         inargno;
00608     int         i;
00609 
00610     /* First pass: resolve polymorphic inputs, check for outputs */
00611     inargno = 0;
00612     for (i = 0; i < numargs; i++)
00613     {
00614         char        argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
00615 
00616         switch (argtypes[i])
00617         {
00618             case ANYELEMENTOID:
00619             case ANYNONARRAYOID:
00620             case ANYENUMOID:
00621                 if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
00622                     have_anyelement_result = true;
00623                 else
00624                 {
00625                     if (!OidIsValid(anyelement_type))
00626                     {
00627                         anyelement_type = get_call_expr_argtype(call_expr,
00628                                                                 inargno);
00629                         if (!OidIsValid(anyelement_type))
00630                             return false;
00631                     }
00632                     argtypes[i] = anyelement_type;
00633                 }
00634                 break;
00635             case ANYARRAYOID:
00636                 if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
00637                     have_anyarray_result = true;
00638                 else
00639                 {
00640                     if (!OidIsValid(anyarray_type))
00641                     {
00642                         anyarray_type = get_call_expr_argtype(call_expr,
00643                                                               inargno);
00644                         if (!OidIsValid(anyarray_type))
00645                             return false;
00646                     }
00647                     argtypes[i] = anyarray_type;
00648                 }
00649                 break;
00650             case ANYRANGEOID:
00651                 if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
00652                     have_anyrange_result = true;
00653                 else
00654                 {
00655                     if (!OidIsValid(anyrange_type))
00656                     {
00657                         anyrange_type = get_call_expr_argtype(call_expr,
00658                                                               inargno);
00659                         if (!OidIsValid(anyrange_type))
00660                             return false;
00661                     }
00662                     argtypes[i] = anyrange_type;
00663                 }
00664                 break;
00665             default:
00666                 break;
00667         }
00668         if (argmode != PROARGMODE_OUT && argmode != PROARGMODE_TABLE)
00669             inargno++;
00670     }
00671 
00672     /* Done? */
00673     if (!have_anyelement_result && !have_anyarray_result &&
00674         !have_anyrange_result)
00675         return true;
00676 
00677     /* If no input polymorphics, parser messed up */
00678     if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
00679         !OidIsValid(anyrange_type))
00680         return false;
00681 
00682     /* If needed, deduce one polymorphic type from others */
00683     if (have_anyelement_result && !OidIsValid(anyelement_type))
00684     {
00685         if (OidIsValid(anyarray_type))
00686             anyelement_type = resolve_generic_type(ANYELEMENTOID,
00687                                                    anyarray_type,
00688                                                    ANYARRAYOID);
00689         if (OidIsValid(anyrange_type))
00690         {
00691             Oid         subtype = resolve_generic_type(ANYELEMENTOID,
00692                                                        anyrange_type,
00693                                                        ANYRANGEOID);
00694 
00695             /* check for inconsistent array and range results */
00696             if (OidIsValid(anyelement_type) && anyelement_type != subtype)
00697                 return false;
00698             anyelement_type = subtype;
00699         }
00700     }
00701 
00702     if (have_anyarray_result && !OidIsValid(anyarray_type))
00703         anyarray_type = resolve_generic_type(ANYARRAYOID,
00704                                              anyelement_type,
00705                                              ANYELEMENTOID);
00706 
00707     /*
00708      * We can't deduce a range type from other polymorphic inputs, because
00709      * there may be multiple range types for the same subtype.
00710      */
00711     if (have_anyrange_result && !OidIsValid(anyrange_type))
00712         return false;
00713 
00714     /* XXX do we need to enforce ANYNONARRAY or ANYENUM here?  I think not */
00715 
00716     /* And finally replace the output column types as needed */
00717     for (i = 0; i < numargs; i++)
00718     {
00719         switch (argtypes[i])
00720         {
00721             case ANYELEMENTOID:
00722             case ANYNONARRAYOID:
00723             case ANYENUMOID:
00724                 argtypes[i] = anyelement_type;
00725                 break;
00726             case ANYARRAYOID:
00727                 argtypes[i] = anyarray_type;
00728                 break;
00729             case ANYRANGEOID:
00730                 argtypes[i] = anyrange_type;
00731                 break;
00732             default:
00733                 break;
00734         }
00735     }
00736 
00737     return true;
00738 }
00739 
00740 /*
00741  * get_type_func_class
00742  *      Given the type OID, obtain its TYPEFUNC classification.
00743  *
00744  * This is intended to centralize a bunch of formerly ad-hoc code for
00745  * classifying types.  The categories used here are useful for deciding
00746  * how to handle functions returning the datatype.
00747  */
00748 static TypeFuncClass
00749 get_type_func_class(Oid typid)
00750 {
00751     switch (get_typtype(typid))
00752     {
00753         case TYPTYPE_COMPOSITE:
00754             return TYPEFUNC_COMPOSITE;
00755         case TYPTYPE_BASE:
00756         case TYPTYPE_DOMAIN:
00757         case TYPTYPE_ENUM:
00758         case TYPTYPE_RANGE:
00759             return TYPEFUNC_SCALAR;
00760         case TYPTYPE_PSEUDO:
00761             if (typid == RECORDOID)
00762                 return TYPEFUNC_RECORD;
00763 
00764             /*
00765              * We treat VOID and CSTRING as legitimate scalar datatypes,
00766              * mostly for the convenience of the JDBC driver (which wants to
00767              * be able to do "SELECT * FROM foo()" for all legitimately
00768              * user-callable functions).
00769              */
00770             if (typid == VOIDOID || typid == CSTRINGOID)
00771                 return TYPEFUNC_SCALAR;
00772             return TYPEFUNC_OTHER;
00773     }
00774     /* shouldn't get here, probably */
00775     return TYPEFUNC_OTHER;
00776 }
00777 
00778 
00779 /*
00780  * get_func_arg_info
00781  *
00782  * Fetch info about the argument types, names, and IN/OUT modes from the
00783  * pg_proc tuple.  Return value is the total number of arguments.
00784  * Other results are palloc'd.  *p_argtypes is always filled in, but
00785  * *p_argnames and *p_argmodes will be set NULL in the default cases
00786  * (no names, and all IN arguments, respectively).
00787  *
00788  * Note that this function simply fetches what is in the pg_proc tuple;
00789  * it doesn't do any interpretation of polymorphic types.
00790  */
00791 int
00792 get_func_arg_info(HeapTuple procTup,
00793                   Oid **p_argtypes, char ***p_argnames, char **p_argmodes)
00794 {
00795     Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
00796     Datum       proallargtypes;
00797     Datum       proargmodes;
00798     Datum       proargnames;
00799     bool        isNull;
00800     ArrayType  *arr;
00801     int         numargs;
00802     Datum      *elems;
00803     int         nelems;
00804     int         i;
00805 
00806     /* First discover the total number of parameters and get their types */
00807     proallargtypes = SysCacheGetAttr(PROCOID, procTup,
00808                                      Anum_pg_proc_proallargtypes,
00809                                      &isNull);
00810     if (!isNull)
00811     {
00812         /*
00813          * We expect the arrays to be 1-D arrays of the right types; verify
00814          * that.  For the OID and char arrays, we don't need to use
00815          * deconstruct_array() since the array data is just going to look like
00816          * a C array of values.
00817          */
00818         arr = DatumGetArrayTypeP(proallargtypes);       /* ensure not toasted */
00819         numargs = ARR_DIMS(arr)[0];
00820         if (ARR_NDIM(arr) != 1 ||
00821             numargs < 0 ||
00822             ARR_HASNULL(arr) ||
00823             ARR_ELEMTYPE(arr) != OIDOID)
00824             elog(ERROR, "proallargtypes is not a 1-D Oid array");
00825         Assert(numargs >= procStruct->pronargs);
00826         *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid));
00827         memcpy(*p_argtypes, ARR_DATA_PTR(arr),
00828                numargs * sizeof(Oid));
00829     }
00830     else
00831     {
00832         /* If no proallargtypes, use proargtypes */
00833         numargs = procStruct->proargtypes.dim1;
00834         Assert(numargs == procStruct->pronargs);
00835         *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid));
00836         memcpy(*p_argtypes, procStruct->proargtypes.values,
00837                numargs * sizeof(Oid));
00838     }
00839 
00840     /* Get argument names, if available */
00841     proargnames = SysCacheGetAttr(PROCOID, procTup,
00842                                   Anum_pg_proc_proargnames,
00843                                   &isNull);
00844     if (isNull)
00845         *p_argnames = NULL;
00846     else
00847     {
00848         deconstruct_array(DatumGetArrayTypeP(proargnames),
00849                           TEXTOID, -1, false, 'i',
00850                           &elems, NULL, &nelems);
00851         if (nelems != numargs)  /* should not happen */
00852             elog(ERROR, "proargnames must have the same number of elements as the function has arguments");
00853         *p_argnames = (char **) palloc(sizeof(char *) * numargs);
00854         for (i = 0; i < numargs; i++)
00855             (*p_argnames)[i] = TextDatumGetCString(elems[i]);
00856     }
00857 
00858     /* Get argument modes, if available */
00859     proargmodes = SysCacheGetAttr(PROCOID, procTup,
00860                                   Anum_pg_proc_proargmodes,
00861                                   &isNull);
00862     if (isNull)
00863         *p_argmodes = NULL;
00864     else
00865     {
00866         arr = DatumGetArrayTypeP(proargmodes);  /* ensure not toasted */
00867         if (ARR_NDIM(arr) != 1 ||
00868             ARR_DIMS(arr)[0] != numargs ||
00869             ARR_HASNULL(arr) ||
00870             ARR_ELEMTYPE(arr) != CHAROID)
00871             elog(ERROR, "proargmodes is not a 1-D char array");
00872         *p_argmodes = (char *) palloc(numargs * sizeof(char));
00873         memcpy(*p_argmodes, ARR_DATA_PTR(arr),
00874                numargs * sizeof(char));
00875     }
00876 
00877     return numargs;
00878 }
00879 
00880 
00881 /*
00882  * get_func_input_arg_names
00883  *
00884  * Extract the names of input arguments only, given a function's
00885  * proargnames and proargmodes entries in Datum form.
00886  *
00887  * Returns the number of input arguments, which is the length of the
00888  * palloc'd array returned to *arg_names.  Entries for unnamed args
00889  * are set to NULL.  You don't get anything if proargnames is NULL.
00890  */
00891 int
00892 get_func_input_arg_names(Datum proargnames, Datum proargmodes,
00893                          char ***arg_names)
00894 {
00895     ArrayType  *arr;
00896     int         numargs;
00897     Datum      *argnames;
00898     char       *argmodes;
00899     char      **inargnames;
00900     int         numinargs;
00901     int         i;
00902 
00903     /* Do nothing if null proargnames */
00904     if (proargnames == PointerGetDatum(NULL))
00905     {
00906         *arg_names = NULL;
00907         return 0;
00908     }
00909 
00910     /*
00911      * We expect the arrays to be 1-D arrays of the right types; verify that.
00912      * For proargmodes, we don't need to use deconstruct_array() since the
00913      * array data is just going to look like a C array of values.
00914      */
00915     arr = DatumGetArrayTypeP(proargnames);      /* ensure not toasted */
00916     if (ARR_NDIM(arr) != 1 ||
00917         ARR_HASNULL(arr) ||
00918         ARR_ELEMTYPE(arr) != TEXTOID)
00919         elog(ERROR, "proargnames is not a 1-D text array");
00920     deconstruct_array(arr, TEXTOID, -1, false, 'i',
00921                       &argnames, NULL, &numargs);
00922     if (proargmodes != PointerGetDatum(NULL))
00923     {
00924         arr = DatumGetArrayTypeP(proargmodes);  /* ensure not toasted */
00925         if (ARR_NDIM(arr) != 1 ||
00926             ARR_DIMS(arr)[0] != numargs ||
00927             ARR_HASNULL(arr) ||
00928             ARR_ELEMTYPE(arr) != CHAROID)
00929             elog(ERROR, "proargmodes is not a 1-D char array");
00930         argmodes = (char *) ARR_DATA_PTR(arr);
00931     }
00932     else
00933         argmodes = NULL;
00934 
00935     /* zero elements probably shouldn't happen, but handle it gracefully */
00936     if (numargs <= 0)
00937     {
00938         *arg_names = NULL;
00939         return 0;
00940     }
00941 
00942     /* extract input-argument names */
00943     inargnames = (char **) palloc(numargs * sizeof(char *));
00944     numinargs = 0;
00945     for (i = 0; i < numargs; i++)
00946     {
00947         if (argmodes == NULL ||
00948             argmodes[i] == PROARGMODE_IN ||
00949             argmodes[i] == PROARGMODE_INOUT ||
00950             argmodes[i] == PROARGMODE_VARIADIC)
00951         {
00952             char       *pname = TextDatumGetCString(argnames[i]);
00953 
00954             if (pname[0] != '\0')
00955                 inargnames[numinargs] = pname;
00956             else
00957                 inargnames[numinargs] = NULL;
00958             numinargs++;
00959         }
00960     }
00961 
00962     *arg_names = inargnames;
00963     return numinargs;
00964 }
00965 
00966 
00967 /*
00968  * get_func_result_name
00969  *
00970  * If the function has exactly one output parameter, and that parameter
00971  * is named, return the name (as a palloc'd string).  Else return NULL.
00972  *
00973  * This is used to determine the default output column name for functions
00974  * returning scalar types.
00975  */
00976 char *
00977 get_func_result_name(Oid functionId)
00978 {
00979     char       *result;
00980     HeapTuple   procTuple;
00981     Datum       proargmodes;
00982     Datum       proargnames;
00983     bool        isnull;
00984     ArrayType  *arr;
00985     int         numargs;
00986     char       *argmodes;
00987     Datum      *argnames;
00988     int         numoutargs;
00989     int         nargnames;
00990     int         i;
00991 
00992     /* First fetch the function's pg_proc row */
00993     procTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionId));
00994     if (!HeapTupleIsValid(procTuple))
00995         elog(ERROR, "cache lookup failed for function %u", functionId);
00996 
00997     /* If there are no named OUT parameters, return NULL */
00998     if (heap_attisnull(procTuple, Anum_pg_proc_proargmodes) ||
00999         heap_attisnull(procTuple, Anum_pg_proc_proargnames))
01000         result = NULL;
01001     else
01002     {
01003         /* Get the data out of the tuple */
01004         proargmodes = SysCacheGetAttr(PROCOID, procTuple,
01005                                       Anum_pg_proc_proargmodes,
01006                                       &isnull);
01007         Assert(!isnull);
01008         proargnames = SysCacheGetAttr(PROCOID, procTuple,
01009                                       Anum_pg_proc_proargnames,
01010                                       &isnull);
01011         Assert(!isnull);
01012 
01013         /*
01014          * We expect the arrays to be 1-D arrays of the right types; verify
01015          * that.  For the char array, we don't need to use deconstruct_array()
01016          * since the array data is just going to look like a C array of
01017          * values.
01018          */
01019         arr = DatumGetArrayTypeP(proargmodes);  /* ensure not toasted */
01020         numargs = ARR_DIMS(arr)[0];
01021         if (ARR_NDIM(arr) != 1 ||
01022             numargs < 0 ||
01023             ARR_HASNULL(arr) ||
01024             ARR_ELEMTYPE(arr) != CHAROID)
01025             elog(ERROR, "proargmodes is not a 1-D char array");
01026         argmodes = (char *) ARR_DATA_PTR(arr);
01027         arr = DatumGetArrayTypeP(proargnames);  /* ensure not toasted */
01028         if (ARR_NDIM(arr) != 1 ||
01029             ARR_DIMS(arr)[0] != numargs ||
01030             ARR_HASNULL(arr) ||
01031             ARR_ELEMTYPE(arr) != TEXTOID)
01032             elog(ERROR, "proargnames is not a 1-D text array");
01033         deconstruct_array(arr, TEXTOID, -1, false, 'i',
01034                           &argnames, NULL, &nargnames);
01035         Assert(nargnames == numargs);
01036 
01037         /* scan for output argument(s) */
01038         result = NULL;
01039         numoutargs = 0;
01040         for (i = 0; i < numargs; i++)
01041         {
01042             if (argmodes[i] == PROARGMODE_IN ||
01043                 argmodes[i] == PROARGMODE_VARIADIC)
01044                 continue;
01045             Assert(argmodes[i] == PROARGMODE_OUT ||
01046                    argmodes[i] == PROARGMODE_INOUT ||
01047                    argmodes[i] == PROARGMODE_TABLE);
01048             if (++numoutargs > 1)
01049             {
01050                 /* multiple out args, so forget it */
01051                 result = NULL;
01052                 break;
01053             }
01054             result = TextDatumGetCString(argnames[i]);
01055             if (result == NULL || result[0] == '\0')
01056             {
01057                 /* Parameter is not named, so forget it */
01058                 result = NULL;
01059                 break;
01060             }
01061         }
01062     }
01063 
01064     ReleaseSysCache(procTuple);
01065 
01066     return result;
01067 }
01068 
01069 
01070 /*
01071  * build_function_result_tupdesc_t
01072  *
01073  * Given a pg_proc row for a function, return a tuple descriptor for the
01074  * result rowtype, or NULL if the function does not have OUT parameters.
01075  *
01076  * Note that this does not handle resolution of polymorphic types;
01077  * that is deliberate.
01078  */
01079 TupleDesc
01080 build_function_result_tupdesc_t(HeapTuple procTuple)
01081 {
01082     Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(procTuple);
01083     Datum       proallargtypes;
01084     Datum       proargmodes;
01085     Datum       proargnames;
01086     bool        isnull;
01087 
01088     /* Return NULL if the function isn't declared to return RECORD */
01089     if (procform->prorettype != RECORDOID)
01090         return NULL;
01091 
01092     /* If there are no OUT parameters, return NULL */
01093     if (heap_attisnull(procTuple, Anum_pg_proc_proallargtypes) ||
01094         heap_attisnull(procTuple, Anum_pg_proc_proargmodes))
01095         return NULL;
01096 
01097     /* Get the data out of the tuple */
01098     proallargtypes = SysCacheGetAttr(PROCOID, procTuple,
01099                                      Anum_pg_proc_proallargtypes,
01100                                      &isnull);
01101     Assert(!isnull);
01102     proargmodes = SysCacheGetAttr(PROCOID, procTuple,
01103                                   Anum_pg_proc_proargmodes,
01104                                   &isnull);
01105     Assert(!isnull);
01106     proargnames = SysCacheGetAttr(PROCOID, procTuple,
01107                                   Anum_pg_proc_proargnames,
01108                                   &isnull);
01109     if (isnull)
01110         proargnames = PointerGetDatum(NULL);    /* just to be sure */
01111 
01112     return build_function_result_tupdesc_d(proallargtypes,
01113                                            proargmodes,
01114                                            proargnames);
01115 }
01116 
01117 /*
01118  * build_function_result_tupdesc_d
01119  *
01120  * Build a RECORD function's tupledesc from the pg_proc proallargtypes,
01121  * proargmodes, and proargnames arrays.  This is split out for the
01122  * convenience of ProcedureCreate, which needs to be able to compute the
01123  * tupledesc before actually creating the function.
01124  *
01125  * Returns NULL if there are not at least two OUT or INOUT arguments.
01126  */
01127 TupleDesc
01128 build_function_result_tupdesc_d(Datum proallargtypes,
01129                                 Datum proargmodes,
01130                                 Datum proargnames)
01131 {
01132     TupleDesc   desc;
01133     ArrayType  *arr;
01134     int         numargs;
01135     Oid        *argtypes;
01136     char       *argmodes;
01137     Datum      *argnames = NULL;
01138     Oid        *outargtypes;
01139     char      **outargnames;
01140     int         numoutargs;
01141     int         nargnames;
01142     int         i;
01143 
01144     /* Can't have output args if columns are null */
01145     if (proallargtypes == PointerGetDatum(NULL) ||
01146         proargmodes == PointerGetDatum(NULL))
01147         return NULL;
01148 
01149     /*
01150      * We expect the arrays to be 1-D arrays of the right types; verify that.
01151      * For the OID and char arrays, we don't need to use deconstruct_array()
01152      * since the array data is just going to look like a C array of values.
01153      */
01154     arr = DatumGetArrayTypeP(proallargtypes);   /* ensure not toasted */
01155     numargs = ARR_DIMS(arr)[0];
01156     if (ARR_NDIM(arr) != 1 ||
01157         numargs < 0 ||
01158         ARR_HASNULL(arr) ||
01159         ARR_ELEMTYPE(arr) != OIDOID)
01160         elog(ERROR, "proallargtypes is not a 1-D Oid array");
01161     argtypes = (Oid *) ARR_DATA_PTR(arr);
01162     arr = DatumGetArrayTypeP(proargmodes);      /* ensure not toasted */
01163     if (ARR_NDIM(arr) != 1 ||
01164         ARR_DIMS(arr)[0] != numargs ||
01165         ARR_HASNULL(arr) ||
01166         ARR_ELEMTYPE(arr) != CHAROID)
01167         elog(ERROR, "proargmodes is not a 1-D char array");
01168     argmodes = (char *) ARR_DATA_PTR(arr);
01169     if (proargnames != PointerGetDatum(NULL))
01170     {
01171         arr = DatumGetArrayTypeP(proargnames);  /* ensure not toasted */
01172         if (ARR_NDIM(arr) != 1 ||
01173             ARR_DIMS(arr)[0] != numargs ||
01174             ARR_HASNULL(arr) ||
01175             ARR_ELEMTYPE(arr) != TEXTOID)
01176             elog(ERROR, "proargnames is not a 1-D text array");
01177         deconstruct_array(arr, TEXTOID, -1, false, 'i',
01178                           &argnames, NULL, &nargnames);
01179         Assert(nargnames == numargs);
01180     }
01181 
01182     /* zero elements probably shouldn't happen, but handle it gracefully */
01183     if (numargs <= 0)
01184         return NULL;
01185 
01186     /* extract output-argument types and names */
01187     outargtypes = (Oid *) palloc(numargs * sizeof(Oid));
01188     outargnames = (char **) palloc(numargs * sizeof(char *));
01189     numoutargs = 0;
01190     for (i = 0; i < numargs; i++)
01191     {
01192         char       *pname;
01193 
01194         if (argmodes[i] == PROARGMODE_IN ||
01195             argmodes[i] == PROARGMODE_VARIADIC)
01196             continue;
01197         Assert(argmodes[i] == PROARGMODE_OUT ||
01198                argmodes[i] == PROARGMODE_INOUT ||
01199                argmodes[i] == PROARGMODE_TABLE);
01200         outargtypes[numoutargs] = argtypes[i];
01201         if (argnames)
01202             pname = TextDatumGetCString(argnames[i]);
01203         else
01204             pname = NULL;
01205         if (pname == NULL || pname[0] == '\0')
01206         {
01207             /* Parameter is not named, so gin up a column name */
01208             pname = (char *) palloc(32);
01209             snprintf(pname, 32, "column%d", numoutargs + 1);
01210         }
01211         outargnames[numoutargs] = pname;
01212         numoutargs++;
01213     }
01214 
01215     /*
01216      * If there is no output argument, or only one, the function does not
01217      * return tuples.
01218      */
01219     if (numoutargs < 2)
01220         return NULL;
01221 
01222     desc = CreateTemplateTupleDesc(numoutargs, false);
01223     for (i = 0; i < numoutargs; i++)
01224     {
01225         TupleDescInitEntry(desc, i + 1,
01226                            outargnames[i],
01227                            outargtypes[i],
01228                            -1,
01229                            0);
01230     }
01231 
01232     return desc;
01233 }
01234 
01235 
01236 /*
01237  * RelationNameGetTupleDesc
01238  *
01239  * Given a (possibly qualified) relation name, build a TupleDesc.
01240  *
01241  * Note: while this works as advertised, it's seldom the best way to
01242  * build a tupdesc for a function's result type.  It's kept around
01243  * only for backwards compatibility with existing user-written code.
01244  */
01245 TupleDesc
01246 RelationNameGetTupleDesc(const char *relname)
01247 {
01248     RangeVar   *relvar;
01249     Relation    rel;
01250     TupleDesc   tupdesc;
01251     List       *relname_list;
01252 
01253     /* Open relation and copy the tuple description */
01254     relname_list = stringToQualifiedNameList(relname);
01255     relvar = makeRangeVarFromNameList(relname_list);
01256     rel = relation_openrv(relvar, AccessShareLock);
01257     tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
01258     relation_close(rel, AccessShareLock);
01259 
01260     return tupdesc;
01261 }
01262 
01263 /*
01264  * TypeGetTupleDesc
01265  *
01266  * Given a type Oid, build a TupleDesc.  (In most cases you should be
01267  * using get_call_result_type or one of its siblings instead of this
01268  * routine, so that you can handle OUT parameters, RECORD result type,
01269  * and polymorphic results.)
01270  *
01271  * If the type is composite, *and* a colaliases List is provided, *and*
01272  * the List is of natts length, use the aliases instead of the relation
01273  * attnames.  (NB: this usage is deprecated since it may result in
01274  * creation of unnecessary transient record types.)
01275  *
01276  * If the type is a base type, a single item alias List is required.
01277  */
01278 TupleDesc
01279 TypeGetTupleDesc(Oid typeoid, List *colaliases)
01280 {
01281     TypeFuncClass functypclass = get_type_func_class(typeoid);
01282     TupleDesc   tupdesc = NULL;
01283 
01284     /*
01285      * Build a suitable tupledesc representing the output rows
01286      */
01287     if (functypclass == TYPEFUNC_COMPOSITE)
01288     {
01289         /* Composite data type, e.g. a table's row type */
01290         tupdesc = lookup_rowtype_tupdesc_copy(typeoid, -1);
01291 
01292         if (colaliases != NIL)
01293         {
01294             int         natts = tupdesc->natts;
01295             int         varattno;
01296 
01297             /* does the list length match the number of attributes? */
01298             if (list_length(colaliases) != natts)
01299                 ereport(ERROR,
01300                         (errcode(ERRCODE_DATATYPE_MISMATCH),
01301                          errmsg("number of aliases does not match number of columns")));
01302 
01303             /* OK, use the aliases instead */
01304             for (varattno = 0; varattno < natts; varattno++)
01305             {
01306                 char       *label = strVal(list_nth(colaliases, varattno));
01307 
01308                 if (label != NULL)
01309                     namestrcpy(&(tupdesc->attrs[varattno]->attname), label);
01310             }
01311 
01312             /* The tuple type is now an anonymous record type */
01313             tupdesc->tdtypeid = RECORDOID;
01314             tupdesc->tdtypmod = -1;
01315         }
01316     }
01317     else if (functypclass == TYPEFUNC_SCALAR)
01318     {
01319         /* Base data type, i.e. scalar */
01320         char       *attname;
01321 
01322         /* the alias list is required for base types */
01323         if (colaliases == NIL)
01324             ereport(ERROR,
01325                     (errcode(ERRCODE_DATATYPE_MISMATCH),
01326                      errmsg("no column alias was provided")));
01327 
01328         /* the alias list length must be 1 */
01329         if (list_length(colaliases) != 1)
01330             ereport(ERROR,
01331                     (errcode(ERRCODE_DATATYPE_MISMATCH),
01332               errmsg("number of aliases does not match number of columns")));
01333 
01334         /* OK, get the column alias */
01335         attname = strVal(linitial(colaliases));
01336 
01337         tupdesc = CreateTemplateTupleDesc(1, false);
01338         TupleDescInitEntry(tupdesc,
01339                            (AttrNumber) 1,
01340                            attname,
01341                            typeoid,
01342                            -1,
01343                            0);
01344     }
01345     else if (functypclass == TYPEFUNC_RECORD)
01346     {
01347         /* XXX can't support this because typmod wasn't passed in ... */
01348         ereport(ERROR,
01349                 (errcode(ERRCODE_DATATYPE_MISMATCH),
01350                  errmsg("could not determine row description for function returning record")));
01351     }
01352     else
01353     {
01354         /* crummy error message, but parser should have caught this */
01355         elog(ERROR, "function in FROM has unsupported return type");
01356     }
01357 
01358     return tupdesc;
01359 }