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