]> granicus.if.org Git - postgresql/blob - src/backend/utils/fmgr/funcapi.c
get_expr_result_type probably needs to be able to handle OpExpr as well
[postgresql] / src / backend / utils / fmgr / funcapi.c
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-2005, PostgreSQL Global Development Group
8  *
9  * IDENTIFICATION
10  *        $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.21 2005/04/25 20:59:44 tgl Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15
16 #include "access/heapam.h"
17 #include "funcapi.h"
18 #include "catalog/namespace.h"
19 #include "catalog/pg_proc.h"
20 #include "catalog/pg_type.h"
21 #include "parser/parse_coerce.h"
22 #include "parser/parse_expr.h"
23 #include "utils/array.h"
24 #include "utils/builtins.h"
25 #include "utils/lsyscache.h"
26 #include "utils/syscache.h"
27 #include "utils/typcache.h"
28
29
30 static void shutdown_MultiFuncCall(Datum arg);
31 static TypeFuncClass internal_get_result_type(Oid funcid,
32                                                                                           Node *call_expr,
33                                                                                           ReturnSetInfo *rsinfo,
34                                                                                           Oid *resultTypeId,
35                                                                                           TupleDesc *resultTupleDesc);
36 static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc,
37                                                                                 oidvector *declared_args,
38                                                                                 Node *call_expr);
39 static TypeFuncClass get_type_func_class(Oid typid);
40
41
42 /*
43  * init_MultiFuncCall
44  * Create an empty FuncCallContext data structure
45  * and do some other basic Multi-function call setup
46  * and error checking
47  */
48 FuncCallContext *
49 init_MultiFuncCall(PG_FUNCTION_ARGS)
50 {
51         FuncCallContext *retval;
52
53         /*
54          * Bail if we're called in the wrong context
55          */
56         if (fcinfo->resultinfo == NULL || !IsA(fcinfo->resultinfo, ReturnSetInfo))
57                 ereport(ERROR,
58                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
59                                  errmsg("set-valued function called in context that cannot accept a set")));
60
61         if (fcinfo->flinfo->fn_extra == NULL)
62         {
63                 /*
64                  * First call
65                  */
66                 ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
67
68                 /*
69                  * Allocate suitably long-lived space and zero it
70                  */
71                 retval = (FuncCallContext *)
72                         MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
73                                                                    sizeof(FuncCallContext));
74
75                 /*
76                  * initialize the elements
77                  */
78                 retval->call_cntr = 0;
79                 retval->max_calls = 0;
80                 retval->slot = NULL;
81                 retval->user_fctx = NULL;
82                 retval->attinmeta = NULL;
83                 retval->tuple_desc = NULL;
84                 retval->multi_call_memory_ctx = fcinfo->flinfo->fn_mcxt;
85
86                 /*
87                  * save the pointer for cross-call use
88                  */
89                 fcinfo->flinfo->fn_extra = retval;
90
91                 /*
92                  * Ensure we will get shut down cleanly if the exprcontext is not
93                  * run to completion.
94                  */
95                 RegisterExprContextCallback(rsi->econtext,
96                                                                         shutdown_MultiFuncCall,
97                                                                         PointerGetDatum(fcinfo->flinfo));
98         }
99         else
100         {
101                 /* second and subsequent calls */
102                 elog(ERROR, "init_MultiFuncCall may not be called more than once");
103
104                 /* never reached, but keep compiler happy */
105                 retval = NULL;
106         }
107
108         return retval;
109 }
110
111 /*
112  * per_MultiFuncCall
113  *
114  * Do Multi-function per-call setup
115  */
116 FuncCallContext *
117 per_MultiFuncCall(PG_FUNCTION_ARGS)
118 {
119         FuncCallContext *retval = (FuncCallContext *) fcinfo->flinfo->fn_extra;
120
121         /*
122          * Clear the TupleTableSlot, if present.  This is for safety's sake:
123          * the Slot will be in a long-lived context (it better be, if the
124          * FuncCallContext is pointing to it), but in most usage patterns the
125          * tuples stored in it will be in the function's per-tuple context. So
126          * at the beginning of each call, the Slot will hold a dangling
127          * pointer to an already-recycled tuple.  We clear it out here.
128          *
129          * Note: use of retval->slot is obsolete as of 8.0, and we expect that it
130          * will always be NULL.  This is just here for backwards compatibility
131          * in case someone creates a slot anyway.
132          */
133         if (retval->slot != NULL)
134                 ExecClearTuple(retval->slot);
135
136         return retval;
137 }
138
139 /*
140  * end_MultiFuncCall
141  * Clean up after init_MultiFuncCall
142  */
143 void
144 end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx)
145 {
146         ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
147
148         /* Deregister the shutdown callback */
149         UnregisterExprContextCallback(rsi->econtext,
150                                                                   shutdown_MultiFuncCall,
151                                                                   PointerGetDatum(fcinfo->flinfo));
152
153         /* But use it to do the real work */
154         shutdown_MultiFuncCall(PointerGetDatum(fcinfo->flinfo));
155 }
156
157 /*
158  * shutdown_MultiFuncCall
159  * Shutdown function to clean up after init_MultiFuncCall
160  */
161 static void
162 shutdown_MultiFuncCall(Datum arg)
163 {
164         FmgrInfo   *flinfo = (FmgrInfo *) DatumGetPointer(arg);
165         FuncCallContext *funcctx = (FuncCallContext *) flinfo->fn_extra;
166
167         /* unbind from flinfo */
168         flinfo->fn_extra = NULL;
169
170         /*
171          * Caller is responsible to free up memory for individual struct
172          * elements other than att_in_funcinfo and elements.
173          */
174         if (funcctx->attinmeta != NULL)
175                 pfree(funcctx->attinmeta);
176
177         pfree(funcctx);
178 }
179
180
181 /*
182  * get_call_result_type
183  *              Given a function's call info record, determine the kind of datatype
184  *              it is supposed to return.  If resultTypeId isn't NULL, *resultTypeId
185  *              receives the actual datatype OID (this is mainly useful for scalar
186  *              result types).  If resultTupleDesc isn't NULL, *resultTupleDesc
187  *              receives a pointer to a TupleDesc when the result is of a composite
188  *              type, or NULL when it's a scalar result.  NB: the tupledesc should
189  *              be copied if it is to be accessed over a long period.
190  *
191  * One hard case that this handles is resolution of actual rowtypes for
192  * functions returning RECORD (from either the function's OUT parameter
193  * list, or a ReturnSetInfo context node).  TYPEFUNC_RECORD is returned
194  * only when we couldn't resolve the actual rowtype for lack of information.
195  *
196  * The other hard case that this handles is resolution of polymorphism.
197  * We will never return ANYELEMENT or ANYARRAY, either as a scalar result
198  * type or as a component of a rowtype.
199  *
200  * This function is relatively expensive --- in a function returning set,
201  * try to call it only the first time through.
202  */
203 TypeFuncClass
204 get_call_result_type(FunctionCallInfo fcinfo,
205                                          Oid *resultTypeId,
206                                          TupleDesc *resultTupleDesc)
207 {
208         return internal_get_result_type(fcinfo->flinfo->fn_oid,
209                                                                         fcinfo->flinfo->fn_expr,
210                                                                         (ReturnSetInfo *) fcinfo->resultinfo,
211                                                                         resultTypeId,
212                                                                         resultTupleDesc);
213 }
214
215 /*
216  * get_expr_result_type
217  *              As above, but work from a calling expression node tree
218  */
219 TypeFuncClass
220 get_expr_result_type(Node *expr,
221                                          Oid *resultTypeId,
222                                          TupleDesc *resultTupleDesc)
223 {
224         TypeFuncClass result;
225
226         if (expr && IsA(expr, FuncExpr))
227                 result = internal_get_result_type(((FuncExpr *) expr)->funcid,
228                                                                                   expr,
229                                                                                   NULL,
230                                                                                   resultTypeId,
231                                                                                   resultTupleDesc);
232         else if (expr && IsA(expr, OpExpr))
233                 result = internal_get_result_type(get_opcode(((OpExpr *) expr)->opno),
234                                                                                   expr,
235                                                                                   NULL,
236                                                                                   resultTypeId,
237                                                                                   resultTupleDesc);
238         else
239         {
240                 /* handle as a generic expression; no chance to resolve RECORD */
241                 Oid             typid = exprType(expr);
242
243                 if (resultTypeId)
244                         *resultTypeId = typid;
245                 if (resultTupleDesc)
246                         *resultTupleDesc = NULL;
247                 result = get_type_func_class(typid);
248                 if (result == TYPEFUNC_COMPOSITE && resultTupleDesc)
249                         *resultTupleDesc = lookup_rowtype_tupdesc(typid, -1);
250         }
251
252         return result;
253 }
254
255 /*
256  * get_func_result_type
257  *              As above, but work from a function's OID only
258  *
259  * This will not be able to resolve pure-RECORD results nor polymorphism.
260  */
261 TypeFuncClass
262 get_func_result_type(Oid functionId,
263                                          Oid *resultTypeId,
264                                          TupleDesc *resultTupleDesc)
265 {
266         return internal_get_result_type(functionId,
267                                                                         NULL,
268                                                                         NULL,
269                                                                         resultTypeId,
270                                                                         resultTupleDesc);
271 }
272
273 /*
274  * internal_get_result_type -- workhorse code implementing all the above
275  *
276  * funcid must always be supplied.  call_expr and rsinfo can be NULL if not
277  * available.  We will return TYPEFUNC_RECORD, and store NULL into
278  * *resultTupleDesc, if we cannot deduce the complete result rowtype from
279  * the available information.
280  */
281 static TypeFuncClass
282 internal_get_result_type(Oid funcid,
283                                                  Node *call_expr,
284                                                  ReturnSetInfo *rsinfo,
285                                                  Oid *resultTypeId,
286                                                  TupleDesc *resultTupleDesc)
287 {
288         TypeFuncClass result;
289         HeapTuple       tp;
290         Form_pg_proc procform;
291         Oid                     rettype;
292         TupleDesc       tupdesc;
293
294         /* First fetch the function's pg_proc row to inspect its rettype */
295         tp = SearchSysCache(PROCOID,
296                                                 ObjectIdGetDatum(funcid),
297                                                 0, 0, 0);
298         if (!HeapTupleIsValid(tp))
299                 elog(ERROR, "cache lookup failed for function %u", funcid);
300         procform = (Form_pg_proc) GETSTRUCT(tp);
301
302         rettype = procform->prorettype;
303
304         /* Check for OUT parameters defining a RECORD result */
305         tupdesc = build_function_result_tupdesc_t(tp);
306         if (tupdesc)
307         {
308                 /*
309                  * It has OUT parameters, so it's basically like a regular
310                  * composite type, except we have to be able to resolve any
311                  * polymorphic OUT parameters.
312                  */
313                 if (resultTypeId)
314                         *resultTypeId = rettype;
315
316                 if (resolve_polymorphic_tupdesc(tupdesc,
317                                                                                 &procform->proargtypes,
318                                                                                 call_expr))
319                 {
320                         if (tupdesc->tdtypeid == RECORDOID &&
321                                 tupdesc->tdtypmod < 0)
322                                 assign_record_type_typmod(tupdesc);
323                         if (resultTupleDesc)
324                                 *resultTupleDesc = tupdesc;
325                         result = TYPEFUNC_COMPOSITE;
326                 }
327                 else
328                 {
329                         if (resultTupleDesc)
330                                 *resultTupleDesc = NULL;
331                         result = TYPEFUNC_RECORD;
332                 }
333
334                 ReleaseSysCache(tp);
335
336                 return result;
337         }
338
339         /*
340          * If scalar polymorphic result, try to resolve it.
341          */
342         if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
343         {
344                 Oid             newrettype = exprType(call_expr);
345
346                 if (newrettype == InvalidOid)   /* this probably should not happen */
347                         ereport(ERROR,
348                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
349                                          errmsg("could not determine actual result type for function \"%s\" declared to return type %s",
350                                                         NameStr(procform->proname),
351                                                         format_type_be(rettype))));
352                 rettype = newrettype;
353         }
354
355         if (resultTypeId)
356                 *resultTypeId = rettype;
357         if (resultTupleDesc)
358                 *resultTupleDesc = NULL;                /* default result */
359
360         /* Classify the result type */
361         result = get_type_func_class(rettype);
362         switch (result)
363         {
364                 case TYPEFUNC_COMPOSITE:
365                         if (resultTupleDesc)
366                                 *resultTupleDesc = lookup_rowtype_tupdesc(rettype, -1);
367                         /* Named composite types can't have any polymorphic columns */
368                         break;
369                 case TYPEFUNC_SCALAR:
370                         break;
371                 case TYPEFUNC_RECORD:
372                         /* We must get the tupledesc from call context */
373                         if (rsinfo && IsA(rsinfo, ReturnSetInfo) &&
374                                 rsinfo->expectedDesc != NULL)
375                         {
376                                 result = TYPEFUNC_COMPOSITE;
377                                 if (resultTupleDesc)
378                                         *resultTupleDesc = rsinfo->expectedDesc;
379                                 /* Assume no polymorphic columns here, either */
380                         }
381                         break;
382                 default:
383                         break;
384         }
385
386         ReleaseSysCache(tp);
387
388         return result;
389 }
390
391 /*
392  * Given the result tuple descriptor for a function with OUT parameters,
393  * replace any polymorphic columns (ANYELEMENT/ANYARRAY) with correct data
394  * types deduced from the input arguments.  Returns TRUE if able to deduce
395  * all types, FALSE if not.
396  */
397 static bool
398 resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
399                                                         Node *call_expr)
400 {
401         int                     natts = tupdesc->natts;
402         int                     nargs = declared_args->dim1;
403         bool            have_anyelement_result = false;
404         bool            have_anyarray_result = false;
405         Oid                     anyelement_type = InvalidOid;
406         Oid                     anyarray_type = InvalidOid;
407         int                     i;
408
409         /* See if there are any polymorphic outputs; quick out if not */
410         for (i = 0; i < natts; i++)
411         {
412                 switch (tupdesc->attrs[i]->atttypid)
413                 {
414                         case ANYELEMENTOID:
415                                 have_anyelement_result = true;
416                                 break;
417                         case ANYARRAYOID:
418                                 have_anyarray_result = true;
419                                 break;
420                         default:
421                                 break;
422                 }
423         }
424         if (!have_anyelement_result && !have_anyarray_result)
425                 return true;
426
427         /*
428          * Otherwise, extract actual datatype(s) from input arguments.  (We assume
429          * the parser already validated consistency of the arguments.)
430          */
431         if (!call_expr)
432                 return false;                   /* no hope */
433
434         for (i = 0; i < nargs; i++)
435         {
436                 switch (declared_args->values[i])
437                 {
438                         case ANYELEMENTOID:
439                                 if (!OidIsValid(anyelement_type))
440                                         anyelement_type = get_call_expr_argtype(call_expr, i);
441                                 break;
442                         case ANYARRAYOID:
443                                 if (!OidIsValid(anyarray_type))
444                                         anyarray_type = get_call_expr_argtype(call_expr, i);
445                                 break;
446                         default:
447                                 break;
448                 }
449         }
450
451         /* If nothing found, parser messed up */
452         if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))
453                 return false;
454
455         /* If needed, deduce one polymorphic type from the other */
456         if (have_anyelement_result && !OidIsValid(anyelement_type))
457                 anyelement_type = resolve_generic_type(ANYELEMENTOID,
458                                                                                            anyarray_type,
459                                                                                            ANYARRAYOID);
460         if (have_anyarray_result && !OidIsValid(anyarray_type))
461                 anyarray_type = resolve_generic_type(ANYARRAYOID,
462                                                                                          anyelement_type,
463                                                                                          ANYELEMENTOID);
464
465         /* And finally replace the tuple column types as needed */
466         for (i = 0; i < natts; i++)
467         {
468                 switch (tupdesc->attrs[i]->atttypid)
469                 {
470                         case ANYELEMENTOID:
471                                 TupleDescInitEntry(tupdesc, i+1,
472                                                                    NameStr(tupdesc->attrs[i]->attname),
473                                                                    anyelement_type,
474                                                                    -1,
475                                                                    0);
476                                 break;
477                         case ANYARRAYOID:
478                                 TupleDescInitEntry(tupdesc, i+1,
479                                                                    NameStr(tupdesc->attrs[i]->attname),
480                                                                    anyarray_type,
481                                                                    -1,
482                                                                    0);
483                                 break;
484                         default:
485                                 break;
486                 }
487         }
488
489         return true;
490 }
491
492 /*
493  * Given the declared argument types and modes for a function,
494  * replace any polymorphic types (ANYELEMENT/ANYARRAY) with correct data
495  * types deduced from the input arguments.  Returns TRUE if able to deduce
496  * all types, FALSE if not.  This is the same logic as
497  * resolve_polymorphic_tupdesc, but with a different argument representation.
498  *
499  * argmodes may be NULL, in which case all arguments are assumed to be IN mode.
500  */
501 bool
502 resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
503                                                          Node *call_expr)
504 {
505         bool            have_anyelement_result = false;
506         bool            have_anyarray_result = false;
507         Oid                     anyelement_type = InvalidOid;
508         Oid                     anyarray_type = InvalidOid;
509         int                     inargno;
510         int                     i;
511
512         /* First pass: resolve polymorphic inputs, check for outputs */
513         inargno = 0;
514         for (i = 0; i < numargs; i++)
515         {
516                 char    argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
517
518                 switch (argtypes[i])
519                 {
520                         case ANYELEMENTOID:
521                                 if (argmode == PROARGMODE_OUT)
522                                         have_anyelement_result = true;
523                                 else
524                                 {
525                                         if (!OidIsValid(anyelement_type))
526                                         {
527                                                 anyelement_type = get_call_expr_argtype(call_expr,
528                                                                                                                                 inargno);
529                                                 if (!OidIsValid(anyelement_type))
530                                                         return false;
531                                         }
532                                         argtypes[i] = anyelement_type;
533                                 }
534                                 break;
535                         case ANYARRAYOID:
536                                 if (argmode == PROARGMODE_OUT)
537                                         have_anyarray_result = true;
538                                 else
539                                 {
540                                         if (!OidIsValid(anyarray_type))
541                                         {
542                                                 anyarray_type = get_call_expr_argtype(call_expr,
543                                                                                                                           inargno);
544                                                 if (!OidIsValid(anyarray_type))
545                                                         return false;
546                                         }
547                                         argtypes[i] = anyarray_type;
548                                 }
549                                 break;
550                         default:
551                                 break;
552                 }
553                 if (argmode != PROARGMODE_OUT)
554                         inargno++;
555         }
556
557         /* Done? */
558         if (!have_anyelement_result && !have_anyarray_result)
559                 return true;
560
561         /* If no input polymorphics, parser messed up */
562         if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))
563                 return false;
564
565         /* If needed, deduce one polymorphic type from the other */
566         if (have_anyelement_result && !OidIsValid(anyelement_type))
567                 anyelement_type = resolve_generic_type(ANYELEMENTOID,
568                                                                                            anyarray_type,
569                                                                                            ANYARRAYOID);
570         if (have_anyarray_result && !OidIsValid(anyarray_type))
571                 anyarray_type = resolve_generic_type(ANYARRAYOID,
572                                                                                          anyelement_type,
573                                                                                          ANYELEMENTOID);
574
575         /* And finally replace the output column types as needed */
576         for (i = 0; i < numargs; i++)
577         {
578                 switch (argtypes[i])
579                 {
580                         case ANYELEMENTOID:
581                                 argtypes[i] = anyelement_type;
582                                 break;
583                         case ANYARRAYOID:
584                                 argtypes[i] = anyarray_type;
585                                 break;
586                         default:
587                                 break;
588                 }
589         }
590
591         return true;
592 }
593
594 /*
595  * get_type_func_class
596  *              Given the type OID, obtain its TYPEFUNC classification.
597  *
598  * This is intended to centralize a bunch of formerly ad-hoc code for
599  * classifying types.  The categories used here are useful for deciding
600  * how to handle functions returning the datatype.
601  */
602 static TypeFuncClass
603 get_type_func_class(Oid typid)
604 {
605         switch (get_typtype(typid))
606         {
607                 case 'c':
608                         return TYPEFUNC_COMPOSITE;
609                 case 'b':
610                 case 'd':
611                         return TYPEFUNC_SCALAR;
612                 case 'p':
613                         if (typid == RECORDOID)
614                                 return TYPEFUNC_RECORD;
615                         /*
616                          * We treat VOID and CSTRING as legitimate scalar datatypes,
617                          * mostly for the convenience of the JDBC driver (which wants
618                          * to be able to do "SELECT * FROM foo()" for all legitimately
619                          * user-callable functions).
620                          */
621                         if (typid == VOIDOID || typid == CSTRINGOID)
622                                 return TYPEFUNC_SCALAR;
623                         return TYPEFUNC_OTHER;
624         }
625         /* shouldn't get here, probably */
626         return TYPEFUNC_OTHER;
627 }
628
629
630 /*
631  * build_function_result_tupdesc_t
632  *
633  * Given a pg_proc row for a function, return a tuple descriptor for the
634  * result rowtype, or NULL if the function does not have OUT parameters.
635  *
636  * Note that this does not handle resolution of ANYELEMENT/ANYARRAY types;
637  * that is deliberate.
638  */
639 TupleDesc
640 build_function_result_tupdesc_t(HeapTuple procTuple)
641 {
642         Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(procTuple);
643         Datum           proallargtypes;
644         Datum           proargmodes;
645         Datum           proargnames;
646         bool            isnull;
647
648         /* Return NULL if the function isn't declared to return RECORD */
649         if (procform->prorettype != RECORDOID)
650                 return NULL;
651
652         /* If there are no OUT parameters, return NULL */
653         if (heap_attisnull(procTuple, Anum_pg_proc_proallargtypes) ||
654                 heap_attisnull(procTuple, Anum_pg_proc_proargmodes))
655                 return NULL;
656
657         /* Get the data out of the tuple */
658         proallargtypes = SysCacheGetAttr(PROCOID, procTuple,
659                                                                          Anum_pg_proc_proallargtypes,
660                                                                          &isnull);
661         Assert(!isnull);
662         proargmodes = SysCacheGetAttr(PROCOID, procTuple,
663                                                                   Anum_pg_proc_proargmodes,
664                                                                   &isnull);
665         Assert(!isnull);
666         proargnames = SysCacheGetAttr(PROCOID, procTuple,
667                                                                   Anum_pg_proc_proargnames,
668                                                                   &isnull);
669         if (isnull)
670                 proargnames = PointerGetDatum(NULL); /* just to be sure */
671
672         return build_function_result_tupdesc_d(proallargtypes,
673                                                                                    proargmodes,
674                                                                                    proargnames);
675 }
676
677 /*
678  * build_function_result_tupdesc_d
679  *
680  * Build a RECORD function's tupledesc from the pg_proc proallargtypes,
681  * proargmodes, and proargnames arrays.  This is split out for the
682  * convenience of ProcedureCreate, which needs to be able to compute the
683  * tupledesc before actually creating the function.
684  *
685  * Returns NULL if there are not at least two OUT or INOUT arguments.
686  */
687 TupleDesc
688 build_function_result_tupdesc_d(Datum proallargtypes,
689                                                                 Datum proargmodes,
690                                                                 Datum proargnames)
691 {
692         TupleDesc       desc;
693         ArrayType  *arr;
694         int                     numargs;
695         Oid                *argtypes;
696         char       *argmodes;
697         Datum      *argnames = NULL;
698         Oid                *outargtypes;
699         char      **outargnames;
700         int                     numoutargs;
701         int                     nargnames;
702         int                     i;
703
704         /* Can't have output args if columns are null */
705         if (proallargtypes == PointerGetDatum(NULL) ||
706                 proargmodes == PointerGetDatum(NULL))
707                 return NULL;
708
709         /*
710          * We expect the arrays to be 1-D arrays of the right types; verify that.
711          * For the OID and char arrays, we don't need to use deconstruct_array()
712          * since the array data is just going to look like a C array of values.
713          */
714         arr = DatumGetArrayTypeP(proallargtypes);       /* ensure not toasted */
715         numargs = ARR_DIMS(arr)[0];
716         if (ARR_NDIM(arr) != 1 ||
717                 numargs < 0 ||
718                 ARR_ELEMTYPE(arr) != OIDOID)
719                 elog(ERROR, "proallargtypes is not a 1-D Oid array");
720         argtypes = (Oid *) ARR_DATA_PTR(arr);
721         arr = DatumGetArrayTypeP(proargmodes);          /* ensure not toasted */
722         if (ARR_NDIM(arr) != 1 ||
723                 ARR_DIMS(arr)[0] != numargs ||
724                 ARR_ELEMTYPE(arr) != CHAROID)
725                 elog(ERROR, "proargmodes is not a 1-D char array");
726         argmodes = (char *) ARR_DATA_PTR(arr);
727         if (proargnames != PointerGetDatum(NULL))
728         {
729                 arr = DatumGetArrayTypeP(proargnames);  /* ensure not toasted */
730                 if (ARR_NDIM(arr) != 1 ||
731                         ARR_DIMS(arr)[0] != numargs ||
732                         ARR_ELEMTYPE(arr) != TEXTOID)
733                         elog(ERROR, "proargnames is not a 1-D text array");
734                 deconstruct_array(arr, TEXTOID, -1, false, 'i',
735                                                   &argnames, &nargnames);
736                 Assert(nargnames == numargs);
737         }
738
739         /* zero elements probably shouldn't happen, but handle it gracefully */
740         if (numargs <= 0)
741                 return NULL;
742
743         /* extract output-argument types and names */
744         outargtypes = (Oid *) palloc(numargs * sizeof(Oid));
745         outargnames = (char **) palloc(numargs * sizeof(char *));
746         numoutargs = 0;
747         for (i = 0; i < numargs; i++)
748         {
749                 char    *pname;
750
751                 if (argmodes[i] == PROARGMODE_IN)
752                         continue;
753                 Assert(argmodes[i] == PROARGMODE_OUT ||
754                            argmodes[i] == PROARGMODE_INOUT);
755                 outargtypes[numoutargs] = argtypes[i];
756                 if (argnames)
757                         pname = DatumGetCString(DirectFunctionCall1(textout, argnames[i]));
758                 else
759                         pname = NULL;
760                 if (pname == NULL || pname[0] == '\0')
761                 {
762                         /* Parameter is not named, so gin up a column name */
763                         pname = (char *) palloc(32);
764                         snprintf(pname, 32, "column%d", numoutargs + 1);
765                 }
766                 outargnames[numoutargs] = pname;
767                 numoutargs++;
768         }
769
770         /*
771          * If there is no output argument, or only one, the function does not
772          * return tuples.
773          */
774         if (numoutargs < 2)
775                 return NULL;
776
777         desc = CreateTemplateTupleDesc(numoutargs, false);
778         for (i = 0; i < numoutargs; i++)
779         {
780                 TupleDescInitEntry(desc, i+1,
781                                                    outargnames[i],
782                                                    outargtypes[i],
783                                                    -1,
784                                                    0);
785         }
786
787         return desc;
788 }
789
790
791 /*
792  * RelationNameGetTupleDesc
793  *
794  * Given a (possibly qualified) relation name, build a TupleDesc.
795  */
796 TupleDesc
797 RelationNameGetTupleDesc(const char *relname)
798 {
799         RangeVar   *relvar;
800         Relation        rel;
801         TupleDesc       tupdesc;
802         List       *relname_list;
803
804         /* Open relation and copy the tuple description */
805         relname_list = stringToQualifiedNameList(relname, "RelationNameGetTupleDesc");
806         relvar = makeRangeVarFromNameList(relname_list);
807         rel = relation_openrv(relvar, AccessShareLock);
808         tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
809         relation_close(rel, AccessShareLock);
810
811         return tupdesc;
812 }
813
814 /*
815  * TypeGetTupleDesc
816  *
817  * Given a type Oid, build a TupleDesc.
818  *
819  * If the type is composite, *and* a colaliases List is provided, *and*
820  * the List is of natts length, use the aliases instead of the relation
821  * attnames.  (NB: this usage is deprecated since it may result in
822  * creation of unnecessary transient record types.)
823  *
824  * If the type is a base type, a single item alias List is required.
825  */
826 TupleDesc
827 TypeGetTupleDesc(Oid typeoid, List *colaliases)
828 {
829         TypeFuncClass functypclass = get_type_func_class(typeoid);
830         TupleDesc       tupdesc = NULL;
831
832         /*
833          * Build a suitable tupledesc representing the output rows
834          */
835         if (functypclass == TYPEFUNC_COMPOSITE)
836         {
837                 /* Composite data type, e.g. a table's row type */
838                 tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(typeoid, -1));
839
840                 if (colaliases != NIL)
841                 {
842                         int                     natts = tupdesc->natts;
843                         int                     varattno;
844
845                         /* does the list length match the number of attributes? */
846                         if (list_length(colaliases) != natts)
847                                 ereport(ERROR,
848                                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
849                                                  errmsg("number of aliases does not match number of columns")));
850
851                         /* OK, use the aliases instead */
852                         for (varattno = 0; varattno < natts; varattno++)
853                         {
854                                 char       *label = strVal(list_nth(colaliases, varattno));
855
856                                 if (label != NULL)
857                                         namestrcpy(&(tupdesc->attrs[varattno]->attname), label);
858                         }
859
860                         /* The tuple type is now an anonymous record type */
861                         tupdesc->tdtypeid = RECORDOID;
862                         tupdesc->tdtypmod = -1;
863                 }
864         }
865         else if (functypclass == TYPEFUNC_SCALAR)
866         {
867                 /* Base data type, i.e. scalar */
868                 char       *attname;
869
870                 /* the alias list is required for base types */
871                 if (colaliases == NIL)
872                         ereport(ERROR,
873                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
874                                          errmsg("no column alias was provided")));
875
876                 /* the alias list length must be 1 */
877                 if (list_length(colaliases) != 1)
878                         ereport(ERROR,
879                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
880                                          errmsg("number of aliases does not match number of columns")));
881
882                 /* OK, get the column alias */
883                 attname = strVal(linitial(colaliases));
884
885                 tupdesc = CreateTemplateTupleDesc(1, false);
886                 TupleDescInitEntry(tupdesc,
887                                                    (AttrNumber) 1,
888                                                    attname,
889                                                    typeoid,
890                                                    -1,
891                                                    0);
892         }
893         else if (functypclass == TYPEFUNC_RECORD)
894         {
895                 /* XXX can't support this because typmod wasn't passed in ... */
896                 ereport(ERROR,
897                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
898                                  errmsg("could not determine row description for function returning record")));
899         }
900         else
901         {
902                 /* crummy error message, but parser should have caught this */
903                 elog(ERROR, "function in FROM has unsupported return type");
904         }
905
906         return tupdesc;
907 }