]> granicus.if.org Git - postgresql/blob - src/backend/utils/fmgr/funcapi.c
First phase of OUT-parameters project. We can now define and use SQL
[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.19 2005/03/31 22:46:16 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
233         {
234                 /* handle as a generic expression; no chance to resolve RECORD */
235                 Oid             typid = exprType(expr);
236
237                 if (resultTypeId)
238                         *resultTypeId = typid;
239                 if (resultTupleDesc)
240                         *resultTupleDesc = NULL;
241                 result = get_type_func_class(typid);
242                 if (result == TYPEFUNC_COMPOSITE && resultTupleDesc)
243                         *resultTupleDesc = lookup_rowtype_tupdesc(typid, -1);
244         }
245
246         return result;
247 }
248
249 /*
250  * get_expr_result_type
251  *              As above, but work from a function's OID only
252  *
253  * This will not be able to resolve pure-RECORD results nor polymorphism.
254  */
255 TypeFuncClass
256 get_func_result_type(Oid functionId,
257                                          Oid *resultTypeId,
258                                          TupleDesc *resultTupleDesc)
259 {
260         return internal_get_result_type(functionId,
261                                                                         NULL,
262                                                                         NULL,
263                                                                         resultTypeId,
264                                                                         resultTupleDesc);
265 }
266
267 /*
268  * internal_get_result_type -- workhorse code implementing all the above
269  *
270  * funcid must always be supplied.  call_expr and rsinfo can be NULL if not
271  * available.  We will return TYPEFUNC_RECORD, and store NULL into
272  * *resultTupleDesc, if we cannot deduce the complete result rowtype from
273  * the available information.
274  */
275 static TypeFuncClass
276 internal_get_result_type(Oid funcid,
277                                                  Node *call_expr,
278                                                  ReturnSetInfo *rsinfo,
279                                                  Oid *resultTypeId,
280                                                  TupleDesc *resultTupleDesc)
281 {
282         TypeFuncClass result;
283         HeapTuple       tp;
284         Form_pg_proc procform;
285         Oid                     rettype;
286         TupleDesc       tupdesc;
287
288         /* First fetch the function's pg_proc row to inspect its rettype */
289         tp = SearchSysCache(PROCOID,
290                                                 ObjectIdGetDatum(funcid),
291                                                 0, 0, 0);
292         if (!HeapTupleIsValid(tp))
293                 elog(ERROR, "cache lookup failed for function %u", funcid);
294         procform = (Form_pg_proc) GETSTRUCT(tp);
295
296         rettype = procform->prorettype;
297
298         /* Check for OUT parameters defining a RECORD result */
299         tupdesc = build_function_result_tupdesc_t(tp);
300         if (tupdesc)
301         {
302                 /*
303                  * It has OUT parameters, so it's basically like a regular
304                  * composite type, except we have to be able to resolve any
305                  * polymorphic OUT parameters.
306                  */
307                 if (resultTypeId)
308                         *resultTypeId = rettype;
309
310                 if (resolve_polymorphic_tupdesc(tupdesc,
311                                                                                 &procform->proargtypes,
312                                                                                 call_expr))
313                 {
314                         if (tupdesc->tdtypeid == RECORDOID &&
315                                 tupdesc->tdtypmod < 0)
316                                 assign_record_type_typmod(tupdesc);
317                         if (resultTupleDesc)
318                                 *resultTupleDesc = tupdesc;
319                         result = TYPEFUNC_COMPOSITE;
320                 }
321                 else
322                 {
323                         if (resultTupleDesc)
324                                 *resultTupleDesc = NULL;
325                         result = TYPEFUNC_RECORD;
326                 }
327
328                 ReleaseSysCache(tp);
329
330                 return result;
331         }
332
333         /*
334          * If scalar polymorphic result, try to resolve it.
335          */
336         if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
337         {
338                 Oid             newrettype = exprType(call_expr);
339
340                 if (newrettype == InvalidOid)   /* this probably should not happen */
341                         ereport(ERROR,
342                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
343                                          errmsg("could not determine actual result type for function \"%s\" declared to return type %s",
344                                                         NameStr(procform->proname),
345                                                         format_type_be(rettype))));
346                 rettype = newrettype;
347         }
348
349         if (resultTypeId)
350                 *resultTypeId = rettype;
351         if (resultTupleDesc)
352                 *resultTupleDesc = NULL;                /* default result */
353
354         /* Classify the result type */
355         result = get_type_func_class(rettype);
356         switch (result)
357         {
358                 case TYPEFUNC_COMPOSITE:
359                         if (resultTupleDesc)
360                                 *resultTupleDesc = lookup_rowtype_tupdesc(rettype, -1);
361                         /* Named composite types can't have any polymorphic columns */
362                         break;
363                 case TYPEFUNC_SCALAR:
364                         break;
365                 case TYPEFUNC_RECORD:
366                         /* We must get the tupledesc from call context */
367                         if (rsinfo && IsA(rsinfo, ReturnSetInfo) &&
368                                 rsinfo->expectedDesc != NULL)
369                         {
370                                 result = TYPEFUNC_COMPOSITE;
371                                 if (resultTupleDesc)
372                                         *resultTupleDesc = rsinfo->expectedDesc;
373                                 /* Assume no polymorphic columns here, either */
374                         }
375                         break;
376                 default:
377                         break;
378         }
379
380         ReleaseSysCache(tp);
381
382         return result;
383 }
384
385 /*
386  * Given the result tuple descriptor for a function with OUT parameters,
387  * replace any polymorphic columns (ANYELEMENT/ANYARRAY) with correct data
388  * types deduced from the input arguments.  Returns TRUE if able to deduce
389  * all types, FALSE if not.
390  */
391 static bool
392 resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
393                                                         Node *call_expr)
394 {
395         int                     natts = tupdesc->natts;
396         int                     nargs = declared_args->dim1;
397         bool            have_anyelement_result = false;
398         bool            have_anyarray_result = false;
399         Oid                     anyelement_type = InvalidOid;
400         Oid                     anyarray_type = InvalidOid;
401         int                     i;
402
403         /* See if there are any polymorphic outputs; quick out if not */
404         for (i = 0; i < natts; i++)
405         {
406                 switch (tupdesc->attrs[i]->atttypid)
407                 {
408                         case ANYELEMENTOID:
409                                 have_anyelement_result = true;
410                                 break;
411                         case ANYARRAYOID:
412                                 have_anyarray_result = true;
413                                 break;
414                         default:
415                                 break;
416                 }
417         }
418         if (!have_anyelement_result && !have_anyarray_result)
419                 return true;
420
421         /*
422          * Otherwise, extract actual datatype(s) from input arguments.  (We assume
423          * the parser already validated consistency of the arguments.)
424          */
425         if (!call_expr)
426                 return false;                   /* no hope */
427
428         for (i = 0; i < nargs; i++)
429         {
430                 switch (declared_args->values[i])
431                 {
432                         case ANYELEMENTOID:
433                                 if (!OidIsValid(anyelement_type))
434                                         anyelement_type = get_call_expr_argtype(call_expr, i);
435                                 break;
436                         case ANYARRAYOID:
437                                 if (!OidIsValid(anyarray_type))
438                                         anyarray_type = get_call_expr_argtype(call_expr, i);
439                                 break;
440                         default:
441                                 break;
442                 }
443         }
444
445         /* If nothing found, parser messed up */
446         if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))
447                 return false;
448
449         /* If needed, deduce one polymorphic type from the other */
450         if (have_anyelement_result && !OidIsValid(anyelement_type))
451                 anyelement_type = resolve_generic_type(ANYELEMENTOID,
452                                                                                            anyarray_type,
453                                                                                            ANYARRAYOID);
454         if (have_anyarray_result && !OidIsValid(anyarray_type))
455                 anyarray_type = resolve_generic_type(ANYARRAYOID,
456                                                                                          anyelement_type,
457                                                                                          ANYELEMENTOID);
458
459         /* And finally replace the tuple column types as needed */
460         for (i = 0; i < natts; i++)
461         {
462                 switch (tupdesc->attrs[i]->atttypid)
463                 {
464                         case ANYELEMENTOID:
465                                 TupleDescInitEntry(tupdesc, i+1,
466                                                                    NameStr(tupdesc->attrs[i]->attname),
467                                                                    anyelement_type,
468                                                                    -1,
469                                                                    0);
470                                 break;
471                         case ANYARRAYOID:
472                                 TupleDescInitEntry(tupdesc, i+1,
473                                                                    NameStr(tupdesc->attrs[i]->attname),
474                                                                    anyarray_type,
475                                                                    -1,
476                                                                    0);
477                                 break;
478                         default:
479                                 break;
480                 }
481         }
482
483         return true;
484 }
485
486 /*
487  * get_type_func_class
488  *              Given the type OID, obtain its TYPEFUNC classification.
489  *
490  * This is intended to centralize a bunch of formerly ad-hoc code for
491  * classifying types.  The categories used here are useful for deciding
492  * how to handle functions returning the datatype.
493  */
494 static TypeFuncClass
495 get_type_func_class(Oid typid)
496 {
497         switch (get_typtype(typid))
498         {
499                 case 'c':
500                         return TYPEFUNC_COMPOSITE;
501                 case 'b':
502                 case 'd':
503                         return TYPEFUNC_SCALAR;
504                 case 'p':
505                         if (typid == RECORDOID)
506                                 return TYPEFUNC_RECORD;
507                         /*
508                          * We treat VOID and CSTRING as legitimate scalar datatypes,
509                          * mostly for the convenience of the JDBC driver (which wants
510                          * to be able to do "SELECT * FROM foo()" for all legitimately
511                          * user-callable functions).
512                          */
513                         if (typid == VOIDOID || typid == CSTRINGOID)
514                                 return TYPEFUNC_SCALAR;
515                         return TYPEFUNC_OTHER;
516         }
517         /* shouldn't get here, probably */
518         return TYPEFUNC_OTHER;
519 }
520
521
522 /*
523  * build_function_result_tupdesc_t
524  *
525  * Given a pg_proc row for a function, return a tuple descriptor for the
526  * result rowtype, or NULL if the function does not have OUT parameters.
527  *
528  * Note that this does not handle resolution of ANYELEMENT/ANYARRAY types;
529  * that is deliberate.
530  */
531 TupleDesc
532 build_function_result_tupdesc_t(HeapTuple procTuple)
533 {
534         Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(procTuple);
535         Datum           proallargtypes;
536         Datum           proargmodes;
537         Datum           proargnames;
538         bool            isnull;
539
540         /* Return NULL if the function isn't declared to return RECORD */
541         if (procform->prorettype != RECORDOID)
542                 return NULL;
543
544         /* If there are no OUT parameters, return NULL */
545         if (heap_attisnull(procTuple, Anum_pg_proc_proallargtypes) ||
546                 heap_attisnull(procTuple, Anum_pg_proc_proargmodes))
547                 return NULL;
548
549         /* Get the data out of the tuple */
550         proallargtypes = SysCacheGetAttr(PROCOID, procTuple,
551                                                                          Anum_pg_proc_proallargtypes,
552                                                                          &isnull);
553         Assert(!isnull);
554         proargmodes = SysCacheGetAttr(PROCOID, procTuple,
555                                                                   Anum_pg_proc_proargmodes,
556                                                                   &isnull);
557         Assert(!isnull);
558         proargnames = SysCacheGetAttr(PROCOID, procTuple,
559                                                                   Anum_pg_proc_proargnames,
560                                                                   &isnull);
561         if (isnull)
562                 proargnames = PointerGetDatum(NULL); /* just to be sure */
563
564         return build_function_result_tupdesc_d(proallargtypes,
565                                                                                    proargmodes,
566                                                                                    proargnames);
567 }
568
569 /*
570  * build_function_result_tupdesc_d
571  *
572  * Build a RECORD function's tupledesc from the pg_proc proallargtypes,
573  * proargmodes, and proargnames arrays.  This is split out for the
574  * convenience of ProcedureCreate, which needs to be able to compute the
575  * tupledesc before actually creating the function.
576  *
577  * Returns NULL if there are not at least two OUT or INOUT arguments.
578  */
579 TupleDesc
580 build_function_result_tupdesc_d(Datum proallargtypes,
581                                                                 Datum proargmodes,
582                                                                 Datum proargnames)
583 {
584         TupleDesc       desc;
585         ArrayType  *arr;
586         int                     numargs;
587         Oid                *argtypes;
588         char       *argmodes;
589         Datum      *argnames = NULL;
590         Oid                *outargtypes;
591         char      **outargnames;
592         int                     numoutargs;
593         int                     nargnames;
594         int                     i;
595
596         /* Can't have output args if columns are null */
597         if (proallargtypes == PointerGetDatum(NULL) ||
598                 proargmodes == PointerGetDatum(NULL))
599                 return NULL;
600
601         /*
602          * We expect the arrays to be 1-D arrays of the right types; verify that.
603          * For the OID and char arrays, we don't need to use deconstruct_array()
604          * since the array data is just going to look like a C array of values.
605          */
606         arr = DatumGetArrayTypeP(proallargtypes);       /* ensure not toasted */
607         numargs = ARR_DIMS(arr)[0];
608         if (ARR_NDIM(arr) != 1 ||
609                 numargs < 0 ||
610                 ARR_ELEMTYPE(arr) != OIDOID)
611                 elog(ERROR, "proallargtypes is not a 1-D Oid array");
612         argtypes = (Oid *) ARR_DATA_PTR(arr);
613         arr = DatumGetArrayTypeP(proargmodes);          /* ensure not toasted */
614         if (ARR_NDIM(arr) != 1 ||
615                 ARR_DIMS(arr)[0] != numargs ||
616                 ARR_ELEMTYPE(arr) != CHAROID)
617                 elog(ERROR, "proargmodes is not a 1-D char array");
618         argmodes = (char *) ARR_DATA_PTR(arr);
619         if (proargnames != PointerGetDatum(NULL))
620         {
621                 arr = DatumGetArrayTypeP(proargnames);  /* ensure not toasted */
622                 if (ARR_NDIM(arr) != 1 ||
623                         ARR_DIMS(arr)[0] != numargs ||
624                         ARR_ELEMTYPE(arr) != TEXTOID)
625                         elog(ERROR, "proargnames is not a 1-D text array");
626                 deconstruct_array(arr, TEXTOID, -1, false, 'i',
627                                                   &argnames, &nargnames);
628                 Assert(nargnames == numargs);
629         }
630
631         /* zero elements probably shouldn't happen, but handle it gracefully */
632         if (numargs <= 0)
633                 return NULL;
634
635         /* extract output-argument types and names */
636         outargtypes = (Oid *) palloc(numargs * sizeof(Oid));
637         outargnames = (char **) palloc(numargs * sizeof(char *));
638         numoutargs = 0;
639         for (i = 0; i < numargs; i++)
640         {
641                 char    *pname;
642
643                 if (argmodes[i] == PROARGMODE_IN)
644                         continue;
645                 Assert(argmodes[i] == PROARGMODE_OUT ||
646                            argmodes[i] == PROARGMODE_INOUT);
647                 outargtypes[numoutargs] = argtypes[i];
648                 if (argnames)
649                         pname = DatumGetCString(DirectFunctionCall1(textout, argnames[i]));
650                 else
651                         pname = NULL;
652                 if (pname == NULL || pname[0] == '\0')
653                 {
654                         /* Parameter is not named, so gin up a column name */
655                         pname = (char *) palloc(32);
656                         snprintf(pname, 32, "column%d", numoutargs + 1);
657                 }
658                 outargnames[numoutargs] = pname;
659                 numoutargs++;
660         }
661
662         /*
663          * If there is no output argument, or only one, the function does not
664          * return tuples.
665          */
666         if (numoutargs < 2)
667                 return NULL;
668
669         desc = CreateTemplateTupleDesc(numoutargs, false);
670         for (i = 0; i < numoutargs; i++)
671         {
672                 TupleDescInitEntry(desc, i+1,
673                                                    outargnames[i],
674                                                    outargtypes[i],
675                                                    -1,
676                                                    0);
677         }
678
679         return desc;
680 }
681
682
683 /*
684  * RelationNameGetTupleDesc
685  *
686  * Given a (possibly qualified) relation name, build a TupleDesc.
687  */
688 TupleDesc
689 RelationNameGetTupleDesc(const char *relname)
690 {
691         RangeVar   *relvar;
692         Relation        rel;
693         TupleDesc       tupdesc;
694         List       *relname_list;
695
696         /* Open relation and copy the tuple description */
697         relname_list = stringToQualifiedNameList(relname, "RelationNameGetTupleDesc");
698         relvar = makeRangeVarFromNameList(relname_list);
699         rel = relation_openrv(relvar, AccessShareLock);
700         tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
701         relation_close(rel, AccessShareLock);
702
703         return tupdesc;
704 }
705
706 /*
707  * TypeGetTupleDesc
708  *
709  * Given a type Oid, build a TupleDesc.
710  *
711  * If the type is composite, *and* a colaliases List is provided, *and*
712  * the List is of natts length, use the aliases instead of the relation
713  * attnames.  (NB: this usage is deprecated since it may result in
714  * creation of unnecessary transient record types.)
715  *
716  * If the type is a base type, a single item alias List is required.
717  */
718 TupleDesc
719 TypeGetTupleDesc(Oid typeoid, List *colaliases)
720 {
721         TypeFuncClass functypclass = get_type_func_class(typeoid);
722         TupleDesc       tupdesc = NULL;
723
724         /*
725          * Build a suitable tupledesc representing the output rows
726          */
727         if (functypclass == TYPEFUNC_COMPOSITE)
728         {
729                 /* Composite data type, e.g. a table's row type */
730                 tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(typeoid, -1));
731
732                 if (colaliases != NIL)
733                 {
734                         int                     natts = tupdesc->natts;
735                         int                     varattno;
736
737                         /* does the list length match the number of attributes? */
738                         if (list_length(colaliases) != natts)
739                                 ereport(ERROR,
740                                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
741                                                  errmsg("number of aliases does not match number of columns")));
742
743                         /* OK, use the aliases instead */
744                         for (varattno = 0; varattno < natts; varattno++)
745                         {
746                                 char       *label = strVal(list_nth(colaliases, varattno));
747
748                                 if (label != NULL)
749                                         namestrcpy(&(tupdesc->attrs[varattno]->attname), label);
750                         }
751
752                         /* The tuple type is now an anonymous record type */
753                         tupdesc->tdtypeid = RECORDOID;
754                         tupdesc->tdtypmod = -1;
755                 }
756         }
757         else if (functypclass == TYPEFUNC_SCALAR)
758         {
759                 /* Base data type, i.e. scalar */
760                 char       *attname;
761
762                 /* the alias list is required for base types */
763                 if (colaliases == NIL)
764                         ereport(ERROR,
765                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
766                                          errmsg("no column alias was provided")));
767
768                 /* the alias list length must be 1 */
769                 if (list_length(colaliases) != 1)
770                         ereport(ERROR,
771                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
772                                          errmsg("number of aliases does not match number of columns")));
773
774                 /* OK, get the column alias */
775                 attname = strVal(linitial(colaliases));
776
777                 tupdesc = CreateTemplateTupleDesc(1, false);
778                 TupleDescInitEntry(tupdesc,
779                                                    (AttrNumber) 1,
780                                                    attname,
781                                                    typeoid,
782                                                    -1,
783                                                    0);
784         }
785         else if (functypclass == TYPEFUNC_RECORD)
786         {
787                 /* XXX can't support this because typmod wasn't passed in ... */
788                 ereport(ERROR,
789                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
790                                  errmsg("could not determine row description for function returning record")));
791         }
792         else
793         {
794                 /* crummy error message, but parser should have caught this */
795                 elog(ERROR, "function in FROM has unsupported return type");
796         }
797
798         return tupdesc;
799 }