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