LCOV - code coverage report
Current view: top level - src/backend/utils/fmgr - funcapi.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 387 498 77.7 %
Date: 2017-09-29 15:12:54 Functions: 16 19 84.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * funcapi.c
       4             :  *    Utility and convenience functions for fmgr functions that return
       5             :  *    sets and/or composite types.
       6             :  *
       7             :  * Copyright (c) 2002-2017, PostgreSQL Global Development Group
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/utils/fmgr/funcapi.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : #include "postgres.h"
      15             : 
      16             : #include "access/htup_details.h"
      17             : #include "catalog/namespace.h"
      18             : #include "catalog/pg_proc.h"
      19             : #include "catalog/pg_type.h"
      20             : #include "funcapi.h"
      21             : #include "nodes/nodeFuncs.h"
      22             : #include "parser/parse_coerce.h"
      23             : #include "utils/array.h"
      24             : #include "utils/builtins.h"
      25             : #include "utils/lsyscache.h"
      26             : #include "utils/memutils.h"
      27             : #include "utils/regproc.h"
      28             : #include "utils/rel.h"
      29             : #include "utils/syscache.h"
      30             : #include "utils/typcache.h"
      31             : 
      32             : 
      33             : static void shutdown_MultiFuncCall(Datum arg);
      34             : static TypeFuncClass internal_get_result_type(Oid funcid,
      35             :                          Node *call_expr,
      36             :                          ReturnSetInfo *rsinfo,
      37             :                          Oid *resultTypeId,
      38             :                          TupleDesc *resultTupleDesc);
      39             : static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc,
      40             :                             oidvector *declared_args,
      41             :                             Node *call_expr);
      42             : static TypeFuncClass get_type_func_class(Oid typid);
      43             : 
      44             : 
      45             : /*
      46             :  * init_MultiFuncCall
      47             :  * Create an empty FuncCallContext data structure
      48             :  * and do some other basic Multi-function call setup
      49             :  * and error checking
      50             :  */
      51             : FuncCallContext *
      52       12705 : init_MultiFuncCall(PG_FUNCTION_ARGS)
      53             : {
      54             :     FuncCallContext *retval;
      55             : 
      56             :     /*
      57             :      * Bail if we're called in the wrong context
      58             :      */
      59       12705 :     if (fcinfo->resultinfo == NULL || !IsA(fcinfo->resultinfo, ReturnSetInfo))
      60           0 :         ereport(ERROR,
      61             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
      62             :                  errmsg("set-valued function called in context that cannot accept a set")));
      63             : 
      64       12705 :     if (fcinfo->flinfo->fn_extra == NULL)
      65             :     {
      66             :         /*
      67             :          * First call
      68             :          */
      69       12705 :         ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
      70             :         MemoryContext multi_call_ctx;
      71             : 
      72             :         /*
      73             :          * Create a suitably long-lived context to hold cross-call data
      74             :          */
      75       12705 :         multi_call_ctx = AllocSetContextCreate(fcinfo->flinfo->fn_mcxt,
      76             :                                                "SRF multi-call context",
      77             :                                                ALLOCSET_SMALL_SIZES);
      78             : 
      79             :         /*
      80             :          * Allocate suitably long-lived space and zero it
      81             :          */
      82       12705 :         retval = (FuncCallContext *)
      83             :             MemoryContextAllocZero(multi_call_ctx,
      84             :                                    sizeof(FuncCallContext));
      85             : 
      86             :         /*
      87             :          * initialize the elements
      88             :          */
      89       12705 :         retval->call_cntr = 0;
      90       12705 :         retval->max_calls = 0;
      91       12705 :         retval->slot = NULL;
      92       12705 :         retval->user_fctx = NULL;
      93       12705 :         retval->attinmeta = NULL;
      94       12705 :         retval->tuple_desc = NULL;
      95       12705 :         retval->multi_call_memory_ctx = multi_call_ctx;
      96             : 
      97             :         /*
      98             :          * save the pointer for cross-call use
      99             :          */
     100       12705 :         fcinfo->flinfo->fn_extra = retval;
     101             : 
     102             :         /*
     103             :          * Ensure we will get shut down cleanly if the exprcontext is not run
     104             :          * to completion.
     105             :          */
     106       12705 :         RegisterExprContextCallback(rsi->econtext,
     107             :                                     shutdown_MultiFuncCall,
     108       12705 :                                     PointerGetDatum(fcinfo->flinfo));
     109             :     }
     110             :     else
     111             :     {
     112             :         /* second and subsequent calls */
     113           0 :         elog(ERROR, "init_MultiFuncCall cannot be called more than once");
     114             : 
     115             :         /* never reached, but keep compiler happy */
     116             :         retval = NULL;
     117             :     }
     118             : 
     119       12705 :     return retval;
     120             : }
     121             : 
     122             : /*
     123             :  * per_MultiFuncCall
     124             :  *
     125             :  * Do Multi-function per-call setup
     126             :  */
     127             : FuncCallContext *
     128      678713 : per_MultiFuncCall(PG_FUNCTION_ARGS)
     129             : {
     130      678713 :     FuncCallContext *retval = (FuncCallContext *) fcinfo->flinfo->fn_extra;
     131             : 
     132             :     /*
     133             :      * Clear the TupleTableSlot, if present.  This is for safety's sake: the
     134             :      * Slot will be in a long-lived context (it better be, if the
     135             :      * FuncCallContext is pointing to it), but in most usage patterns the
     136             :      * tuples stored in it will be in the function's per-tuple context. So at
     137             :      * the beginning of each call, the Slot will hold a dangling pointer to an
     138             :      * already-recycled tuple.  We clear it out here.
     139             :      *
     140             :      * Note: use of retval->slot is obsolete as of 8.0, and we expect that it
     141             :      * will always be NULL.  This is just here for backwards compatibility in
     142             :      * case someone creates a slot anyway.
     143             :      */
     144      678713 :     if (retval->slot != NULL)
     145           0 :         ExecClearTuple(retval->slot);
     146             : 
     147      678713 :     return retval;
     148             : }
     149             : 
     150             : /*
     151             :  * end_MultiFuncCall
     152             :  * Clean up after init_MultiFuncCall
     153             :  */
     154             : void
     155       12690 : end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx)
     156             : {
     157       12690 :     ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
     158             : 
     159             :     /* Deregister the shutdown callback */
     160       12690 :     UnregisterExprContextCallback(rsi->econtext,
     161             :                                   shutdown_MultiFuncCall,
     162       12690 :                                   PointerGetDatum(fcinfo->flinfo));
     163             : 
     164             :     /* But use it to do the real work */
     165       12690 :     shutdown_MultiFuncCall(PointerGetDatum(fcinfo->flinfo));
     166       12690 : }
     167             : 
     168             : /*
     169             :  * shutdown_MultiFuncCall
     170             :  * Shutdown function to clean up after init_MultiFuncCall
     171             :  */
     172             : static void
     173       12698 : shutdown_MultiFuncCall(Datum arg)
     174             : {
     175       12698 :     FmgrInfo   *flinfo = (FmgrInfo *) DatumGetPointer(arg);
     176       12698 :     FuncCallContext *funcctx = (FuncCallContext *) flinfo->fn_extra;
     177             : 
     178             :     /* unbind from flinfo */
     179       12698 :     flinfo->fn_extra = NULL;
     180             : 
     181             :     /*
     182             :      * Delete context that holds all multi-call data, including the
     183             :      * FuncCallContext itself
     184             :      */
     185       12698 :     MemoryContextDelete(funcctx->multi_call_memory_ctx);
     186       12698 : }
     187             : 
     188             : 
     189             : /*
     190             :  * get_call_result_type
     191             :  *      Given a function's call info record, determine the kind of datatype
     192             :  *      it is supposed to return.  If resultTypeId isn't NULL, *resultTypeId
     193             :  *      receives the actual datatype OID (this is mainly useful for scalar
     194             :  *      result types).  If resultTupleDesc isn't NULL, *resultTupleDesc
     195             :  *      receives a pointer to a TupleDesc when the result is of a composite
     196             :  *      type, or NULL when it's a scalar result.
     197             :  *
     198             :  * One hard case that this handles is resolution of actual rowtypes for
     199             :  * functions returning RECORD (from either the function's OUT parameter
     200             :  * list, or a ReturnSetInfo context node).  TYPEFUNC_RECORD is returned
     201             :  * only when we couldn't resolve the actual rowtype for lack of information.
     202             :  *
     203             :  * The other hard case that this handles is resolution of polymorphism.
     204             :  * We will never return polymorphic pseudotypes (ANYELEMENT etc), either
     205             :  * as a scalar result type or as a component of a rowtype.
     206             :  *
     207             :  * This function is relatively expensive --- in a function returning set,
     208             :  * try to call it only the first time through.
     209             :  */
     210             : TypeFuncClass
     211        2154 : get_call_result_type(FunctionCallInfo fcinfo,
     212             :                      Oid *resultTypeId,
     213             :                      TupleDesc *resultTupleDesc)
     214             : {
     215        2154 :     return internal_get_result_type(fcinfo->flinfo->fn_oid,
     216        2154 :                                     fcinfo->flinfo->fn_expr,
     217        2154 :                                     (ReturnSetInfo *) fcinfo->resultinfo,
     218             :                                     resultTypeId,
     219             :                                     resultTupleDesc);
     220             : }
     221             : 
     222             : /*
     223             :  * get_expr_result_type
     224             :  *      As above, but work from a calling expression node tree
     225             :  */
     226             : TypeFuncClass
     227        5685 : get_expr_result_type(Node *expr,
     228             :                      Oid *resultTypeId,
     229             :                      TupleDesc *resultTupleDesc)
     230             : {
     231             :     TypeFuncClass result;
     232             : 
     233        5685 :     if (expr && IsA(expr, FuncExpr))
     234        5490 :         result = internal_get_result_type(((FuncExpr *) expr)->funcid,
     235             :                                           expr,
     236             :                                           NULL,
     237             :                                           resultTypeId,
     238             :                                           resultTupleDesc);
     239         195 :     else if (expr && IsA(expr, OpExpr))
     240           3 :         result = internal_get_result_type(get_opcode(((OpExpr *) expr)->opno),
     241             :                                           expr,
     242             :                                           NULL,
     243             :                                           resultTypeId,
     244             :                                           resultTupleDesc);
     245             :     else
     246             :     {
     247             :         /* handle as a generic expression; no chance to resolve RECORD */
     248         192 :         Oid         typid = exprType(expr);
     249             : 
     250         192 :         if (resultTypeId)
     251          40 :             *resultTypeId = typid;
     252         192 :         if (resultTupleDesc)
     253         192 :             *resultTupleDesc = NULL;
     254         192 :         result = get_type_func_class(typid);
     255         192 :         if (result == TYPEFUNC_COMPOSITE && resultTupleDesc)
     256         144 :             *resultTupleDesc = lookup_rowtype_tupdesc_copy(typid, -1);
     257             :     }
     258             : 
     259        5685 :     return result;
     260             : }
     261             : 
     262             : /*
     263             :  * get_func_result_type
     264             :  *      As above, but work from a function's OID only
     265             :  *
     266             :  * This will not be able to resolve pure-RECORD results nor polymorphism.
     267             :  */
     268             : TypeFuncClass
     269         222 : get_func_result_type(Oid functionId,
     270             :                      Oid *resultTypeId,
     271             :                      TupleDesc *resultTupleDesc)
     272             : {
     273         222 :     return internal_get_result_type(functionId,
     274             :                                     NULL,
     275             :                                     NULL,
     276             :                                     resultTypeId,
     277             :                                     resultTupleDesc);
     278             : }
     279             : 
     280             : /*
     281             :  * internal_get_result_type -- workhorse code implementing all the above
     282             :  *
     283             :  * funcid must always be supplied.  call_expr and rsinfo can be NULL if not
     284             :  * available.  We will return TYPEFUNC_RECORD, and store NULL into
     285             :  * *resultTupleDesc, if we cannot deduce the complete result rowtype from
     286             :  * the available information.
     287             :  */
     288             : static TypeFuncClass
     289        7869 : internal_get_result_type(Oid funcid,
     290             :                          Node *call_expr,
     291             :                          ReturnSetInfo *rsinfo,
     292             :                          Oid *resultTypeId,
     293             :                          TupleDesc *resultTupleDesc)
     294             : {
     295             :     TypeFuncClass result;
     296             :     HeapTuple   tp;
     297             :     Form_pg_proc procform;
     298             :     Oid         rettype;
     299             :     TupleDesc   tupdesc;
     300             : 
     301             :     /* First fetch the function's pg_proc row to inspect its rettype */
     302        7869 :     tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
     303        7869 :     if (!HeapTupleIsValid(tp))
     304           0 :         elog(ERROR, "cache lookup failed for function %u", funcid);
     305        7869 :     procform = (Form_pg_proc) GETSTRUCT(tp);
     306             : 
     307        7869 :     rettype = procform->prorettype;
     308             : 
     309             :     /* Check for OUT parameters defining a RECORD result */
     310        7869 :     tupdesc = build_function_result_tupdesc_t(tp);
     311        7869 :     if (tupdesc)
     312             :     {
     313             :         /*
     314             :          * It has OUT parameters, so it's basically like a regular composite
     315             :          * type, except we have to be able to resolve any polymorphic OUT
     316             :          * parameters.
     317             :          */
     318        3544 :         if (resultTypeId)
     319        1348 :             *resultTypeId = rettype;
     320             : 
     321        3544 :         if (resolve_polymorphic_tupdesc(tupdesc,
     322             :                                         &procform->proargtypes,
     323             :                                         call_expr))
     324             :         {
     325        7072 :             if (tupdesc->tdtypeid == RECORDOID &&
     326        3536 :                 tupdesc->tdtypmod < 0)
     327        3536 :                 assign_record_type_typmod(tupdesc);
     328        3536 :             if (resultTupleDesc)
     329        3536 :                 *resultTupleDesc = tupdesc;
     330        3536 :             result = TYPEFUNC_COMPOSITE;
     331             :         }
     332             :         else
     333             :         {
     334           8 :             if (resultTupleDesc)
     335           8 :                 *resultTupleDesc = NULL;
     336           8 :             result = TYPEFUNC_RECORD;
     337             :         }
     338             : 
     339        3544 :         ReleaseSysCache(tp);
     340             : 
     341        3544 :         return result;
     342             :     }
     343             : 
     344             :     /*
     345             :      * If scalar polymorphic result, try to resolve it.
     346             :      */
     347        4325 :     if (IsPolymorphicType(rettype))
     348             :     {
     349        1860 :         Oid         newrettype = exprType(call_expr);
     350             : 
     351        1860 :         if (newrettype == InvalidOid)   /* this probably should not happen */
     352           0 :             ereport(ERROR,
     353             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
     354             :                      errmsg("could not determine actual result type for function \"%s\" declared to return type %s",
     355             :                             NameStr(procform->proname),
     356             :                             format_type_be(rettype))));
     357        1860 :         rettype = newrettype;
     358             :     }
     359             : 
     360        4325 :     if (resultTypeId)
     361        4058 :         *resultTypeId = rettype;
     362        4325 :     if (resultTupleDesc)
     363        4325 :         *resultTupleDesc = NULL;    /* default result */
     364             : 
     365             :     /* Classify the result type */
     366        4325 :     result = get_type_func_class(rettype);
     367        4325 :     switch (result)
     368             :     {
     369             :         case TYPEFUNC_COMPOSITE:
     370        1093 :             if (resultTupleDesc)
     371        1093 :                 *resultTupleDesc = lookup_rowtype_tupdesc_copy(rettype, -1);
     372             :             /* Named composite types can't have any polymorphic columns */
     373        1093 :             break;
     374             :         case TYPEFUNC_SCALAR:
     375        3022 :             break;
     376             :         case TYPEFUNC_RECORD:
     377             :             /* We must get the tupledesc from call context */
     378         239 :             if (rsinfo && IsA(rsinfo, ReturnSetInfo) &&
     379          29 :                 rsinfo->expectedDesc != NULL)
     380             :             {
     381          29 :                 result = TYPEFUNC_COMPOSITE;
     382          29 :                 if (resultTupleDesc)
     383          29 :                     *resultTupleDesc = rsinfo->expectedDesc;
     384             :                 /* Assume no polymorphic columns here, either */
     385             :             }
     386         210 :             break;
     387             :         default:
     388           0 :             break;
     389             :     }
     390             : 
     391        4325 :     ReleaseSysCache(tp);
     392             : 
     393        4325 :     return result;
     394             : }
     395             : 
     396             : /*
     397             :  * Given the result tuple descriptor for a function with OUT parameters,
     398             :  * replace any polymorphic columns (ANYELEMENT etc) with correct data types
     399             :  * deduced from the input arguments. Returns TRUE if able to deduce all types,
     400             :  * FALSE if not.
     401             :  */
     402             : static bool
     403        3544 : resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
     404             :                             Node *call_expr)
     405             : {
     406        3544 :     int         natts = tupdesc->natts;
     407        3544 :     int         nargs = declared_args->dim1;
     408        3544 :     bool        have_anyelement_result = false;
     409        3544 :     bool        have_anyarray_result = false;
     410        3544 :     bool        have_anyrange_result = false;
     411        3544 :     bool        have_anynonarray = false;
     412        3544 :     bool        have_anyenum = false;
     413        3544 :     Oid         anyelement_type = InvalidOid;
     414        3544 :     Oid         anyarray_type = InvalidOid;
     415        3544 :     Oid         anyrange_type = InvalidOid;
     416        3544 :     Oid         anycollation = InvalidOid;
     417             :     int         i;
     418             : 
     419             :     /* See if there are any polymorphic outputs; quick out if not */
     420       16493 :     for (i = 0; i < natts; i++)
     421             :     {
     422       12949 :         switch (TupleDescAttr(tupdesc, i)->atttypid)
     423             :         {
     424             :             case ANYELEMENTOID:
     425          44 :                 have_anyelement_result = true;
     426          44 :                 break;
     427             :             case ANYARRAYOID:
     428          15 :                 have_anyarray_result = true;
     429          15 :                 break;
     430             :             case ANYNONARRAYOID:
     431           0 :                 have_anyelement_result = true;
     432           0 :                 have_anynonarray = true;
     433           0 :                 break;
     434             :             case ANYENUMOID:
     435           0 :                 have_anyelement_result = true;
     436           0 :                 have_anyenum = true;
     437           0 :                 break;
     438             :             case ANYRANGEOID:
     439          12 :                 have_anyrange_result = true;
     440          12 :                 break;
     441             :             default:
     442       12878 :                 break;
     443             :         }
     444             :     }
     445        3544 :     if (!have_anyelement_result && !have_anyarray_result &&
     446             :         !have_anyrange_result)
     447        3496 :         return true;
     448             : 
     449             :     /*
     450             :      * Otherwise, extract actual datatype(s) from input arguments.  (We assume
     451             :      * the parser already validated consistency of the arguments.)
     452             :      */
     453          48 :     if (!call_expr)
     454           8 :         return false;           /* no hope */
     455             : 
     456          83 :     for (i = 0; i < nargs; i++)
     457             :     {
     458          43 :         switch (declared_args->values[i])
     459             :         {
     460             :             case ANYELEMENTOID:
     461             :             case ANYNONARRAYOID:
     462             :             case ANYENUMOID:
     463          14 :                 if (!OidIsValid(anyelement_type))
     464          14 :                     anyelement_type = get_call_expr_argtype(call_expr, i);
     465          14 :                 break;
     466             :             case ANYARRAYOID:
     467          20 :                 if (!OidIsValid(anyarray_type))
     468          20 :                     anyarray_type = get_call_expr_argtype(call_expr, i);
     469          20 :                 break;
     470             :             case ANYRANGEOID:
     471           9 :                 if (!OidIsValid(anyrange_type))
     472           9 :                     anyrange_type = get_call_expr_argtype(call_expr, i);
     473           9 :                 break;
     474             :             default:
     475           0 :                 break;
     476             :         }
     477             :     }
     478             : 
     479             :     /* If nothing found, parser messed up */
     480          40 :     if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
     481             :         !OidIsValid(anyrange_type))
     482           0 :         return false;
     483             : 
     484             :     /* If needed, deduce one polymorphic type from others */
     485          40 :     if (have_anyelement_result && !OidIsValid(anyelement_type))
     486             :     {
     487          23 :         if (OidIsValid(anyarray_type))
     488          20 :             anyelement_type = resolve_generic_type(ANYELEMENTOID,
     489             :                                                    anyarray_type,
     490             :                                                    ANYARRAYOID);
     491          23 :         if (OidIsValid(anyrange_type))
     492             :         {
     493           3 :             Oid         subtype = resolve_generic_type(ANYELEMENTOID,
     494             :                                                        anyrange_type,
     495             :                                                        ANYRANGEOID);
     496             : 
     497             :             /* check for inconsistent array and range results */
     498           3 :             if (OidIsValid(anyelement_type) && anyelement_type != subtype)
     499           0 :                 return false;
     500           3 :             anyelement_type = subtype;
     501             :         }
     502             :     }
     503             : 
     504          40 :     if (have_anyarray_result && !OidIsValid(anyarray_type))
     505          11 :         anyarray_type = resolve_generic_type(ANYARRAYOID,
     506             :                                              anyelement_type,
     507             :                                              ANYELEMENTOID);
     508             : 
     509             :     /*
     510             :      * We can't deduce a range type from other polymorphic inputs, because
     511             :      * there may be multiple range types for the same subtype.
     512             :      */
     513          40 :     if (have_anyrange_result && !OidIsValid(anyrange_type))
     514           0 :         return false;
     515             : 
     516             :     /* Enforce ANYNONARRAY if needed */
     517          40 :     if (have_anynonarray && type_is_array(anyelement_type))
     518           0 :         return false;
     519             : 
     520             :     /* Enforce ANYENUM if needed */
     521          40 :     if (have_anyenum && !type_is_enum(anyelement_type))
     522           0 :         return false;
     523             : 
     524             :     /*
     525             :      * Identify the collation to use for polymorphic OUT parameters. (It'll
     526             :      * necessarily be the same for both anyelement and anyarray.)  Note that
     527             :      * range types are not collatable, so any possible internal collation of a
     528             :      * range type is not considered here.
     529             :      */
     530          40 :     if (OidIsValid(anyelement_type))
     531          37 :         anycollation = get_typcollation(anyelement_type);
     532           3 :     else if (OidIsValid(anyarray_type))
     533           0 :         anycollation = get_typcollation(anyarray_type);
     534             : 
     535          40 :     if (OidIsValid(anycollation))
     536             :     {
     537             :         /*
     538             :          * The types are collatable, so consider whether to use a nondefault
     539             :          * collation.  We do so if we can identify the input collation used
     540             :          * for the function.
     541             :          */
     542           7 :         Oid         inputcollation = exprInputCollation(call_expr);
     543             : 
     544           7 :         if (OidIsValid(inputcollation))
     545           7 :             anycollation = inputcollation;
     546             :     }
     547             : 
     548             :     /* And finally replace the tuple column types as needed */
     549         120 :     for (i = 0; i < natts; i++)
     550             :     {
     551          80 :         Form_pg_attribute att = TupleDescAttr(tupdesc, i);
     552             : 
     553          80 :         switch (att->atttypid)
     554             :         {
     555             :             case ANYELEMENTOID:
     556             :             case ANYNONARRAYOID:
     557             :             case ANYENUMOID:
     558          37 :                 TupleDescInitEntry(tupdesc, i + 1,
     559          37 :                                    NameStr(att->attname),
     560             :                                    anyelement_type,
     561             :                                    -1,
     562             :                                    0);
     563          37 :                 TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
     564          37 :                 break;
     565             :             case ANYARRAYOID:
     566          11 :                 TupleDescInitEntry(tupdesc, i + 1,
     567          11 :                                    NameStr(att->attname),
     568             :                                    anyarray_type,
     569             :                                    -1,
     570             :                                    0);
     571          11 :                 TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
     572          11 :                 break;
     573             :             case ANYRANGEOID:
     574           9 :                 TupleDescInitEntry(tupdesc, i + 1,
     575           9 :                                    NameStr(att->attname),
     576             :                                    anyrange_type,
     577             :                                    -1,
     578             :                                    0);
     579             :                 /* no collation should be attached to a range type */
     580           9 :                 break;
     581             :             default:
     582          23 :                 break;
     583             :         }
     584             :     }
     585             : 
     586          40 :     return true;
     587             : }
     588             : 
     589             : /*
     590             :  * Given the declared argument types and modes for a function, replace any
     591             :  * polymorphic types (ANYELEMENT etc) with correct data types deduced from the
     592             :  * input arguments.  Returns TRUE if able to deduce all types, FALSE if not.
     593             :  * This is the same logic as resolve_polymorphic_tupdesc, but with a different
     594             :  * argument representation.
     595             :  *
     596             :  * argmodes may be NULL, in which case all arguments are assumed to be IN mode.
     597             :  */
     598             : bool
     599         653 : resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
     600             :                              Node *call_expr)
     601             : {
     602         653 :     bool        have_anyelement_result = false;
     603         653 :     bool        have_anyarray_result = false;
     604         653 :     bool        have_anyrange_result = false;
     605         653 :     Oid         anyelement_type = InvalidOid;
     606         653 :     Oid         anyarray_type = InvalidOid;
     607         653 :     Oid         anyrange_type = InvalidOid;
     608             :     int         inargno;
     609             :     int         i;
     610             : 
     611             :     /* First pass: resolve polymorphic inputs, check for outputs */
     612         653 :     inargno = 0;
     613        1579 :     for (i = 0; i < numargs; i++)
     614             :     {
     615         926 :         char        argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
     616             : 
     617         926 :         switch (argtypes[i])
     618             :         {
     619             :             case ANYELEMENTOID:
     620             :             case ANYNONARRAYOID:
     621             :             case ANYENUMOID:
     622          38 :                 if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
     623           1 :                     have_anyelement_result = true;
     624             :                 else
     625             :                 {
     626          37 :                     if (!OidIsValid(anyelement_type))
     627             :                     {
     628          37 :                         anyelement_type = get_call_expr_argtype(call_expr,
     629             :                                                                 inargno);
     630          37 :                         if (!OidIsValid(anyelement_type))
     631           0 :                             return false;
     632             :                     }
     633          37 :                     argtypes[i] = anyelement_type;
     634             :                 }
     635          38 :                 break;
     636             :             case ANYARRAYOID:
     637          21 :                 if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
     638           1 :                     have_anyarray_result = true;
     639             :                 else
     640             :                 {
     641          20 :                     if (!OidIsValid(anyarray_type))
     642             :                     {
     643          20 :                         anyarray_type = get_call_expr_argtype(call_expr,
     644             :                                                               inargno);
     645          20 :                         if (!OidIsValid(anyarray_type))
     646           0 :                             return false;
     647             :                     }
     648          20 :                     argtypes[i] = anyarray_type;
     649             :                 }
     650          21 :                 break;
     651             :             case ANYRANGEOID:
     652           0 :                 if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
     653           0 :                     have_anyrange_result = true;
     654             :                 else
     655             :                 {
     656           0 :                     if (!OidIsValid(anyrange_type))
     657             :                     {
     658           0 :                         anyrange_type = get_call_expr_argtype(call_expr,
     659             :                                                               inargno);
     660           0 :                         if (!OidIsValid(anyrange_type))
     661           0 :                             return false;
     662             :                     }
     663           0 :                     argtypes[i] = anyrange_type;
     664             :                 }
     665           0 :                 break;
     666             :             default:
     667         867 :                 break;
     668             :         }
     669         926 :         if (argmode != PROARGMODE_OUT && argmode != PROARGMODE_TABLE)
     670         924 :             inargno++;
     671             :     }
     672             : 
     673             :     /* Done? */
     674         653 :     if (!have_anyelement_result && !have_anyarray_result &&
     675             :         !have_anyrange_result)
     676         652 :         return true;
     677             : 
     678             :     /* If no input polymorphics, parser messed up */
     679           1 :     if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
     680             :         !OidIsValid(anyrange_type))
     681           0 :         return false;
     682             : 
     683             :     /* If needed, deduce one polymorphic type from others */
     684           1 :     if (have_anyelement_result && !OidIsValid(anyelement_type))
     685             :     {
     686           0 :         if (OidIsValid(anyarray_type))
     687           0 :             anyelement_type = resolve_generic_type(ANYELEMENTOID,
     688             :                                                    anyarray_type,
     689             :                                                    ANYARRAYOID);
     690           0 :         if (OidIsValid(anyrange_type))
     691             :         {
     692           0 :             Oid         subtype = resolve_generic_type(ANYELEMENTOID,
     693             :                                                        anyrange_type,
     694             :                                                        ANYRANGEOID);
     695             : 
     696             :             /* check for inconsistent array and range results */
     697           0 :             if (OidIsValid(anyelement_type) && anyelement_type != subtype)
     698           0 :                 return false;
     699           0 :             anyelement_type = subtype;
     700             :         }
     701             :     }
     702             : 
     703           1 :     if (have_anyarray_result && !OidIsValid(anyarray_type))
     704           1 :         anyarray_type = resolve_generic_type(ANYARRAYOID,
     705             :                                              anyelement_type,
     706             :                                              ANYELEMENTOID);
     707             : 
     708             :     /*
     709             :      * We can't deduce a range type from other polymorphic inputs, because
     710             :      * there may be multiple range types for the same subtype.
     711             :      */
     712           1 :     if (have_anyrange_result && !OidIsValid(anyrange_type))
     713           0 :         return false;
     714             : 
     715             :     /* XXX do we need to enforce ANYNONARRAY or ANYENUM here?  I think not */
     716             : 
     717             :     /* And finally replace the output column types as needed */
     718           4 :     for (i = 0; i < numargs; i++)
     719             :     {
     720           3 :         switch (argtypes[i])
     721             :         {
     722             :             case ANYELEMENTOID:
     723             :             case ANYNONARRAYOID:
     724             :             case ANYENUMOID:
     725           1 :                 argtypes[i] = anyelement_type;
     726           1 :                 break;
     727             :             case ANYARRAYOID:
     728           1 :                 argtypes[i] = anyarray_type;
     729           1 :                 break;
     730             :             case ANYRANGEOID:
     731           0 :                 argtypes[i] = anyrange_type;
     732           0 :                 break;
     733             :             default:
     734           1 :                 break;
     735             :         }
     736             :     }
     737             : 
     738           1 :     return true;
     739             : }
     740             : 
     741             : /*
     742             :  * get_type_func_class
     743             :  *      Given the type OID, obtain its TYPEFUNC classification.
     744             :  *
     745             :  * This is intended to centralize a bunch of formerly ad-hoc code for
     746             :  * classifying types.  The categories used here are useful for deciding
     747             :  * how to handle functions returning the datatype.
     748             :  */
     749             : static TypeFuncClass
     750        4517 : get_type_func_class(Oid typid)
     751             : {
     752        4517 :     switch (get_typtype(typid))
     753             :     {
     754             :         case TYPTYPE_COMPOSITE:
     755        1237 :             return TYPEFUNC_COMPOSITE;
     756             :         case TYPTYPE_BASE:
     757             :         case TYPTYPE_DOMAIN:
     758             :         case TYPTYPE_ENUM:
     759             :         case TYPTYPE_RANGE:
     760        3049 :             return TYPEFUNC_SCALAR;
     761             :         case TYPTYPE_PSEUDO:
     762         231 :             if (typid == RECORDOID)
     763         218 :                 return TYPEFUNC_RECORD;
     764             : 
     765             :             /*
     766             :              * We treat VOID and CSTRING as legitimate scalar datatypes,
     767             :              * mostly for the convenience of the JDBC driver (which wants to
     768             :              * be able to do "SELECT * FROM foo()" for all legitimately
     769             :              * user-callable functions).
     770             :              */
     771          13 :             if (typid == VOIDOID || typid == CSTRINGOID)
     772          13 :                 return TYPEFUNC_SCALAR;
     773           0 :             return TYPEFUNC_OTHER;
     774             :     }
     775             :     /* shouldn't get here, probably */
     776           0 :     return TYPEFUNC_OTHER;
     777             : }
     778             : 
     779             : 
     780             : /*
     781             :  * get_func_arg_info
     782             :  *
     783             :  * Fetch info about the argument types, names, and IN/OUT modes from the
     784             :  * pg_proc tuple.  Return value is the total number of arguments.
     785             :  * Other results are palloc'd.  *p_argtypes is always filled in, but
     786             :  * *p_argnames and *p_argmodes will be set NULL in the default cases
     787             :  * (no names, and all IN arguments, respectively).
     788             :  *
     789             :  * Note that this function simply fetches what is in the pg_proc tuple;
     790             :  * it doesn't do any interpretation of polymorphic types.
     791             :  */
     792             : int
     793         688 : get_func_arg_info(HeapTuple procTup,
     794             :                   Oid **p_argtypes, char ***p_argnames, char **p_argmodes)
     795             : {
     796         688 :     Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
     797             :     Datum       proallargtypes;
     798             :     Datum       proargmodes;
     799             :     Datum       proargnames;
     800             :     bool        isNull;
     801             :     ArrayType  *arr;
     802             :     int         numargs;
     803             :     Datum      *elems;
     804             :     int         nelems;
     805             :     int         i;
     806             : 
     807             :     /* First discover the total number of parameters and get their types */
     808         688 :     proallargtypes = SysCacheGetAttr(PROCOID, procTup,
     809             :                                      Anum_pg_proc_proallargtypes,
     810             :                                      &isNull);
     811         688 :     if (!isNull)
     812             :     {
     813             :         /*
     814             :          * We expect the arrays to be 1-D arrays of the right types; verify
     815             :          * that.  For the OID and char arrays, we don't need to use
     816             :          * deconstruct_array() since the array data is just going to look like
     817             :          * a C array of values.
     818             :          */
     819          56 :         arr = DatumGetArrayTypeP(proallargtypes);   /* ensure not toasted */
     820          56 :         numargs = ARR_DIMS(arr)[0];
     821          56 :         if (ARR_NDIM(arr) != 1 ||
     822          56 :             numargs < 0 ||
     823         112 :             ARR_HASNULL(arr) ||
     824          56 :             ARR_ELEMTYPE(arr) != OIDOID)
     825           0 :             elog(ERROR, "proallargtypes is not a 1-D Oid array");
     826          56 :         Assert(numargs >= procStruct->pronargs);
     827          56 :         *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid));
     828          56 :         memcpy(*p_argtypes, ARR_DATA_PTR(arr),
     829             :                numargs * sizeof(Oid));
     830             :     }
     831             :     else
     832             :     {
     833             :         /* If no proallargtypes, use proargtypes */
     834         632 :         numargs = procStruct->proargtypes.dim1;
     835         632 :         Assert(numargs == procStruct->pronargs);
     836         632 :         *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid));
     837         632 :         memcpy(*p_argtypes, procStruct->proargtypes.values,
     838             :                numargs * sizeof(Oid));
     839             :     }
     840             : 
     841             :     /* Get argument names, if available */
     842         688 :     proargnames = SysCacheGetAttr(PROCOID, procTup,
     843             :                                   Anum_pg_proc_proargnames,
     844             :                                   &isNull);
     845         688 :     if (isNull)
     846         535 :         *p_argnames = NULL;
     847             :     else
     848             :     {
     849         153 :         deconstruct_array(DatumGetArrayTypeP(proargnames),
     850             :                           TEXTOID, -1, false, 'i',
     851             :                           &elems, NULL, &nelems);
     852         153 :         if (nelems != numargs)  /* should not happen */
     853           0 :             elog(ERROR, "proargnames must have the same number of elements as the function has arguments");
     854         153 :         *p_argnames = (char **) palloc(sizeof(char *) * numargs);
     855         578 :         for (i = 0; i < numargs; i++)
     856         425 :             (*p_argnames)[i] = TextDatumGetCString(elems[i]);
     857             :     }
     858             : 
     859             :     /* Get argument modes, if available */
     860         688 :     proargmodes = SysCacheGetAttr(PROCOID, procTup,
     861             :                                   Anum_pg_proc_proargmodes,
     862             :                                   &isNull);
     863         688 :     if (isNull)
     864         632 :         *p_argmodes = NULL;
     865             :     else
     866             :     {
     867          56 :         arr = DatumGetArrayTypeP(proargmodes);  /* ensure not toasted */
     868         112 :         if (ARR_NDIM(arr) != 1 ||
     869         112 :             ARR_DIMS(arr)[0] != numargs ||
     870         112 :             ARR_HASNULL(arr) ||
     871          56 :             ARR_ELEMTYPE(arr) != CHAROID)
     872           0 :             elog(ERROR, "proargmodes is not a 1-D char array");
     873          56 :         *p_argmodes = (char *) palloc(numargs * sizeof(char));
     874          56 :         memcpy(*p_argmodes, ARR_DATA_PTR(arr),
     875             :                numargs * sizeof(char));
     876             :     }
     877             : 
     878         688 :     return numargs;
     879             : }
     880             : 
     881             : /*
     882             :  * get_func_trftypes
     883             :  *
     884             :  * Returns the number of transformed types used by function.
     885             :  */
     886             : int
     887           0 : get_func_trftypes(HeapTuple procTup,
     888             :                   Oid **p_trftypes)
     889             : {
     890             :     Datum       protrftypes;
     891             :     ArrayType  *arr;
     892             :     int         nelems;
     893             :     bool        isNull;
     894             : 
     895           0 :     protrftypes = SysCacheGetAttr(PROCOID, procTup,
     896             :                                   Anum_pg_proc_protrftypes,
     897             :                                   &isNull);
     898           0 :     if (!isNull)
     899             :     {
     900             :         /*
     901             :          * We expect the arrays to be 1-D arrays of the right types; verify
     902             :          * that.  For the OID and char arrays, we don't need to use
     903             :          * deconstruct_array() since the array data is just going to look like
     904             :          * a C array of values.
     905             :          */
     906           0 :         arr = DatumGetArrayTypeP(protrftypes);  /* ensure not toasted */
     907           0 :         nelems = ARR_DIMS(arr)[0];
     908           0 :         if (ARR_NDIM(arr) != 1 ||
     909           0 :             nelems < 0 ||
     910           0 :             ARR_HASNULL(arr) ||
     911           0 :             ARR_ELEMTYPE(arr) != OIDOID)
     912           0 :             elog(ERROR, "protrftypes is not a 1-D Oid array");
     913           0 :         Assert(nelems >= ((Form_pg_proc) GETSTRUCT(procTup))->pronargs);
     914           0 :         *p_trftypes = (Oid *) palloc(nelems * sizeof(Oid));
     915           0 :         memcpy(*p_trftypes, ARR_DATA_PTR(arr),
     916             :                nelems * sizeof(Oid));
     917             : 
     918           0 :         return nelems;
     919             :     }
     920             :     else
     921           0 :         return 0;
     922             : }
     923             : 
     924             : /*
     925             :  * get_func_input_arg_names
     926             :  *
     927             :  * Extract the names of input arguments only, given a function's
     928             :  * proargnames and proargmodes entries in Datum form.
     929             :  *
     930             :  * Returns the number of input arguments, which is the length of the
     931             :  * palloc'd array returned to *arg_names.  Entries for unnamed args
     932             :  * are set to NULL.  You don't get anything if proargnames is NULL.
     933             :  */
     934             : int
     935        1080 : get_func_input_arg_names(Datum proargnames, Datum proargmodes,
     936             :                          char ***arg_names)
     937             : {
     938             :     ArrayType  *arr;
     939             :     int         numargs;
     940             :     Datum      *argnames;
     941             :     char       *argmodes;
     942             :     char      **inargnames;
     943             :     int         numinargs;
     944             :     int         i;
     945             : 
     946             :     /* Do nothing if null proargnames */
     947        1080 :     if (proargnames == PointerGetDatum(NULL))
     948             :     {
     949         845 :         *arg_names = NULL;
     950         845 :         return 0;
     951             :     }
     952             : 
     953             :     /*
     954             :      * We expect the arrays to be 1-D arrays of the right types; verify that.
     955             :      * For proargmodes, we don't need to use deconstruct_array() since the
     956             :      * array data is just going to look like a C array of values.
     957             :      */
     958         235 :     arr = DatumGetArrayTypeP(proargnames);  /* ensure not toasted */
     959         470 :     if (ARR_NDIM(arr) != 1 ||
     960         470 :         ARR_HASNULL(arr) ||
     961         235 :         ARR_ELEMTYPE(arr) != TEXTOID)
     962           0 :         elog(ERROR, "proargnames is not a 1-D text array");
     963         235 :     deconstruct_array(arr, TEXTOID, -1, false, 'i',
     964             :                       &argnames, NULL, &numargs);
     965         235 :     if (proargmodes != PointerGetDatum(NULL))
     966             :     {
     967         111 :         arr = DatumGetArrayTypeP(proargmodes);  /* ensure not toasted */
     968         222 :         if (ARR_NDIM(arr) != 1 ||
     969         222 :             ARR_DIMS(arr)[0] != numargs ||
     970         222 :             ARR_HASNULL(arr) ||
     971         111 :             ARR_ELEMTYPE(arr) != CHAROID)
     972           0 :             elog(ERROR, "proargmodes is not a 1-D char array");
     973         111 :         argmodes = (char *) ARR_DATA_PTR(arr);
     974             :     }
     975             :     else
     976         124 :         argmodes = NULL;
     977             : 
     978             :     /* zero elements probably shouldn't happen, but handle it gracefully */
     979         235 :     if (numargs <= 0)
     980             :     {
     981           0 :         *arg_names = NULL;
     982           0 :         return 0;
     983             :     }
     984             : 
     985             :     /* extract input-argument names */
     986         235 :     inargnames = (char **) palloc(numargs * sizeof(char *));
     987         235 :     numinargs = 0;
     988         896 :     for (i = 0; i < numargs; i++)
     989             :     {
     990        1096 :         if (argmodes == NULL ||
     991         688 :             argmodes[i] == PROARGMODE_IN ||
     992         498 :             argmodes[i] == PROARGMODE_INOUT ||
     993         245 :             argmodes[i] == PROARGMODE_VARIADIC)
     994             :         {
     995         435 :             char       *pname = TextDatumGetCString(argnames[i]);
     996             : 
     997         435 :             if (pname[0] != '\0')
     998         422 :                 inargnames[numinargs] = pname;
     999             :             else
    1000          13 :                 inargnames[numinargs] = NULL;
    1001         435 :             numinargs++;
    1002             :         }
    1003             :     }
    1004             : 
    1005         235 :     *arg_names = inargnames;
    1006         235 :     return numinargs;
    1007             : }
    1008             : 
    1009             : 
    1010             : /*
    1011             :  * get_func_result_name
    1012             :  *
    1013             :  * If the function has exactly one output parameter, and that parameter
    1014             :  * is named, return the name (as a palloc'd string).  Else return NULL.
    1015             :  *
    1016             :  * This is used to determine the default output column name for functions
    1017             :  * returning scalar types.
    1018             :  */
    1019             : char *
    1020         770 : get_func_result_name(Oid functionId)
    1021             : {
    1022             :     char       *result;
    1023             :     HeapTuple   procTuple;
    1024             :     Datum       proargmodes;
    1025             :     Datum       proargnames;
    1026             :     bool        isnull;
    1027             :     ArrayType  *arr;
    1028             :     int         numargs;
    1029             :     char       *argmodes;
    1030             :     Datum      *argnames;
    1031             :     int         numoutargs;
    1032             :     int         nargnames;
    1033             :     int         i;
    1034             : 
    1035             :     /* First fetch the function's pg_proc row */
    1036         770 :     procTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionId));
    1037         770 :     if (!HeapTupleIsValid(procTuple))
    1038           0 :         elog(ERROR, "cache lookup failed for function %u", functionId);
    1039             : 
    1040             :     /* If there are no named OUT parameters, return NULL */
    1041         787 :     if (heap_attisnull(procTuple, Anum_pg_proc_proargmodes) ||
    1042          17 :         heap_attisnull(procTuple, Anum_pg_proc_proargnames))
    1043         753 :         result = NULL;
    1044             :     else
    1045             :     {
    1046             :         /* Get the data out of the tuple */
    1047          17 :         proargmodes = SysCacheGetAttr(PROCOID, procTuple,
    1048             :                                       Anum_pg_proc_proargmodes,
    1049             :                                       &isnull);
    1050          17 :         Assert(!isnull);
    1051          17 :         proargnames = SysCacheGetAttr(PROCOID, procTuple,
    1052             :                                       Anum_pg_proc_proargnames,
    1053             :                                       &isnull);
    1054          17 :         Assert(!isnull);
    1055             : 
    1056             :         /*
    1057             :          * We expect the arrays to be 1-D arrays of the right types; verify
    1058             :          * that.  For the char array, we don't need to use deconstruct_array()
    1059             :          * since the array data is just going to look like a C array of
    1060             :          * values.
    1061             :          */
    1062          17 :         arr = DatumGetArrayTypeP(proargmodes);  /* ensure not toasted */
    1063          17 :         numargs = ARR_DIMS(arr)[0];
    1064          17 :         if (ARR_NDIM(arr) != 1 ||
    1065          17 :             numargs < 0 ||
    1066          34 :             ARR_HASNULL(arr) ||
    1067          17 :             ARR_ELEMTYPE(arr) != CHAROID)
    1068           0 :             elog(ERROR, "proargmodes is not a 1-D char array");
    1069          17 :         argmodes = (char *) ARR_DATA_PTR(arr);
    1070          17 :         arr = DatumGetArrayTypeP(proargnames);  /* ensure not toasted */
    1071          34 :         if (ARR_NDIM(arr) != 1 ||
    1072          34 :             ARR_DIMS(arr)[0] != numargs ||
    1073          34 :             ARR_HASNULL(arr) ||
    1074          17 :             ARR_ELEMTYPE(arr) != TEXTOID)
    1075           0 :             elog(ERROR, "proargnames is not a 1-D text array");
    1076          17 :         deconstruct_array(arr, TEXTOID, -1, false, 'i',
    1077             :                           &argnames, NULL, &nargnames);
    1078          17 :         Assert(nargnames == numargs);
    1079             : 
    1080             :         /* scan for output argument(s) */
    1081          17 :         result = NULL;
    1082          17 :         numoutargs = 0;
    1083          48 :         for (i = 0; i < numargs; i++)
    1084             :         {
    1085          48 :             if (argmodes[i] == PROARGMODE_IN ||
    1086          17 :                 argmodes[i] == PROARGMODE_VARIADIC)
    1087          14 :                 continue;
    1088          17 :             Assert(argmodes[i] == PROARGMODE_OUT ||
    1089             :                    argmodes[i] == PROARGMODE_INOUT ||
    1090             :                    argmodes[i] == PROARGMODE_TABLE);
    1091          17 :             if (++numoutargs > 1)
    1092             :             {
    1093             :                 /* multiple out args, so forget it */
    1094           0 :                 result = NULL;
    1095           0 :                 break;
    1096             :             }
    1097          17 :             result = TextDatumGetCString(argnames[i]);
    1098          17 :             if (result == NULL || result[0] == '\0')
    1099             :             {
    1100             :                 /* Parameter is not named, so forget it */
    1101           0 :                 result = NULL;
    1102           0 :                 break;
    1103             :             }
    1104             :         }
    1105             :     }
    1106             : 
    1107         770 :     ReleaseSysCache(procTuple);
    1108             : 
    1109         770 :     return result;
    1110             : }
    1111             : 
    1112             : 
    1113             : /*
    1114             :  * build_function_result_tupdesc_t
    1115             :  *
    1116             :  * Given a pg_proc row for a function, return a tuple descriptor for the
    1117             :  * result rowtype, or NULL if the function does not have OUT parameters.
    1118             :  *
    1119             :  * Note that this does not handle resolution of polymorphic types;
    1120             :  * that is deliberate.
    1121             :  */
    1122             : TupleDesc
    1123        7883 : build_function_result_tupdesc_t(HeapTuple procTuple)
    1124             : {
    1125        7883 :     Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(procTuple);
    1126             :     Datum       proallargtypes;
    1127             :     Datum       proargmodes;
    1128             :     Datum       proargnames;
    1129             :     bool        isnull;
    1130             : 
    1131             :     /* Return NULL if the function isn't declared to return RECORD */
    1132        7883 :     if (procform->prorettype != RECORDOID)
    1133        4115 :         return NULL;
    1134             : 
    1135             :     /* If there are no OUT parameters, return NULL */
    1136        7325 :     if (heap_attisnull(procTuple, Anum_pg_proc_proallargtypes) ||
    1137        3557 :         heap_attisnull(procTuple, Anum_pg_proc_proargmodes))
    1138         211 :         return NULL;
    1139             : 
    1140             :     /* Get the data out of the tuple */
    1141        3557 :     proallargtypes = SysCacheGetAttr(PROCOID, procTuple,
    1142             :                                      Anum_pg_proc_proallargtypes,
    1143             :                                      &isnull);
    1144        3557 :     Assert(!isnull);
    1145        3557 :     proargmodes = SysCacheGetAttr(PROCOID, procTuple,
    1146             :                                   Anum_pg_proc_proargmodes,
    1147             :                                   &isnull);
    1148        3557 :     Assert(!isnull);
    1149        3557 :     proargnames = SysCacheGetAttr(PROCOID, procTuple,
    1150             :                                   Anum_pg_proc_proargnames,
    1151             :                                   &isnull);
    1152        3557 :     if (isnull)
    1153          14 :         proargnames = PointerGetDatum(NULL);    /* just to be sure */
    1154             : 
    1155        3557 :     return build_function_result_tupdesc_d(proallargtypes,
    1156             :                                            proargmodes,
    1157             :                                            proargnames);
    1158             : }
    1159             : 
    1160             : /*
    1161             :  * build_function_result_tupdesc_d
    1162             :  *
    1163             :  * Build a RECORD function's tupledesc from the pg_proc proallargtypes,
    1164             :  * proargmodes, and proargnames arrays.  This is split out for the
    1165             :  * convenience of ProcedureCreate, which needs to be able to compute the
    1166             :  * tupledesc before actually creating the function.
    1167             :  *
    1168             :  * Returns NULL if there are not at least two OUT or INOUT arguments.
    1169             :  */
    1170             : TupleDesc
    1171        3571 : build_function_result_tupdesc_d(Datum proallargtypes,
    1172             :                                 Datum proargmodes,
    1173             :                                 Datum proargnames)
    1174             : {
    1175             :     TupleDesc   desc;
    1176             :     ArrayType  *arr;
    1177             :     int         numargs;
    1178             :     Oid        *argtypes;
    1179             :     char       *argmodes;
    1180        3571 :     Datum      *argnames = NULL;
    1181             :     Oid        *outargtypes;
    1182             :     char      **outargnames;
    1183             :     int         numoutargs;
    1184             :     int         nargnames;
    1185             :     int         i;
    1186             : 
    1187             :     /* Can't have output args if columns are null */
    1188        3571 :     if (proallargtypes == PointerGetDatum(NULL) ||
    1189             :         proargmodes == PointerGetDatum(NULL))
    1190           1 :         return NULL;
    1191             : 
    1192             :     /*
    1193             :      * We expect the arrays to be 1-D arrays of the right types; verify that.
    1194             :      * For the OID and char arrays, we don't need to use deconstruct_array()
    1195             :      * since the array data is just going to look like a C array of values.
    1196             :      */
    1197        3570 :     arr = DatumGetArrayTypeP(proallargtypes);   /* ensure not toasted */
    1198        3570 :     numargs = ARR_DIMS(arr)[0];
    1199        3570 :     if (ARR_NDIM(arr) != 1 ||
    1200        3570 :         numargs < 0 ||
    1201        7140 :         ARR_HASNULL(arr) ||
    1202        3570 :         ARR_ELEMTYPE(arr) != OIDOID)
    1203           0 :         elog(ERROR, "proallargtypes is not a 1-D Oid array");
    1204        3570 :     argtypes = (Oid *) ARR_DATA_PTR(arr);
    1205        3570 :     arr = DatumGetArrayTypeP(proargmodes);  /* ensure not toasted */
    1206        7140 :     if (ARR_NDIM(arr) != 1 ||
    1207        7140 :         ARR_DIMS(arr)[0] != numargs ||
    1208        7140 :         ARR_HASNULL(arr) ||
    1209        3570 :         ARR_ELEMTYPE(arr) != CHAROID)
    1210           0 :         elog(ERROR, "proargmodes is not a 1-D char array");
    1211        3570 :     argmodes = (char *) ARR_DATA_PTR(arr);
    1212        3570 :     if (proargnames != PointerGetDatum(NULL))
    1213             :     {
    1214        3554 :         arr = DatumGetArrayTypeP(proargnames);  /* ensure not toasted */
    1215        7108 :         if (ARR_NDIM(arr) != 1 ||
    1216        7108 :             ARR_DIMS(arr)[0] != numargs ||
    1217        7108 :             ARR_HASNULL(arr) ||
    1218        3554 :             ARR_ELEMTYPE(arr) != TEXTOID)
    1219           0 :             elog(ERROR, "proargnames is not a 1-D text array");
    1220        3554 :         deconstruct_array(arr, TEXTOID, -1, false, 'i',
    1221             :                           &argnames, NULL, &nargnames);
    1222        3554 :         Assert(nargnames == numargs);
    1223             :     }
    1224             : 
    1225             :     /* zero elements probably shouldn't happen, but handle it gracefully */
    1226        3570 :     if (numargs <= 0)
    1227           0 :         return NULL;
    1228             : 
    1229             :     /* extract output-argument types and names */
    1230        3570 :     outargtypes = (Oid *) palloc(numargs * sizeof(Oid));
    1231        3570 :     outargnames = (char **) palloc(numargs * sizeof(char *));
    1232        3570 :     numoutargs = 0;
    1233       19960 :     for (i = 0; i < numargs; i++)
    1234             :     {
    1235             :         char       *pname;
    1236             : 
    1237       29409 :         if (argmodes[i] == PROARGMODE_IN ||
    1238       13019 :             argmodes[i] == PROARGMODE_VARIADIC)
    1239        3379 :             continue;
    1240       13011 :         Assert(argmodes[i] == PROARGMODE_OUT ||
    1241             :                argmodes[i] == PROARGMODE_INOUT ||
    1242             :                argmodes[i] == PROARGMODE_TABLE);
    1243       13011 :         outargtypes[numoutargs] = argtypes[i];
    1244       13011 :         if (argnames)
    1245       12979 :             pname = TextDatumGetCString(argnames[i]);
    1246             :         else
    1247          32 :             pname = NULL;
    1248       13011 :         if (pname == NULL || pname[0] == '\0')
    1249             :         {
    1250             :             /* Parameter is not named, so gin up a column name */
    1251          52 :             pname = psprintf("column%d", numoutargs + 1);
    1252             :         }
    1253       13011 :         outargnames[numoutargs] = pname;
    1254       13011 :         numoutargs++;
    1255             :     }
    1256             : 
    1257             :     /*
    1258             :      * If there is no output argument, or only one, the function does not
    1259             :      * return tuples.
    1260             :      */
    1261        3570 :     if (numoutargs < 2)
    1262           0 :         return NULL;
    1263             : 
    1264        3570 :     desc = CreateTemplateTupleDesc(numoutargs, false);
    1265       16581 :     for (i = 0; i < numoutargs; i++)
    1266             :     {
    1267       26022 :         TupleDescInitEntry(desc, i + 1,
    1268       13011 :                            outargnames[i],
    1269       13011 :                            outargtypes[i],
    1270             :                            -1,
    1271             :                            0);
    1272             :     }
    1273             : 
    1274        3570 :     return desc;
    1275             : }
    1276             : 
    1277             : 
    1278             : /*
    1279             :  * RelationNameGetTupleDesc
    1280             :  *
    1281             :  * Given a (possibly qualified) relation name, build a TupleDesc.
    1282             :  *
    1283             :  * Note: while this works as advertised, it's seldom the best way to
    1284             :  * build a tupdesc for a function's result type.  It's kept around
    1285             :  * only for backwards compatibility with existing user-written code.
    1286             :  */
    1287             : TupleDesc
    1288           0 : RelationNameGetTupleDesc(const char *relname)
    1289             : {
    1290             :     RangeVar   *relvar;
    1291             :     Relation    rel;
    1292             :     TupleDesc   tupdesc;
    1293             :     List       *relname_list;
    1294             : 
    1295             :     /* Open relation and copy the tuple description */
    1296           0 :     relname_list = stringToQualifiedNameList(relname);
    1297           0 :     relvar = makeRangeVarFromNameList(relname_list);
    1298           0 :     rel = relation_openrv(relvar, AccessShareLock);
    1299           0 :     tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
    1300           0 :     relation_close(rel, AccessShareLock);
    1301             : 
    1302           0 :     return tupdesc;
    1303             : }
    1304             : 
    1305             : /*
    1306             :  * TypeGetTupleDesc
    1307             :  *
    1308             :  * Given a type Oid, build a TupleDesc.  (In most cases you should be
    1309             :  * using get_call_result_type or one of its siblings instead of this
    1310             :  * routine, so that you can handle OUT parameters, RECORD result type,
    1311             :  * and polymorphic results.)
    1312             :  *
    1313             :  * If the type is composite, *and* a colaliases List is provided, *and*
    1314             :  * the List is of natts length, use the aliases instead of the relation
    1315             :  * attnames.  (NB: this usage is deprecated since it may result in
    1316             :  * creation of unnecessary transient record types.)
    1317             :  *
    1318             :  * If the type is a base type, a single item alias List is required.
    1319             :  */
    1320             : TupleDesc
    1321           0 : TypeGetTupleDesc(Oid typeoid, List *colaliases)
    1322             : {
    1323           0 :     TypeFuncClass functypclass = get_type_func_class(typeoid);
    1324           0 :     TupleDesc   tupdesc = NULL;
    1325             : 
    1326             :     /*
    1327             :      * Build a suitable tupledesc representing the output rows
    1328             :      */
    1329           0 :     if (functypclass == TYPEFUNC_COMPOSITE)
    1330             :     {
    1331             :         /* Composite data type, e.g. a table's row type */
    1332           0 :         tupdesc = lookup_rowtype_tupdesc_copy(typeoid, -1);
    1333             : 
    1334           0 :         if (colaliases != NIL)
    1335             :         {
    1336           0 :             int         natts = tupdesc->natts;
    1337             :             int         varattno;
    1338             : 
    1339             :             /* does the list length match the number of attributes? */
    1340           0 :             if (list_length(colaliases) != natts)
    1341           0 :                 ereport(ERROR,
    1342             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
    1343             :                          errmsg("number of aliases does not match number of columns")));
    1344             : 
    1345             :             /* OK, use the aliases instead */
    1346           0 :             for (varattno = 0; varattno < natts; varattno++)
    1347             :             {
    1348           0 :                 char       *label = strVal(list_nth(colaliases, varattno));
    1349           0 :                 Form_pg_attribute attr = TupleDescAttr(tupdesc, varattno);
    1350             : 
    1351           0 :                 if (label != NULL)
    1352           0 :                     namestrcpy(&(attr->attname), label);
    1353             :             }
    1354             : 
    1355             :             /* The tuple type is now an anonymous record type */
    1356           0 :             tupdesc->tdtypeid = RECORDOID;
    1357           0 :             tupdesc->tdtypmod = -1;
    1358             :         }
    1359             :     }
    1360           0 :     else if (functypclass == TYPEFUNC_SCALAR)
    1361             :     {
    1362             :         /* Base data type, i.e. scalar */
    1363             :         char       *attname;
    1364             : 
    1365             :         /* the alias list is required for base types */
    1366           0 :         if (colaliases == NIL)
    1367           0 :             ereport(ERROR,
    1368             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    1369             :                      errmsg("no column alias was provided")));
    1370             : 
    1371             :         /* the alias list length must be 1 */
    1372           0 :         if (list_length(colaliases) != 1)
    1373           0 :             ereport(ERROR,
    1374             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    1375             :                      errmsg("number of aliases does not match number of columns")));
    1376             : 
    1377             :         /* OK, get the column alias */
    1378           0 :         attname = strVal(linitial(colaliases));
    1379             : 
    1380           0 :         tupdesc = CreateTemplateTupleDesc(1, false);
    1381           0 :         TupleDescInitEntry(tupdesc,
    1382             :                            (AttrNumber) 1,
    1383             :                            attname,
    1384             :                            typeoid,
    1385             :                            -1,
    1386             :                            0);
    1387             :     }
    1388           0 :     else if (functypclass == TYPEFUNC_RECORD)
    1389             :     {
    1390             :         /* XXX can't support this because typmod wasn't passed in ... */
    1391           0 :         ereport(ERROR,
    1392             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    1393             :                  errmsg("could not determine row description for function returning record")));
    1394             :     }
    1395             :     else
    1396             :     {
    1397             :         /* crummy error message, but parser should have caught this */
    1398           0 :         elog(ERROR, "function in FROM has unsupported return type");
    1399             :     }
    1400             : 
    1401           0 :     return tupdesc;
    1402             : }

Generated by: LCOV version 1.11