]> granicus.if.org Git - postgresql/blob - src/backend/utils/fmgr/funcapi.c
Update CVS HEAD for 2007 copyright. Back branches are typically not
[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-2007, PostgreSQL Global Development Group
8  *
9  * IDENTIFICATION
10  *        $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.32 2007/01/05 22:19:43 momjian Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15
16 #include "access/heapam.h"
17 #include "catalog/namespace.h"
18 #include "catalog/pg_proc.h"
19 #include "catalog/pg_type.h"
20 #include "funcapi.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 run
93                  * 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: the
123          * 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 at
126          * the beginning of each call, the Slot will hold a dangling pointer to an
127          * 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 in
131          * 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 elements
172          * 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.
189  *
190  * One hard case that this handles is resolution of actual rowtypes for
191  * functions returning RECORD (from either the function's OUT parameter
192  * list, or a ReturnSetInfo context node).      TYPEFUNC_RECORD is returned
193  * only when we couldn't resolve the actual rowtype for lack of information.
194  *
195  * The other hard case that this handles is resolution of polymorphism.
196  * We will never return ANYELEMENT or ANYARRAY, either as a scalar result
197  * type or as a component of a rowtype.
198  *
199  * This function is relatively expensive --- in a function returning set,
200  * try to call it only the first time through.
201  */
202 TypeFuncClass
203 get_call_result_type(FunctionCallInfo fcinfo,
204                                          Oid *resultTypeId,
205                                          TupleDesc *resultTupleDesc)
206 {
207         return internal_get_result_type(fcinfo->flinfo->fn_oid,
208                                                                         fcinfo->flinfo->fn_expr,
209                                                                         (ReturnSetInfo *) fcinfo->resultinfo,
210                                                                         resultTypeId,
211                                                                         resultTupleDesc);
212 }
213
214 /*
215  * get_expr_result_type
216  *              As above, but work from a calling expression node tree
217  */
218 TypeFuncClass
219 get_expr_result_type(Node *expr,
220                                          Oid *resultTypeId,
221                                          TupleDesc *resultTupleDesc)
222 {
223         TypeFuncClass result;
224
225         if (expr && IsA(expr, FuncExpr))
226                 result = internal_get_result_type(((FuncExpr *) expr)->funcid,
227                                                                                   expr,
228                                                                                   NULL,
229                                                                                   resultTypeId,
230                                                                                   resultTupleDesc);
231         else if (expr && IsA(expr, OpExpr))
232                 result = internal_get_result_type(get_opcode(((OpExpr *) expr)->opno),
233                                                                                   expr,
234                                                                                   NULL,
235                                                                                   resultTypeId,
236                                                                                   resultTupleDesc);
237         else
238         {
239                 /* handle as a generic expression; no chance to resolve RECORD */
240                 Oid                     typid = exprType(expr);
241
242                 if (resultTypeId)
243                         *resultTypeId = typid;
244                 if (resultTupleDesc)
245                         *resultTupleDesc = NULL;
246                 result = get_type_func_class(typid);
247                 if (result == TYPEFUNC_COMPOSITE && resultTupleDesc)
248                         *resultTupleDesc = lookup_rowtype_tupdesc_copy(typid, -1);
249         }
250
251         return result;
252 }
253
254 /*
255  * get_func_result_type
256  *              As above, but work from a function's OID only
257  *
258  * This will not be able to resolve pure-RECORD results nor polymorphism.
259  */
260 TypeFuncClass
261 get_func_result_type(Oid functionId,
262                                          Oid *resultTypeId,
263                                          TupleDesc *resultTupleDesc)
264 {
265         return internal_get_result_type(functionId,
266                                                                         NULL,
267                                                                         NULL,
268                                                                         resultTypeId,
269                                                                         resultTupleDesc);
270 }
271
272 /*
273  * internal_get_result_type -- workhorse code implementing all the above
274  *
275  * funcid must always be supplied.      call_expr and rsinfo can be NULL if not
276  * available.  We will return TYPEFUNC_RECORD, and store NULL into
277  * *resultTupleDesc, if we cannot deduce the complete result rowtype from
278  * the available information.
279  */
280 static TypeFuncClass
281 internal_get_result_type(Oid funcid,
282                                                  Node *call_expr,
283                                                  ReturnSetInfo *rsinfo,
284                                                  Oid *resultTypeId,
285                                                  TupleDesc *resultTupleDesc)
286 {
287         TypeFuncClass result;
288         HeapTuple       tp;
289         Form_pg_proc procform;
290         Oid                     rettype;
291         TupleDesc       tupdesc;
292
293         /* First fetch the function's pg_proc row to inspect its rettype */
294         tp = SearchSysCache(PROCOID,
295                                                 ObjectIdGetDatum(funcid),
296                                                 0, 0, 0);
297         if (!HeapTupleIsValid(tp))
298                 elog(ERROR, "cache lookup failed for function %u", funcid);
299         procform = (Form_pg_proc) GETSTRUCT(tp);
300
301         rettype = procform->prorettype;
302
303         /* Check for OUT parameters defining a RECORD result */
304         tupdesc = build_function_result_tupdesc_t(tp);
305         if (tupdesc)
306         {
307                 /*
308                  * It has OUT parameters, so it's basically like a regular composite
309                  * type, except we have to be able to resolve any polymorphic OUT
310                  * parameters.
311                  */
312                 if (resultTypeId)
313                         *resultTypeId = rettype;
314
315                 if (resolve_polymorphic_tupdesc(tupdesc,
316                                                                                 &procform->proargtypes,
317                                                                                 call_expr))
318                 {
319                         if (tupdesc->tdtypeid == RECORDOID &&
320                                 tupdesc->tdtypmod < 0)
321                                 assign_record_type_typmod(tupdesc);
322                         if (resultTupleDesc)
323                                 *resultTupleDesc = tupdesc;
324                         result = TYPEFUNC_COMPOSITE;
325                 }
326                 else
327                 {
328                         if (resultTupleDesc)
329                                 *resultTupleDesc = NULL;
330                         result = TYPEFUNC_RECORD;
331                 }
332
333                 ReleaseSysCache(tp);
334
335                 return result;
336         }
337
338         /*
339          * If scalar polymorphic result, try to resolve it.
340          */
341         if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
342         {
343                 Oid                     newrettype = exprType(call_expr);
344
345                 if (newrettype == InvalidOid)   /* this probably should not happen */
346                         ereport(ERROR,
347                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
348                                          errmsg("could not determine actual result type for function \"%s\" declared to return type %s",
349                                                         NameStr(procform->proname),
350                                                         format_type_be(rettype))));
351                 rettype = newrettype;
352         }
353
354         if (resultTypeId)
355                 *resultTypeId = rettype;
356         if (resultTupleDesc)
357                 *resultTupleDesc = NULL;        /* default result */
358
359         /* Classify the result type */
360         result = get_type_func_class(rettype);
361         switch (result)
362         {
363                 case TYPEFUNC_COMPOSITE:
364                         if (resultTupleDesc)
365                                 *resultTupleDesc = lookup_rowtype_tupdesc_copy(rettype, -1);
366                         /* Named composite types can't have any polymorphic columns */
367                         break;
368                 case TYPEFUNC_SCALAR:
369                         break;
370                 case TYPEFUNC_RECORD:
371                         /* We must get the tupledesc from call context */
372                         if (rsinfo && IsA(rsinfo, ReturnSetInfo) &&
373                                 rsinfo->expectedDesc != NULL)
374                         {
375                                 result = TYPEFUNC_COMPOSITE;
376                                 if (resultTupleDesc)
377                                         *resultTupleDesc = rsinfo->expectedDesc;
378                                 /* Assume no polymorphic columns here, either */
379                         }
380                         break;
381                 default:
382                         break;
383         }
384
385         ReleaseSysCache(tp);
386
387         return result;
388 }
389
390 /*
391  * Given the result tuple descriptor for a function with OUT parameters,
392  * replace any polymorphic columns (ANYELEMENT/ANYARRAY) with correct data
393  * types deduced from the input arguments.      Returns TRUE if able to deduce
394  * all types, FALSE if not.
395  */
396 static bool
397 resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
398                                                         Node *call_expr)
399 {
400         int                     natts = tupdesc->natts;
401         int                     nargs = declared_args->dim1;
402         bool            have_anyelement_result = false;
403         bool            have_anyarray_result = false;
404         Oid                     anyelement_type = InvalidOid;
405         Oid                     anyarray_type = InvalidOid;
406         int                     i;
407
408         /* See if there are any polymorphic outputs; quick out if not */
409         for (i = 0; i < natts; i++)
410         {
411                 switch (tupdesc->attrs[i]->atttypid)
412                 {
413                         case ANYELEMENTOID:
414                                 have_anyelement_result = true;
415                                 break;
416                         case ANYARRAYOID:
417                                 have_anyarray_result = true;
418                                 break;
419                         default:
420                                 break;
421                 }
422         }
423         if (!have_anyelement_result && !have_anyarray_result)
424                 return true;
425
426         /*
427          * Otherwise, extract actual datatype(s) from input arguments.  (We assume
428          * the parser already validated consistency of the arguments.)
429          */
430         if (!call_expr)
431                 return false;                   /* no hope */
432
433         for (i = 0; i < nargs; i++)
434         {
435                 switch (declared_args->values[i])
436                 {
437                         case ANYELEMENTOID:
438                                 if (!OidIsValid(anyelement_type))
439                                         anyelement_type = get_call_expr_argtype(call_expr, i);
440                                 break;
441                         case ANYARRAYOID:
442                                 if (!OidIsValid(anyarray_type))
443                                         anyarray_type = get_call_expr_argtype(call_expr, i);
444                                 break;
445                         default:
446                                 break;
447                 }
448         }
449
450         /* If nothing found, parser messed up */
451         if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))
452                 return false;
453
454         /* If needed, deduce one polymorphic type from the other */
455         if (have_anyelement_result && !OidIsValid(anyelement_type))
456                 anyelement_type = resolve_generic_type(ANYELEMENTOID,
457                                                                                            anyarray_type,
458                                                                                            ANYARRAYOID);
459         if (have_anyarray_result && !OidIsValid(anyarray_type))
460                 anyarray_type = resolve_generic_type(ANYARRAYOID,
461                                                                                          anyelement_type,
462                                                                                          ANYELEMENTOID);
463
464         /* And finally replace the tuple column types as needed */
465         for (i = 0; i < natts; i++)
466         {
467                 switch (tupdesc->attrs[i]->atttypid)
468                 {
469                         case ANYELEMENTOID:
470                                 TupleDescInitEntry(tupdesc, i + 1,
471                                                                    NameStr(tupdesc->attrs[i]->attname),
472                                                                    anyelement_type,
473                                                                    -1,
474                                                                    0);
475                                 break;
476                         case ANYARRAYOID:
477                                 TupleDescInitEntry(tupdesc, i + 1,
478                                                                    NameStr(tupdesc->attrs[i]->attname),
479                                                                    anyarray_type,
480                                                                    -1,
481                                                                    0);
482                                 break;
483                         default:
484                                 break;
485                 }
486         }
487
488         return true;
489 }
490
491 /*
492  * Given the declared argument types and modes for a function,
493  * replace any polymorphic types (ANYELEMENT/ANYARRAY) with correct data
494  * types deduced from the input arguments.      Returns TRUE if able to deduce
495  * all types, FALSE if not.  This is the same logic as
496  * resolve_polymorphic_tupdesc, but with a different argument representation.
497  *
498  * argmodes may be NULL, in which case all arguments are assumed to be IN mode.
499  */
500 bool
501 resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
502                                                          Node *call_expr)
503 {
504         bool            have_anyelement_result = false;
505         bool            have_anyarray_result = false;
506         Oid                     anyelement_type = InvalidOid;
507         Oid                     anyarray_type = InvalidOid;
508         int                     inargno;
509         int                     i;
510
511         /* First pass: resolve polymorphic inputs, check for outputs */
512         inargno = 0;
513         for (i = 0; i < numargs; i++)
514         {
515                 char            argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
516
517                 switch (argtypes[i])
518                 {
519                         case ANYELEMENTOID:
520                                 if (argmode == PROARGMODE_OUT)
521                                         have_anyelement_result = true;
522                                 else
523                                 {
524                                         if (!OidIsValid(anyelement_type))
525                                         {
526                                                 anyelement_type = get_call_expr_argtype(call_expr,
527                                                                                                                                 inargno);
528                                                 if (!OidIsValid(anyelement_type))
529                                                         return false;
530                                         }
531                                         argtypes[i] = anyelement_type;
532                                 }
533                                 break;
534                         case ANYARRAYOID:
535                                 if (argmode == PROARGMODE_OUT)
536                                         have_anyarray_result = true;
537                                 else
538                                 {
539                                         if (!OidIsValid(anyarray_type))
540                                         {
541                                                 anyarray_type = get_call_expr_argtype(call_expr,
542                                                                                                                           inargno);
543                                                 if (!OidIsValid(anyarray_type))
544                                                         return false;
545                                         }
546                                         argtypes[i] = anyarray_type;
547                                 }
548                                 break;
549                         default:
550                                 break;
551                 }
552                 if (argmode != PROARGMODE_OUT)
553                         inargno++;
554         }
555
556         /* Done? */
557         if (!have_anyelement_result && !have_anyarray_result)
558                 return true;
559
560         /* If no input polymorphics, parser messed up */
561         if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))
562                 return false;
563
564         /* If needed, deduce one polymorphic type from the other */
565         if (have_anyelement_result && !OidIsValid(anyelement_type))
566                 anyelement_type = resolve_generic_type(ANYELEMENTOID,
567                                                                                            anyarray_type,
568                                                                                            ANYARRAYOID);
569         if (have_anyarray_result && !OidIsValid(anyarray_type))
570                 anyarray_type = resolve_generic_type(ANYARRAYOID,
571                                                                                          anyelement_type,
572                                                                                          ANYELEMENTOID);
573
574         /* And finally replace the output column types as needed */
575         for (i = 0; i < numargs; i++)
576         {
577                 switch (argtypes[i])
578                 {
579                         case ANYELEMENTOID:
580                                 argtypes[i] = anyelement_type;
581                                 break;
582                         case ANYARRAYOID:
583                                 argtypes[i] = anyarray_type;
584                                 break;
585                         default:
586                                 break;
587                 }
588         }
589
590         return true;
591 }
592
593 /*
594  * get_type_func_class
595  *              Given the type OID, obtain its TYPEFUNC classification.
596  *
597  * This is intended to centralize a bunch of formerly ad-hoc code for
598  * classifying types.  The categories used here are useful for deciding
599  * how to handle functions returning the datatype.
600  */
601 static TypeFuncClass
602 get_type_func_class(Oid typid)
603 {
604         switch (get_typtype(typid))
605         {
606                 case 'c':
607                         return TYPEFUNC_COMPOSITE;
608                 case 'b':
609                 case 'd':
610                         return TYPEFUNC_SCALAR;
611                 case 'p':
612                         if (typid == RECORDOID)
613                                 return TYPEFUNC_RECORD;
614
615                         /*
616                          * We treat VOID and CSTRING as legitimate scalar datatypes,
617                          * mostly for the convenience of the JDBC driver (which wants to
618                          * 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  * get_func_arg_info
632  *
633  * Fetch info about the argument types, names, and IN/OUT modes from the
634  * pg_proc tuple.  Return value is the total number of arguments.
635  * Other results are palloc'd.  *p_argtypes is always filled in, but
636  * *p_argnames and *p_argmodes will be set NULL in the default cases
637  * (no names, and all IN arguments, respectively).
638  *
639  * Note that this function simply fetches what is in the pg_proc tuple;
640  * it doesn't do any interpretation of polymorphic types.
641  */
642 int
643 get_func_arg_info(HeapTuple procTup,
644                                   Oid **p_argtypes, char ***p_argnames, char **p_argmodes)
645 {
646         Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
647         Datum           proallargtypes;
648         Datum           proargmodes;
649         Datum           proargnames;
650         bool            isNull;
651         ArrayType  *arr;
652         int                     numargs;
653         Datum      *elems;
654         int                     nelems;
655         int                     i;
656
657         /* First discover the total number of parameters and get their types */
658         proallargtypes = SysCacheGetAttr(PROCOID, procTup,
659                                                                          Anum_pg_proc_proallargtypes,
660                                                                          &isNull);
661         if (!isNull)
662         {
663                 /*
664                  * We expect the arrays to be 1-D arrays of the right types; verify
665                  * that.  For the OID and char arrays, we don't need to use
666                  * deconstruct_array() since the array data is just going to look like
667                  * a C array of values.
668                  */
669                 arr = DatumGetArrayTypeP(proallargtypes);               /* ensure not toasted */
670                 numargs = ARR_DIMS(arr)[0];
671                 if (ARR_NDIM(arr) != 1 ||
672                         numargs < 0 ||
673                         ARR_HASNULL(arr) ||
674                         ARR_ELEMTYPE(arr) != OIDOID)
675                         elog(ERROR, "proallargtypes is not a 1-D Oid array");
676                 Assert(numargs >= procStruct->pronargs);
677                 *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid));
678                 memcpy(*p_argtypes, ARR_DATA_PTR(arr),
679                            numargs * sizeof(Oid));
680         }
681         else
682         {
683                 /* If no proallargtypes, use proargtypes */
684                 numargs = procStruct->proargtypes.dim1;
685                 Assert(numargs == procStruct->pronargs);
686                 *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid));
687                 memcpy(*p_argtypes, procStruct->proargtypes.values,
688                            numargs * sizeof(Oid));
689         }
690
691         /* Get argument names, if available */
692         proargnames = SysCacheGetAttr(PROCOID, procTup,
693                                                                   Anum_pg_proc_proargnames,
694                                                                   &isNull);
695         if (isNull)
696                 *p_argnames = NULL;
697         else
698         {
699                 deconstruct_array(DatumGetArrayTypeP(proargnames),
700                                                   TEXTOID, -1, false, 'i',
701                                                   &elems, NULL, &nelems);
702                 if (nelems != numargs)  /* should not happen */
703                         elog(ERROR, "proargnames must have the same number of elements as the function has arguments");
704                 *p_argnames = (char **) palloc(sizeof(char *) * numargs);
705                 for (i = 0; i < numargs; i++)
706                         (*p_argnames)[i] = DatumGetCString(DirectFunctionCall1(textout,
707                                                                                                                                    elems[i]));
708         }
709
710         /* Get argument modes, if available */
711         proargmodes = SysCacheGetAttr(PROCOID, procTup,
712                                                                   Anum_pg_proc_proargmodes,
713                                                                   &isNull);
714         if (isNull)
715                 *p_argmodes = NULL;
716         else
717         {
718                 arr = DatumGetArrayTypeP(proargmodes);  /* ensure not toasted */
719                 if (ARR_NDIM(arr) != 1 ||
720                         ARR_DIMS(arr)[0] != numargs ||
721                         ARR_HASNULL(arr) ||
722                         ARR_ELEMTYPE(arr) != CHAROID)
723                         elog(ERROR, "proargmodes is not a 1-D char array");
724                 *p_argmodes = (char *) palloc(numargs * sizeof(char));
725                 memcpy(*p_argmodes, ARR_DATA_PTR(arr),
726                            numargs * sizeof(char));
727         }
728
729         return numargs;
730 }
731
732
733 /*
734  * get_func_result_name
735  *
736  * If the function has exactly one output parameter, and that parameter
737  * is named, return the name (as a palloc'd string).  Else return NULL.
738  *
739  * This is used to determine the default output column name for functions
740  * returning scalar types.
741  */
742 char *
743 get_func_result_name(Oid functionId)
744 {
745         char       *result;
746         HeapTuple       procTuple;
747         Datum           proargmodes;
748         Datum           proargnames;
749         bool            isnull;
750         ArrayType  *arr;
751         int                     numargs;
752         char       *argmodes;
753         Datum      *argnames;
754         int                     numoutargs;
755         int                     nargnames;
756         int                     i;
757
758         /* First fetch the function's pg_proc row */
759         procTuple = SearchSysCache(PROCOID,
760                                                            ObjectIdGetDatum(functionId),
761                                                            0, 0, 0);
762         if (!HeapTupleIsValid(procTuple))
763                 elog(ERROR, "cache lookup failed for function %u", functionId);
764
765         /* If there are no named OUT parameters, return NULL */
766         if (heap_attisnull(procTuple, Anum_pg_proc_proargmodes) ||
767                 heap_attisnull(procTuple, Anum_pg_proc_proargnames))
768                 result = NULL;
769         else
770         {
771                 /* Get the data out of the tuple */
772                 proargmodes = SysCacheGetAttr(PROCOID, procTuple,
773                                                                           Anum_pg_proc_proargmodes,
774                                                                           &isnull);
775                 Assert(!isnull);
776                 proargnames = SysCacheGetAttr(PROCOID, procTuple,
777                                                                           Anum_pg_proc_proargnames,
778                                                                           &isnull);
779                 Assert(!isnull);
780
781                 /*
782                  * We expect the arrays to be 1-D arrays of the right types; verify
783                  * that.  For the char array, we don't need to use deconstruct_array()
784                  * since the array data is just going to look like a C array of
785                  * values.
786                  */
787                 arr = DatumGetArrayTypeP(proargmodes);  /* ensure not toasted */
788                 numargs = ARR_DIMS(arr)[0];
789                 if (ARR_NDIM(arr) != 1 ||
790                         numargs < 0 ||
791                         ARR_HASNULL(arr) ||
792                         ARR_ELEMTYPE(arr) != CHAROID)
793                         elog(ERROR, "proargmodes is not a 1-D char array");
794                 argmodes = (char *) ARR_DATA_PTR(arr);
795                 arr = DatumGetArrayTypeP(proargnames);  /* ensure not toasted */
796                 if (ARR_NDIM(arr) != 1 ||
797                         ARR_DIMS(arr)[0] != numargs ||
798                         ARR_HASNULL(arr) ||
799                         ARR_ELEMTYPE(arr) != TEXTOID)
800                         elog(ERROR, "proargnames is not a 1-D text array");
801                 deconstruct_array(arr, TEXTOID, -1, false, 'i',
802                                                   &argnames, NULL, &nargnames);
803                 Assert(nargnames == numargs);
804
805                 /* scan for output argument(s) */
806                 result = NULL;
807                 numoutargs = 0;
808                 for (i = 0; i < numargs; i++)
809                 {
810                         if (argmodes[i] == PROARGMODE_IN)
811                                 continue;
812                         Assert(argmodes[i] == PROARGMODE_OUT ||
813                                    argmodes[i] == PROARGMODE_INOUT);
814                         if (++numoutargs > 1)
815                         {
816                                 /* multiple out args, so forget it */
817                                 result = NULL;
818                                 break;
819                         }
820                         result = DatumGetCString(DirectFunctionCall1(textout,
821                                                                                                                  argnames[i]));
822                         if (result == NULL || result[0] == '\0')
823                         {
824                                 /* Parameter is not named, so forget it */
825                                 result = NULL;
826                                 break;
827                         }
828                 }
829         }
830
831         ReleaseSysCache(procTuple);
832
833         return result;
834 }
835
836
837 /*
838  * build_function_result_tupdesc_t
839  *
840  * Given a pg_proc row for a function, return a tuple descriptor for the
841  * result rowtype, or NULL if the function does not have OUT parameters.
842  *
843  * Note that this does not handle resolution of ANYELEMENT/ANYARRAY types;
844  * that is deliberate.
845  */
846 TupleDesc
847 build_function_result_tupdesc_t(HeapTuple procTuple)
848 {
849         Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(procTuple);
850         Datum           proallargtypes;
851         Datum           proargmodes;
852         Datum           proargnames;
853         bool            isnull;
854
855         /* Return NULL if the function isn't declared to return RECORD */
856         if (procform->prorettype != RECORDOID)
857                 return NULL;
858
859         /* If there are no OUT parameters, return NULL */
860         if (heap_attisnull(procTuple, Anum_pg_proc_proallargtypes) ||
861                 heap_attisnull(procTuple, Anum_pg_proc_proargmodes))
862                 return NULL;
863
864         /* Get the data out of the tuple */
865         proallargtypes = SysCacheGetAttr(PROCOID, procTuple,
866                                                                          Anum_pg_proc_proallargtypes,
867                                                                          &isnull);
868         Assert(!isnull);
869         proargmodes = SysCacheGetAttr(PROCOID, procTuple,
870                                                                   Anum_pg_proc_proargmodes,
871                                                                   &isnull);
872         Assert(!isnull);
873         proargnames = SysCacheGetAttr(PROCOID, procTuple,
874                                                                   Anum_pg_proc_proargnames,
875                                                                   &isnull);
876         if (isnull)
877                 proargnames = PointerGetDatum(NULL);    /* just to be sure */
878
879         return build_function_result_tupdesc_d(proallargtypes,
880                                                                                    proargmodes,
881                                                                                    proargnames);
882 }
883
884 /*
885  * build_function_result_tupdesc_d
886  *
887  * Build a RECORD function's tupledesc from the pg_proc proallargtypes,
888  * proargmodes, and proargnames arrays.  This is split out for the
889  * convenience of ProcedureCreate, which needs to be able to compute the
890  * tupledesc before actually creating the function.
891  *
892  * Returns NULL if there are not at least two OUT or INOUT arguments.
893  */
894 TupleDesc
895 build_function_result_tupdesc_d(Datum proallargtypes,
896                                                                 Datum proargmodes,
897                                                                 Datum proargnames)
898 {
899         TupleDesc       desc;
900         ArrayType  *arr;
901         int                     numargs;
902         Oid                *argtypes;
903         char       *argmodes;
904         Datum      *argnames = NULL;
905         Oid                *outargtypes;
906         char      **outargnames;
907         int                     numoutargs;
908         int                     nargnames;
909         int                     i;
910
911         /* Can't have output args if columns are null */
912         if (proallargtypes == PointerGetDatum(NULL) ||
913                 proargmodes == PointerGetDatum(NULL))
914                 return NULL;
915
916         /*
917          * We expect the arrays to be 1-D arrays of the right types; verify that.
918          * For the OID and char arrays, we don't need to use deconstruct_array()
919          * since the array data is just going to look like a C array of values.
920          */
921         arr = DatumGetArrayTypeP(proallargtypes);       /* ensure not toasted */
922         numargs = ARR_DIMS(arr)[0];
923         if (ARR_NDIM(arr) != 1 ||
924                 numargs < 0 ||
925                 ARR_HASNULL(arr) ||
926                 ARR_ELEMTYPE(arr) != OIDOID)
927                 elog(ERROR, "proallargtypes is not a 1-D Oid array");
928         argtypes = (Oid *) ARR_DATA_PTR(arr);
929         arr = DatumGetArrayTypeP(proargmodes);          /* ensure not toasted */
930         if (ARR_NDIM(arr) != 1 ||
931                 ARR_DIMS(arr)[0] != numargs ||
932                 ARR_HASNULL(arr) ||
933                 ARR_ELEMTYPE(arr) != CHAROID)
934                 elog(ERROR, "proargmodes is not a 1-D char array");
935         argmodes = (char *) ARR_DATA_PTR(arr);
936         if (proargnames != PointerGetDatum(NULL))
937         {
938                 arr = DatumGetArrayTypeP(proargnames);  /* ensure not toasted */
939                 if (ARR_NDIM(arr) != 1 ||
940                         ARR_DIMS(arr)[0] != numargs ||
941                         ARR_HASNULL(arr) ||
942                         ARR_ELEMTYPE(arr) != TEXTOID)
943                         elog(ERROR, "proargnames is not a 1-D text array");
944                 deconstruct_array(arr, TEXTOID, -1, false, 'i',
945                                                   &argnames, NULL, &nargnames);
946                 Assert(nargnames == numargs);
947         }
948
949         /* zero elements probably shouldn't happen, but handle it gracefully */
950         if (numargs <= 0)
951                 return NULL;
952
953         /* extract output-argument types and names */
954         outargtypes = (Oid *) palloc(numargs * sizeof(Oid));
955         outargnames = (char **) palloc(numargs * sizeof(char *));
956         numoutargs = 0;
957         for (i = 0; i < numargs; i++)
958         {
959                 char       *pname;
960
961                 if (argmodes[i] == PROARGMODE_IN)
962                         continue;
963                 Assert(argmodes[i] == PROARGMODE_OUT ||
964                            argmodes[i] == PROARGMODE_INOUT);
965                 outargtypes[numoutargs] = argtypes[i];
966                 if (argnames)
967                         pname = DatumGetCString(DirectFunctionCall1(textout, argnames[i]));
968                 else
969                         pname = NULL;
970                 if (pname == NULL || pname[0] == '\0')
971                 {
972                         /* Parameter is not named, so gin up a column name */
973                         pname = (char *) palloc(32);
974                         snprintf(pname, 32, "column%d", numoutargs + 1);
975                 }
976                 outargnames[numoutargs] = pname;
977                 numoutargs++;
978         }
979
980         /*
981          * If there is no output argument, or only one, the function does not
982          * return tuples.
983          */
984         if (numoutargs < 2)
985                 return NULL;
986
987         desc = CreateTemplateTupleDesc(numoutargs, false);
988         for (i = 0; i < numoutargs; i++)
989         {
990                 TupleDescInitEntry(desc, i + 1,
991                                                    outargnames[i],
992                                                    outargtypes[i],
993                                                    -1,
994                                                    0);
995         }
996
997         return desc;
998 }
999
1000
1001 /*
1002  * RelationNameGetTupleDesc
1003  *
1004  * Given a (possibly qualified) relation name, build a TupleDesc.
1005  *
1006  * Note: while this works as advertised, it's seldom the best way to
1007  * build a tupdesc for a function's result type.  It's kept around
1008  * only for backwards compatibility with existing user-written code.
1009  */
1010 TupleDesc
1011 RelationNameGetTupleDesc(const char *relname)
1012 {
1013         RangeVar   *relvar;
1014         Relation        rel;
1015         TupleDesc       tupdesc;
1016         List       *relname_list;
1017
1018         /* Open relation and copy the tuple description */
1019         relname_list = stringToQualifiedNameList(relname, "RelationNameGetTupleDesc");
1020         relvar = makeRangeVarFromNameList(relname_list);
1021         rel = relation_openrv(relvar, AccessShareLock);
1022         tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
1023         relation_close(rel, AccessShareLock);
1024
1025         return tupdesc;
1026 }
1027
1028 /*
1029  * TypeGetTupleDesc
1030  *
1031  * Given a type Oid, build a TupleDesc.  (In most cases you should be
1032  * using get_call_result_type or one of its siblings instead of this
1033  * routine, so that you can handle OUT parameters, RECORD result type,
1034  * and polymorphic results.)
1035  *
1036  * If the type is composite, *and* a colaliases List is provided, *and*
1037  * the List is of natts length, use the aliases instead of the relation
1038  * attnames.  (NB: this usage is deprecated since it may result in
1039  * creation of unnecessary transient record types.)
1040  *
1041  * If the type is a base type, a single item alias List is required.
1042  */
1043 TupleDesc
1044 TypeGetTupleDesc(Oid typeoid, List *colaliases)
1045 {
1046         TypeFuncClass functypclass = get_type_func_class(typeoid);
1047         TupleDesc       tupdesc = NULL;
1048
1049         /*
1050          * Build a suitable tupledesc representing the output rows
1051          */
1052         if (functypclass == TYPEFUNC_COMPOSITE)
1053         {
1054                 /* Composite data type, e.g. a table's row type */
1055                 tupdesc = lookup_rowtype_tupdesc_copy(typeoid, -1);
1056
1057                 if (colaliases != NIL)
1058                 {
1059                         int                     natts = tupdesc->natts;
1060                         int                     varattno;
1061
1062                         /* does the list length match the number of attributes? */
1063                         if (list_length(colaliases) != natts)
1064                                 ereport(ERROR,
1065                                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
1066                                                  errmsg("number of aliases does not match number of columns")));
1067
1068                         /* OK, use the aliases instead */
1069                         for (varattno = 0; varattno < natts; varattno++)
1070                         {
1071                                 char       *label = strVal(list_nth(colaliases, varattno));
1072
1073                                 if (label != NULL)
1074                                         namestrcpy(&(tupdesc->attrs[varattno]->attname), label);
1075                         }
1076
1077                         /* The tuple type is now an anonymous record type */
1078                         tupdesc->tdtypeid = RECORDOID;
1079                         tupdesc->tdtypmod = -1;
1080                 }
1081         }
1082         else if (functypclass == TYPEFUNC_SCALAR)
1083         {
1084                 /* Base data type, i.e. scalar */
1085                 char       *attname;
1086
1087                 /* the alias list is required for base types */
1088                 if (colaliases == NIL)
1089                         ereport(ERROR,
1090                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
1091                                          errmsg("no column alias was provided")));
1092
1093                 /* the alias list length must be 1 */
1094                 if (list_length(colaliases) != 1)
1095                         ereport(ERROR,
1096                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
1097                           errmsg("number of aliases does not match number of columns")));
1098
1099                 /* OK, get the column alias */
1100                 attname = strVal(linitial(colaliases));
1101
1102                 tupdesc = CreateTemplateTupleDesc(1, false);
1103                 TupleDescInitEntry(tupdesc,
1104                                                    (AttrNumber) 1,
1105                                                    attname,
1106                                                    typeoid,
1107                                                    -1,
1108                                                    0);
1109         }
1110         else if (functypclass == TYPEFUNC_RECORD)
1111         {
1112                 /* XXX can't support this because typmod wasn't passed in ... */
1113                 ereport(ERROR,
1114                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
1115                                  errmsg("could not determine row description for function returning record")));
1116         }
1117         else
1118         {
1119                 /* crummy error message, but parser should have caught this */
1120                 elog(ERROR, "function in FROM has unsupported return type");
1121         }
1122
1123         return tupdesc;
1124 }