~ubuntu-branches/ubuntu/hardy/postgresql-8.4/hardy-backports

« back to all changes in this revision

Viewing changes to src/backend/utils/fmgr/funcapi.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2009-03-20 12:00:13 UTC
  • Revision ID: james.westby@ubuntu.com-20090320120013-hogj7egc5mjncc5g
Tags: upstream-8.4~0cvs20090328
ImportĀ upstreamĀ versionĀ 8.4~0cvs20090328

Show diffs side-by-side

added added

removed removed

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