/src/postgres/src/backend/utils/fmgr/funcapi.c
Line | Count | Source (jump to first uncovered line) |
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-2025, 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 "miscadmin.h" |
23 | | #include "nodes/nodeFuncs.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/tuplestore.h" |
32 | | #include "utils/typcache.h" |
33 | | |
34 | | |
35 | | typedef struct polymorphic_actuals |
36 | | { |
37 | | Oid anyelement_type; /* anyelement mapping, if known */ |
38 | | Oid anyarray_type; /* anyarray mapping, if known */ |
39 | | Oid anyrange_type; /* anyrange mapping, if known */ |
40 | | Oid anymultirange_type; /* anymultirange mapping, if known */ |
41 | | } polymorphic_actuals; |
42 | | |
43 | | static void shutdown_MultiFuncCall(Datum arg); |
44 | | static TypeFuncClass internal_get_result_type(Oid funcid, |
45 | | Node *call_expr, |
46 | | ReturnSetInfo *rsinfo, |
47 | | Oid *resultTypeId, |
48 | | TupleDesc *resultTupleDesc); |
49 | | static void resolve_anyelement_from_others(polymorphic_actuals *actuals); |
50 | | static void resolve_anyarray_from_others(polymorphic_actuals *actuals); |
51 | | static void resolve_anyrange_from_others(polymorphic_actuals *actuals); |
52 | | static void resolve_anymultirange_from_others(polymorphic_actuals *actuals); |
53 | | static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc, |
54 | | oidvector *declared_args, |
55 | | Node *call_expr); |
56 | | static TypeFuncClass get_type_func_class(Oid typid, Oid *base_typeid); |
57 | | |
58 | | |
59 | | /* |
60 | | * InitMaterializedSRF |
61 | | * |
62 | | * Helper function to build the state of a set-returning function used |
63 | | * in the context of a single call with materialize mode. This code |
64 | | * includes sanity checks on ReturnSetInfo, creates the Tuplestore and |
65 | | * the TupleDesc used with the function and stores them into the |
66 | | * function's ReturnSetInfo. |
67 | | * |
68 | | * "flags" can be set to MAT_SRF_USE_EXPECTED_DESC, to use the tuple |
69 | | * descriptor coming from expectedDesc, which is the tuple descriptor |
70 | | * expected by the caller. MAT_SRF_BLESS can be set to complete the |
71 | | * information associated to the tuple descriptor, which is necessary |
72 | | * in some cases where the tuple descriptor comes from a transient |
73 | | * RECORD datatype. |
74 | | */ |
75 | | void |
76 | | InitMaterializedSRF(FunctionCallInfo fcinfo, bits32 flags) |
77 | 0 | { |
78 | 0 | bool random_access; |
79 | 0 | ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; |
80 | 0 | Tuplestorestate *tupstore; |
81 | 0 | MemoryContext old_context, |
82 | 0 | per_query_ctx; |
83 | 0 | TupleDesc stored_tupdesc; |
84 | | |
85 | | /* check to see if caller supports returning a tuplestore */ |
86 | 0 | if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) |
87 | 0 | ereport(ERROR, |
88 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
89 | 0 | errmsg("set-valued function called in context that cannot accept a set"))); |
90 | 0 | if (!(rsinfo->allowedModes & SFRM_Materialize) || |
91 | 0 | ((flags & MAT_SRF_USE_EXPECTED_DESC) != 0 && rsinfo->expectedDesc == NULL)) |
92 | 0 | ereport(ERROR, |
93 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
94 | 0 | errmsg("materialize mode required, but it is not allowed in this context"))); |
95 | | |
96 | | /* |
97 | | * Store the tuplestore and the tuple descriptor in ReturnSetInfo. This |
98 | | * must be done in the per-query memory context. |
99 | | */ |
100 | 0 | per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; |
101 | 0 | old_context = MemoryContextSwitchTo(per_query_ctx); |
102 | | |
103 | | /* build a tuple descriptor for our result type */ |
104 | 0 | if ((flags & MAT_SRF_USE_EXPECTED_DESC) != 0) |
105 | 0 | stored_tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc); |
106 | 0 | else |
107 | 0 | { |
108 | 0 | if (get_call_result_type(fcinfo, NULL, &stored_tupdesc) != TYPEFUNC_COMPOSITE) |
109 | 0 | elog(ERROR, "return type must be a row type"); |
110 | 0 | } |
111 | | |
112 | | /* If requested, bless the tuple descriptor */ |
113 | 0 | if ((flags & MAT_SRF_BLESS) != 0) |
114 | 0 | BlessTupleDesc(stored_tupdesc); |
115 | |
|
116 | 0 | random_access = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0; |
117 | |
|
118 | 0 | tupstore = tuplestore_begin_heap(random_access, false, work_mem); |
119 | 0 | rsinfo->returnMode = SFRM_Materialize; |
120 | 0 | rsinfo->setResult = tupstore; |
121 | 0 | rsinfo->setDesc = stored_tupdesc; |
122 | 0 | MemoryContextSwitchTo(old_context); |
123 | 0 | } |
124 | | |
125 | | |
126 | | /* |
127 | | * init_MultiFuncCall |
128 | | * Create an empty FuncCallContext data structure |
129 | | * and do some other basic Multi-function call setup |
130 | | * and error checking |
131 | | */ |
132 | | FuncCallContext * |
133 | | init_MultiFuncCall(PG_FUNCTION_ARGS) |
134 | 0 | { |
135 | 0 | FuncCallContext *retval; |
136 | | |
137 | | /* |
138 | | * Bail if we're called in the wrong context |
139 | | */ |
140 | 0 | if (fcinfo->resultinfo == NULL || !IsA(fcinfo->resultinfo, ReturnSetInfo)) |
141 | 0 | ereport(ERROR, |
142 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
143 | 0 | errmsg("set-valued function called in context that cannot accept a set"))); |
144 | | |
145 | 0 | if (fcinfo->flinfo->fn_extra == NULL) |
146 | 0 | { |
147 | | /* |
148 | | * First call |
149 | | */ |
150 | 0 | ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; |
151 | 0 | MemoryContext multi_call_ctx; |
152 | | |
153 | | /* |
154 | | * Create a suitably long-lived context to hold cross-call data |
155 | | */ |
156 | 0 | multi_call_ctx = AllocSetContextCreate(fcinfo->flinfo->fn_mcxt, |
157 | 0 | "SRF multi-call context", |
158 | 0 | ALLOCSET_SMALL_SIZES); |
159 | | |
160 | | /* |
161 | | * Allocate suitably long-lived space and zero it |
162 | | */ |
163 | 0 | retval = (FuncCallContext *) |
164 | 0 | MemoryContextAllocZero(multi_call_ctx, |
165 | 0 | sizeof(FuncCallContext)); |
166 | | |
167 | | /* |
168 | | * initialize the elements |
169 | | */ |
170 | 0 | retval->call_cntr = 0; |
171 | 0 | retval->max_calls = 0; |
172 | 0 | retval->user_fctx = NULL; |
173 | 0 | retval->attinmeta = NULL; |
174 | 0 | retval->tuple_desc = NULL; |
175 | 0 | retval->multi_call_memory_ctx = multi_call_ctx; |
176 | | |
177 | | /* |
178 | | * save the pointer for cross-call use |
179 | | */ |
180 | 0 | fcinfo->flinfo->fn_extra = retval; |
181 | | |
182 | | /* |
183 | | * Ensure we will get shut down cleanly if the exprcontext is not run |
184 | | * to completion. |
185 | | */ |
186 | 0 | RegisterExprContextCallback(rsi->econtext, |
187 | 0 | shutdown_MultiFuncCall, |
188 | 0 | PointerGetDatum(fcinfo->flinfo)); |
189 | 0 | } |
190 | 0 | else |
191 | 0 | { |
192 | | /* second and subsequent calls */ |
193 | 0 | elog(ERROR, "init_MultiFuncCall cannot be called more than once"); |
194 | | |
195 | | /* never reached, but keep compiler happy */ |
196 | 0 | retval = NULL; |
197 | 0 | } |
198 | | |
199 | 0 | return retval; |
200 | 0 | } |
201 | | |
202 | | /* |
203 | | * per_MultiFuncCall |
204 | | * |
205 | | * Do Multi-function per-call setup |
206 | | */ |
207 | | FuncCallContext * |
208 | | per_MultiFuncCall(PG_FUNCTION_ARGS) |
209 | 0 | { |
210 | 0 | FuncCallContext *retval = (FuncCallContext *) fcinfo->flinfo->fn_extra; |
211 | |
|
212 | 0 | return retval; |
213 | 0 | } |
214 | | |
215 | | /* |
216 | | * end_MultiFuncCall |
217 | | * Clean up after init_MultiFuncCall |
218 | | */ |
219 | | void |
220 | | end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx) |
221 | 0 | { |
222 | 0 | ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; |
223 | | |
224 | | /* Deregister the shutdown callback */ |
225 | 0 | UnregisterExprContextCallback(rsi->econtext, |
226 | 0 | shutdown_MultiFuncCall, |
227 | 0 | PointerGetDatum(fcinfo->flinfo)); |
228 | | |
229 | | /* But use it to do the real work */ |
230 | 0 | shutdown_MultiFuncCall(PointerGetDatum(fcinfo->flinfo)); |
231 | 0 | } |
232 | | |
233 | | /* |
234 | | * shutdown_MultiFuncCall |
235 | | * Shutdown function to clean up after init_MultiFuncCall |
236 | | */ |
237 | | static void |
238 | | shutdown_MultiFuncCall(Datum arg) |
239 | 0 | { |
240 | 0 | FmgrInfo *flinfo = (FmgrInfo *) DatumGetPointer(arg); |
241 | 0 | FuncCallContext *funcctx = (FuncCallContext *) flinfo->fn_extra; |
242 | | |
243 | | /* unbind from flinfo */ |
244 | 0 | flinfo->fn_extra = NULL; |
245 | | |
246 | | /* |
247 | | * Delete context that holds all multi-call data, including the |
248 | | * FuncCallContext itself |
249 | | */ |
250 | 0 | MemoryContextDelete(funcctx->multi_call_memory_ctx); |
251 | 0 | } |
252 | | |
253 | | |
254 | | /* |
255 | | * get_call_result_type |
256 | | * Given a function's call info record, determine the kind of datatype |
257 | | * it is supposed to return. If resultTypeId isn't NULL, *resultTypeId |
258 | | * receives the actual datatype OID (this is mainly useful for scalar |
259 | | * result types). If resultTupleDesc isn't NULL, *resultTupleDesc |
260 | | * receives a pointer to a TupleDesc when the result is of a composite |
261 | | * type, or NULL when it's a scalar result. |
262 | | * |
263 | | * One hard case that this handles is resolution of actual rowtypes for |
264 | | * functions returning RECORD (from either the function's OUT parameter |
265 | | * list, or a ReturnSetInfo context node). TYPEFUNC_RECORD is returned |
266 | | * only when we couldn't resolve the actual rowtype for lack of information. |
267 | | * |
268 | | * The other hard case that this handles is resolution of polymorphism. |
269 | | * We will never return polymorphic pseudotypes (ANYELEMENT etc), either |
270 | | * as a scalar result type or as a component of a rowtype. |
271 | | * |
272 | | * This function is relatively expensive --- in a function returning set, |
273 | | * try to call it only the first time through. |
274 | | */ |
275 | | TypeFuncClass |
276 | | get_call_result_type(FunctionCallInfo fcinfo, |
277 | | Oid *resultTypeId, |
278 | | TupleDesc *resultTupleDesc) |
279 | 0 | { |
280 | 0 | return internal_get_result_type(fcinfo->flinfo->fn_oid, |
281 | 0 | fcinfo->flinfo->fn_expr, |
282 | 0 | (ReturnSetInfo *) fcinfo->resultinfo, |
283 | 0 | resultTypeId, |
284 | 0 | resultTupleDesc); |
285 | 0 | } |
286 | | |
287 | | /* |
288 | | * get_expr_result_type |
289 | | * As above, but work from a calling expression node tree |
290 | | * |
291 | | * Beware of using this on the funcexpr of a RTE that has a coldeflist. |
292 | | * The correct conclusion in such cases is always that the function returns |
293 | | * RECORD with the columns defined by the coldeflist fields (funccolnames etc). |
294 | | * If it does not, it's the executor's responsibility to catch the discrepancy |
295 | | * at runtime; but code processing the query in advance of that point might |
296 | | * come to inconsistent conclusions if it checks the actual expression. |
297 | | */ |
298 | | TypeFuncClass |
299 | | get_expr_result_type(Node *expr, |
300 | | Oid *resultTypeId, |
301 | | TupleDesc *resultTupleDesc) |
302 | 0 | { |
303 | 0 | TypeFuncClass result; |
304 | |
|
305 | 0 | if (expr && IsA(expr, FuncExpr)) |
306 | 0 | result = internal_get_result_type(((FuncExpr *) expr)->funcid, |
307 | 0 | expr, |
308 | 0 | NULL, |
309 | 0 | resultTypeId, |
310 | 0 | resultTupleDesc); |
311 | 0 | else if (expr && IsA(expr, OpExpr)) |
312 | 0 | result = internal_get_result_type(get_opcode(((OpExpr *) expr)->opno), |
313 | 0 | expr, |
314 | 0 | NULL, |
315 | 0 | resultTypeId, |
316 | 0 | resultTupleDesc); |
317 | 0 | else if (expr && IsA(expr, RowExpr) && |
318 | 0 | ((RowExpr *) expr)->row_typeid == RECORDOID) |
319 | 0 | { |
320 | | /* We can resolve the record type by generating the tupdesc directly */ |
321 | 0 | RowExpr *rexpr = (RowExpr *) expr; |
322 | 0 | TupleDesc tupdesc; |
323 | 0 | AttrNumber i = 1; |
324 | 0 | ListCell *lcc, |
325 | 0 | *lcn; |
326 | |
|
327 | 0 | tupdesc = CreateTemplateTupleDesc(list_length(rexpr->args)); |
328 | 0 | Assert(list_length(rexpr->args) == list_length(rexpr->colnames)); |
329 | 0 | forboth(lcc, rexpr->args, lcn, rexpr->colnames) |
330 | 0 | { |
331 | 0 | Node *col = (Node *) lfirst(lcc); |
332 | 0 | char *colname = strVal(lfirst(lcn)); |
333 | |
|
334 | 0 | TupleDescInitEntry(tupdesc, i, |
335 | 0 | colname, |
336 | 0 | exprType(col), |
337 | 0 | exprTypmod(col), |
338 | 0 | 0); |
339 | 0 | TupleDescInitEntryCollation(tupdesc, i, |
340 | 0 | exprCollation(col)); |
341 | 0 | i++; |
342 | 0 | } |
343 | 0 | if (resultTypeId) |
344 | 0 | *resultTypeId = rexpr->row_typeid; |
345 | 0 | if (resultTupleDesc) |
346 | 0 | *resultTupleDesc = BlessTupleDesc(tupdesc); |
347 | 0 | return TYPEFUNC_COMPOSITE; |
348 | 0 | } |
349 | 0 | else if (expr && IsA(expr, Const) && |
350 | 0 | ((Const *) expr)->consttype == RECORDOID && |
351 | 0 | !((Const *) expr)->constisnull) |
352 | 0 | { |
353 | | /* |
354 | | * When EXPLAIN'ing some queries with SEARCH/CYCLE clauses, we may |
355 | | * need to resolve field names of a RECORD-type Const. The datum |
356 | | * should contain a typmod that will tell us that. |
357 | | */ |
358 | 0 | HeapTupleHeader rec; |
359 | 0 | Oid tupType; |
360 | 0 | int32 tupTypmod; |
361 | |
|
362 | 0 | rec = DatumGetHeapTupleHeader(((Const *) expr)->constvalue); |
363 | 0 | tupType = HeapTupleHeaderGetTypeId(rec); |
364 | 0 | tupTypmod = HeapTupleHeaderGetTypMod(rec); |
365 | 0 | if (resultTypeId) |
366 | 0 | *resultTypeId = tupType; |
367 | 0 | if (tupType != RECORDOID || tupTypmod >= 0) |
368 | 0 | { |
369 | | /* Should be able to look it up */ |
370 | 0 | if (resultTupleDesc) |
371 | 0 | *resultTupleDesc = lookup_rowtype_tupdesc_copy(tupType, |
372 | 0 | tupTypmod); |
373 | 0 | return TYPEFUNC_COMPOSITE; |
374 | 0 | } |
375 | 0 | else |
376 | 0 | { |
377 | | /* This shouldn't really happen ... */ |
378 | 0 | if (resultTupleDesc) |
379 | 0 | *resultTupleDesc = NULL; |
380 | 0 | return TYPEFUNC_RECORD; |
381 | 0 | } |
382 | 0 | } |
383 | 0 | else |
384 | 0 | { |
385 | | /* handle as a generic expression; no chance to resolve RECORD */ |
386 | 0 | Oid typid = exprType(expr); |
387 | 0 | Oid base_typid; |
388 | |
|
389 | 0 | if (resultTypeId) |
390 | 0 | *resultTypeId = typid; |
391 | 0 | if (resultTupleDesc) |
392 | 0 | *resultTupleDesc = NULL; |
393 | 0 | result = get_type_func_class(typid, &base_typid); |
394 | 0 | if ((result == TYPEFUNC_COMPOSITE || |
395 | 0 | result == TYPEFUNC_COMPOSITE_DOMAIN) && |
396 | 0 | resultTupleDesc) |
397 | 0 | *resultTupleDesc = lookup_rowtype_tupdesc_copy(base_typid, -1); |
398 | 0 | } |
399 | | |
400 | 0 | return result; |
401 | 0 | } |
402 | | |
403 | | /* |
404 | | * get_func_result_type |
405 | | * As above, but work from a function's OID only |
406 | | * |
407 | | * This will not be able to resolve pure-RECORD results nor polymorphism. |
408 | | */ |
409 | | TypeFuncClass |
410 | | get_func_result_type(Oid functionId, |
411 | | Oid *resultTypeId, |
412 | | TupleDesc *resultTupleDesc) |
413 | 0 | { |
414 | 0 | return internal_get_result_type(functionId, |
415 | 0 | NULL, |
416 | 0 | NULL, |
417 | 0 | resultTypeId, |
418 | 0 | resultTupleDesc); |
419 | 0 | } |
420 | | |
421 | | /* |
422 | | * internal_get_result_type -- workhorse code implementing all the above |
423 | | * |
424 | | * funcid must always be supplied. call_expr and rsinfo can be NULL if not |
425 | | * available. We will return TYPEFUNC_RECORD, and store NULL into |
426 | | * *resultTupleDesc, if we cannot deduce the complete result rowtype from |
427 | | * the available information. |
428 | | */ |
429 | | static TypeFuncClass |
430 | | internal_get_result_type(Oid funcid, |
431 | | Node *call_expr, |
432 | | ReturnSetInfo *rsinfo, |
433 | | Oid *resultTypeId, |
434 | | TupleDesc *resultTupleDesc) |
435 | 0 | { |
436 | 0 | TypeFuncClass result; |
437 | 0 | HeapTuple tp; |
438 | 0 | Form_pg_proc procform; |
439 | 0 | Oid rettype; |
440 | 0 | Oid base_rettype; |
441 | 0 | TupleDesc tupdesc; |
442 | | |
443 | | /* First fetch the function's pg_proc row to inspect its rettype */ |
444 | 0 | tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); |
445 | 0 | if (!HeapTupleIsValid(tp)) |
446 | 0 | elog(ERROR, "cache lookup failed for function %u", funcid); |
447 | 0 | procform = (Form_pg_proc) GETSTRUCT(tp); |
448 | |
|
449 | 0 | rettype = procform->prorettype; |
450 | | |
451 | | /* Check for OUT parameters defining a RECORD result */ |
452 | 0 | tupdesc = build_function_result_tupdesc_t(tp); |
453 | 0 | if (tupdesc) |
454 | 0 | { |
455 | | /* |
456 | | * It has OUT parameters, so it's basically like a regular composite |
457 | | * type, except we have to be able to resolve any polymorphic OUT |
458 | | * parameters. |
459 | | */ |
460 | 0 | if (resultTypeId) |
461 | 0 | *resultTypeId = rettype; |
462 | |
|
463 | 0 | if (resolve_polymorphic_tupdesc(tupdesc, |
464 | 0 | &procform->proargtypes, |
465 | 0 | call_expr)) |
466 | 0 | { |
467 | 0 | if (tupdesc->tdtypeid == RECORDOID && |
468 | 0 | tupdesc->tdtypmod < 0) |
469 | 0 | assign_record_type_typmod(tupdesc); |
470 | 0 | if (resultTupleDesc) |
471 | 0 | *resultTupleDesc = tupdesc; |
472 | 0 | result = TYPEFUNC_COMPOSITE; |
473 | 0 | } |
474 | 0 | else |
475 | 0 | { |
476 | 0 | if (resultTupleDesc) |
477 | 0 | *resultTupleDesc = NULL; |
478 | 0 | result = TYPEFUNC_RECORD; |
479 | 0 | } |
480 | |
|
481 | 0 | ReleaseSysCache(tp); |
482 | |
|
483 | 0 | return result; |
484 | 0 | } |
485 | | |
486 | | /* |
487 | | * If scalar polymorphic result, try to resolve it. |
488 | | */ |
489 | 0 | if (IsPolymorphicType(rettype)) |
490 | 0 | { |
491 | 0 | Oid newrettype = exprType(call_expr); |
492 | |
|
493 | 0 | if (newrettype == InvalidOid) /* this probably should not happen */ |
494 | 0 | ereport(ERROR, |
495 | 0 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
496 | 0 | errmsg("could not determine actual result type for function \"%s\" declared to return type %s", |
497 | 0 | NameStr(procform->proname), |
498 | 0 | format_type_be(rettype)))); |
499 | 0 | rettype = newrettype; |
500 | 0 | } |
501 | | |
502 | 0 | if (resultTypeId) |
503 | 0 | *resultTypeId = rettype; |
504 | 0 | if (resultTupleDesc) |
505 | 0 | *resultTupleDesc = NULL; /* default result */ |
506 | | |
507 | | /* Classify the result type */ |
508 | 0 | result = get_type_func_class(rettype, &base_rettype); |
509 | 0 | switch (result) |
510 | 0 | { |
511 | 0 | case TYPEFUNC_COMPOSITE: |
512 | 0 | case TYPEFUNC_COMPOSITE_DOMAIN: |
513 | 0 | if (resultTupleDesc) |
514 | 0 | *resultTupleDesc = lookup_rowtype_tupdesc_copy(base_rettype, -1); |
515 | | /* Named composite types can't have any polymorphic columns */ |
516 | 0 | break; |
517 | 0 | case TYPEFUNC_SCALAR: |
518 | 0 | break; |
519 | 0 | case TYPEFUNC_RECORD: |
520 | | /* We must get the tupledesc from call context */ |
521 | 0 | if (rsinfo && IsA(rsinfo, ReturnSetInfo) && |
522 | 0 | rsinfo->expectedDesc != NULL) |
523 | 0 | { |
524 | 0 | result = TYPEFUNC_COMPOSITE; |
525 | 0 | if (resultTupleDesc) |
526 | 0 | *resultTupleDesc = rsinfo->expectedDesc; |
527 | | /* Assume no polymorphic columns here, either */ |
528 | 0 | } |
529 | 0 | break; |
530 | 0 | default: |
531 | 0 | break; |
532 | 0 | } |
533 | | |
534 | 0 | ReleaseSysCache(tp); |
535 | |
|
536 | 0 | return result; |
537 | 0 | } |
538 | | |
539 | | /* |
540 | | * get_expr_result_tupdesc |
541 | | * Get a tupdesc describing the result of a composite-valued expression |
542 | | * |
543 | | * If expression is not composite or rowtype can't be determined, returns NULL |
544 | | * if noError is true, else throws error. |
545 | | * |
546 | | * This is a simpler version of get_expr_result_type() for use when the caller |
547 | | * is only interested in determinate rowtype results. As with that function, |
548 | | * beware of using this on the funcexpr of a RTE that has a coldeflist. |
549 | | */ |
550 | | TupleDesc |
551 | | get_expr_result_tupdesc(Node *expr, bool noError) |
552 | 0 | { |
553 | 0 | TupleDesc tupleDesc; |
554 | 0 | TypeFuncClass functypclass; |
555 | |
|
556 | 0 | functypclass = get_expr_result_type(expr, NULL, &tupleDesc); |
557 | |
|
558 | 0 | if (functypclass == TYPEFUNC_COMPOSITE || |
559 | 0 | functypclass == TYPEFUNC_COMPOSITE_DOMAIN) |
560 | 0 | return tupleDesc; |
561 | | |
562 | 0 | if (!noError) |
563 | 0 | { |
564 | 0 | Oid exprTypeId = exprType(expr); |
565 | |
|
566 | 0 | if (exprTypeId != RECORDOID) |
567 | 0 | ereport(ERROR, |
568 | 0 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
569 | 0 | errmsg("type %s is not composite", |
570 | 0 | format_type_be(exprTypeId)))); |
571 | 0 | else |
572 | 0 | ereport(ERROR, |
573 | 0 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
574 | 0 | errmsg("record type has not been registered"))); |
575 | 0 | } |
576 | | |
577 | 0 | return NULL; |
578 | 0 | } |
579 | | |
580 | | /* |
581 | | * Resolve actual type of ANYELEMENT from other polymorphic inputs |
582 | | * |
583 | | * Note: the error cases here and in the sibling functions below are not |
584 | | * really user-facing; they could only occur if the function signature is |
585 | | * incorrect or the parser failed to enforce consistency of the actual |
586 | | * argument types. Hence, we don't sweat too much over the error messages. |
587 | | */ |
588 | | static void |
589 | | resolve_anyelement_from_others(polymorphic_actuals *actuals) |
590 | 0 | { |
591 | 0 | if (OidIsValid(actuals->anyarray_type)) |
592 | 0 | { |
593 | | /* Use the element type corresponding to actual type */ |
594 | 0 | Oid array_base_type = getBaseType(actuals->anyarray_type); |
595 | 0 | Oid array_typelem = get_element_type(array_base_type); |
596 | |
|
597 | 0 | if (!OidIsValid(array_typelem)) |
598 | 0 | ereport(ERROR, |
599 | 0 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
600 | 0 | errmsg("argument declared %s is not an array but type %s", |
601 | 0 | "anyarray", |
602 | 0 | format_type_be(array_base_type)))); |
603 | 0 | actuals->anyelement_type = array_typelem; |
604 | 0 | } |
605 | 0 | else if (OidIsValid(actuals->anyrange_type)) |
606 | 0 | { |
607 | | /* Use the element type corresponding to actual type */ |
608 | 0 | Oid range_base_type = getBaseType(actuals->anyrange_type); |
609 | 0 | Oid range_typelem = get_range_subtype(range_base_type); |
610 | |
|
611 | 0 | if (!OidIsValid(range_typelem)) |
612 | 0 | ereport(ERROR, |
613 | 0 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
614 | 0 | errmsg("argument declared %s is not a range type but type %s", |
615 | 0 | "anyrange", |
616 | 0 | format_type_be(range_base_type)))); |
617 | 0 | actuals->anyelement_type = range_typelem; |
618 | 0 | } |
619 | 0 | else if (OidIsValid(actuals->anymultirange_type)) |
620 | 0 | { |
621 | | /* Use the element type based on the multirange type */ |
622 | 0 | Oid multirange_base_type; |
623 | 0 | Oid multirange_typelem; |
624 | 0 | Oid range_base_type; |
625 | 0 | Oid range_typelem; |
626 | |
|
627 | 0 | multirange_base_type = getBaseType(actuals->anymultirange_type); |
628 | 0 | multirange_typelem = get_multirange_range(multirange_base_type); |
629 | 0 | if (!OidIsValid(multirange_typelem)) |
630 | 0 | ereport(ERROR, |
631 | 0 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
632 | 0 | errmsg("argument declared %s is not a multirange type but type %s", |
633 | 0 | "anymultirange", |
634 | 0 | format_type_be(multirange_base_type)))); |
635 | | |
636 | 0 | range_base_type = getBaseType(multirange_typelem); |
637 | 0 | range_typelem = get_range_subtype(range_base_type); |
638 | |
|
639 | 0 | if (!OidIsValid(range_typelem)) |
640 | 0 | ereport(ERROR, |
641 | 0 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
642 | 0 | errmsg("argument declared %s does not contain a range type but type %s", |
643 | 0 | "anymultirange", |
644 | 0 | format_type_be(range_base_type)))); |
645 | 0 | actuals->anyelement_type = range_typelem; |
646 | 0 | } |
647 | 0 | else |
648 | 0 | elog(ERROR, "could not determine polymorphic type"); |
649 | 0 | } |
650 | | |
651 | | /* |
652 | | * Resolve actual type of ANYARRAY from other polymorphic inputs |
653 | | */ |
654 | | static void |
655 | | resolve_anyarray_from_others(polymorphic_actuals *actuals) |
656 | 0 | { |
657 | | /* If we don't know ANYELEMENT, resolve that first */ |
658 | 0 | if (!OidIsValid(actuals->anyelement_type)) |
659 | 0 | resolve_anyelement_from_others(actuals); |
660 | |
|
661 | 0 | if (OidIsValid(actuals->anyelement_type)) |
662 | 0 | { |
663 | | /* Use the array type corresponding to actual type */ |
664 | 0 | Oid array_typeid = get_array_type(actuals->anyelement_type); |
665 | |
|
666 | 0 | if (!OidIsValid(array_typeid)) |
667 | 0 | ereport(ERROR, |
668 | 0 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
669 | 0 | errmsg("could not find array type for data type %s", |
670 | 0 | format_type_be(actuals->anyelement_type)))); |
671 | 0 | actuals->anyarray_type = array_typeid; |
672 | 0 | } |
673 | 0 | else |
674 | 0 | elog(ERROR, "could not determine polymorphic type"); |
675 | 0 | } |
676 | | |
677 | | /* |
678 | | * Resolve actual type of ANYRANGE from other polymorphic inputs |
679 | | */ |
680 | | static void |
681 | | resolve_anyrange_from_others(polymorphic_actuals *actuals) |
682 | 0 | { |
683 | | /* |
684 | | * We can't deduce a range type from other polymorphic array or base |
685 | | * types, because there may be multiple range types with the same subtype, |
686 | | * but we can deduce it from a polymorphic multirange type. |
687 | | */ |
688 | 0 | if (OidIsValid(actuals->anymultirange_type)) |
689 | 0 | { |
690 | | /* Use the element type based on the multirange type */ |
691 | 0 | Oid multirange_base_type = getBaseType(actuals->anymultirange_type); |
692 | 0 | Oid multirange_typelem = get_multirange_range(multirange_base_type); |
693 | |
|
694 | 0 | if (!OidIsValid(multirange_typelem)) |
695 | 0 | ereport(ERROR, |
696 | 0 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
697 | 0 | errmsg("argument declared %s is not a multirange type but type %s", |
698 | 0 | "anymultirange", |
699 | 0 | format_type_be(multirange_base_type)))); |
700 | 0 | actuals->anyrange_type = multirange_typelem; |
701 | 0 | } |
702 | 0 | else |
703 | 0 | elog(ERROR, "could not determine polymorphic type"); |
704 | 0 | } |
705 | | |
706 | | /* |
707 | | * Resolve actual type of ANYMULTIRANGE from other polymorphic inputs |
708 | | */ |
709 | | static void |
710 | | resolve_anymultirange_from_others(polymorphic_actuals *actuals) |
711 | 0 | { |
712 | | /* |
713 | | * We can't deduce a multirange type from polymorphic array or base types, |
714 | | * because there may be multiple range types with the same subtype, but we |
715 | | * can deduce it from a polymorphic range type. |
716 | | */ |
717 | 0 | if (OidIsValid(actuals->anyrange_type)) |
718 | 0 | { |
719 | 0 | Oid range_base_type = getBaseType(actuals->anyrange_type); |
720 | 0 | Oid multirange_typeid = get_range_multirange(range_base_type); |
721 | |
|
722 | 0 | if (!OidIsValid(multirange_typeid)) |
723 | 0 | ereport(ERROR, |
724 | 0 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
725 | 0 | errmsg("could not find multirange type for data type %s", |
726 | 0 | format_type_be(actuals->anyrange_type)))); |
727 | 0 | actuals->anymultirange_type = multirange_typeid; |
728 | 0 | } |
729 | 0 | else |
730 | 0 | elog(ERROR, "could not determine polymorphic type"); |
731 | 0 | } |
732 | | |
733 | | /* |
734 | | * Given the result tuple descriptor for a function with OUT parameters, |
735 | | * replace any polymorphic column types (ANYELEMENT etc) in the tupdesc |
736 | | * with concrete data types deduced from the input arguments. |
737 | | * declared_args is an oidvector of the function's declared input arg types |
738 | | * (showing which are polymorphic), and call_expr is the call expression. |
739 | | * |
740 | | * Returns true if able to deduce all types, false if necessary information |
741 | | * is not provided (call_expr is NULL or arg types aren't identifiable). |
742 | | */ |
743 | | static bool |
744 | | resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, |
745 | | Node *call_expr) |
746 | 0 | { |
747 | 0 | int natts = tupdesc->natts; |
748 | 0 | int nargs = declared_args->dim1; |
749 | 0 | bool have_polymorphic_result = false; |
750 | 0 | bool have_anyelement_result = false; |
751 | 0 | bool have_anyarray_result = false; |
752 | 0 | bool have_anyrange_result = false; |
753 | 0 | bool have_anymultirange_result = false; |
754 | 0 | bool have_anycompatible_result = false; |
755 | 0 | bool have_anycompatible_array_result = false; |
756 | 0 | bool have_anycompatible_range_result = false; |
757 | 0 | bool have_anycompatible_multirange_result = false; |
758 | 0 | polymorphic_actuals poly_actuals; |
759 | 0 | polymorphic_actuals anyc_actuals; |
760 | 0 | Oid anycollation = InvalidOid; |
761 | 0 | Oid anycompatcollation = InvalidOid; |
762 | 0 | int i; |
763 | | |
764 | | /* See if there are any polymorphic outputs; quick out if not */ |
765 | 0 | for (i = 0; i < natts; i++) |
766 | 0 | { |
767 | 0 | switch (TupleDescAttr(tupdesc, i)->atttypid) |
768 | 0 | { |
769 | 0 | case ANYELEMENTOID: |
770 | 0 | case ANYNONARRAYOID: |
771 | 0 | case ANYENUMOID: |
772 | 0 | have_polymorphic_result = true; |
773 | 0 | have_anyelement_result = true; |
774 | 0 | break; |
775 | 0 | case ANYARRAYOID: |
776 | 0 | have_polymorphic_result = true; |
777 | 0 | have_anyarray_result = true; |
778 | 0 | break; |
779 | 0 | case ANYRANGEOID: |
780 | 0 | have_polymorphic_result = true; |
781 | 0 | have_anyrange_result = true; |
782 | 0 | break; |
783 | 0 | case ANYMULTIRANGEOID: |
784 | 0 | have_polymorphic_result = true; |
785 | 0 | have_anymultirange_result = true; |
786 | 0 | break; |
787 | 0 | case ANYCOMPATIBLEOID: |
788 | 0 | case ANYCOMPATIBLENONARRAYOID: |
789 | 0 | have_polymorphic_result = true; |
790 | 0 | have_anycompatible_result = true; |
791 | 0 | break; |
792 | 0 | case ANYCOMPATIBLEARRAYOID: |
793 | 0 | have_polymorphic_result = true; |
794 | 0 | have_anycompatible_array_result = true; |
795 | 0 | break; |
796 | 0 | case ANYCOMPATIBLERANGEOID: |
797 | 0 | have_polymorphic_result = true; |
798 | 0 | have_anycompatible_range_result = true; |
799 | 0 | break; |
800 | 0 | case ANYCOMPATIBLEMULTIRANGEOID: |
801 | 0 | have_polymorphic_result = true; |
802 | 0 | have_anycompatible_multirange_result = true; |
803 | 0 | break; |
804 | 0 | default: |
805 | 0 | break; |
806 | 0 | } |
807 | 0 | } |
808 | 0 | if (!have_polymorphic_result) |
809 | 0 | return true; |
810 | | |
811 | | /* |
812 | | * Otherwise, extract actual datatype(s) from input arguments. (We assume |
813 | | * the parser already validated consistency of the arguments. Also, for |
814 | | * the ANYCOMPATIBLE pseudotype family, we expect that all matching |
815 | | * arguments were coerced to the selected common supertype, so that it |
816 | | * doesn't matter which one's exposed type we look at.) |
817 | | */ |
818 | 0 | if (!call_expr) |
819 | 0 | return false; /* no hope */ |
820 | | |
821 | 0 | memset(&poly_actuals, 0, sizeof(poly_actuals)); |
822 | 0 | memset(&anyc_actuals, 0, sizeof(anyc_actuals)); |
823 | |
|
824 | 0 | for (i = 0; i < nargs; i++) |
825 | 0 | { |
826 | 0 | switch (declared_args->values[i]) |
827 | 0 | { |
828 | 0 | case ANYELEMENTOID: |
829 | 0 | case ANYNONARRAYOID: |
830 | 0 | case ANYENUMOID: |
831 | 0 | if (!OidIsValid(poly_actuals.anyelement_type)) |
832 | 0 | { |
833 | 0 | poly_actuals.anyelement_type = |
834 | 0 | get_call_expr_argtype(call_expr, i); |
835 | 0 | if (!OidIsValid(poly_actuals.anyelement_type)) |
836 | 0 | return false; |
837 | 0 | } |
838 | 0 | break; |
839 | 0 | case ANYARRAYOID: |
840 | 0 | if (!OidIsValid(poly_actuals.anyarray_type)) |
841 | 0 | { |
842 | 0 | poly_actuals.anyarray_type = |
843 | 0 | get_call_expr_argtype(call_expr, i); |
844 | 0 | if (!OidIsValid(poly_actuals.anyarray_type)) |
845 | 0 | return false; |
846 | 0 | } |
847 | 0 | break; |
848 | 0 | case ANYRANGEOID: |
849 | 0 | if (!OidIsValid(poly_actuals.anyrange_type)) |
850 | 0 | { |
851 | 0 | poly_actuals.anyrange_type = |
852 | 0 | get_call_expr_argtype(call_expr, i); |
853 | 0 | if (!OidIsValid(poly_actuals.anyrange_type)) |
854 | 0 | return false; |
855 | 0 | } |
856 | 0 | break; |
857 | 0 | case ANYMULTIRANGEOID: |
858 | 0 | if (!OidIsValid(poly_actuals.anymultirange_type)) |
859 | 0 | { |
860 | 0 | poly_actuals.anymultirange_type = |
861 | 0 | get_call_expr_argtype(call_expr, i); |
862 | 0 | if (!OidIsValid(poly_actuals.anymultirange_type)) |
863 | 0 | return false; |
864 | 0 | } |
865 | 0 | break; |
866 | 0 | case ANYCOMPATIBLEOID: |
867 | 0 | case ANYCOMPATIBLENONARRAYOID: |
868 | 0 | if (!OidIsValid(anyc_actuals.anyelement_type)) |
869 | 0 | { |
870 | 0 | anyc_actuals.anyelement_type = |
871 | 0 | get_call_expr_argtype(call_expr, i); |
872 | 0 | if (!OidIsValid(anyc_actuals.anyelement_type)) |
873 | 0 | return false; |
874 | 0 | } |
875 | 0 | break; |
876 | 0 | case ANYCOMPATIBLEARRAYOID: |
877 | 0 | if (!OidIsValid(anyc_actuals.anyarray_type)) |
878 | 0 | { |
879 | 0 | anyc_actuals.anyarray_type = |
880 | 0 | get_call_expr_argtype(call_expr, i); |
881 | 0 | if (!OidIsValid(anyc_actuals.anyarray_type)) |
882 | 0 | return false; |
883 | 0 | } |
884 | 0 | break; |
885 | 0 | case ANYCOMPATIBLERANGEOID: |
886 | 0 | if (!OidIsValid(anyc_actuals.anyrange_type)) |
887 | 0 | { |
888 | 0 | anyc_actuals.anyrange_type = |
889 | 0 | get_call_expr_argtype(call_expr, i); |
890 | 0 | if (!OidIsValid(anyc_actuals.anyrange_type)) |
891 | 0 | return false; |
892 | 0 | } |
893 | 0 | break; |
894 | 0 | case ANYCOMPATIBLEMULTIRANGEOID: |
895 | 0 | if (!OidIsValid(anyc_actuals.anymultirange_type)) |
896 | 0 | { |
897 | 0 | anyc_actuals.anymultirange_type = |
898 | 0 | get_call_expr_argtype(call_expr, i); |
899 | 0 | if (!OidIsValid(anyc_actuals.anymultirange_type)) |
900 | 0 | return false; |
901 | 0 | } |
902 | 0 | break; |
903 | 0 | default: |
904 | 0 | break; |
905 | 0 | } |
906 | 0 | } |
907 | | |
908 | | /* If needed, deduce one polymorphic type from others */ |
909 | 0 | if (have_anyelement_result && !OidIsValid(poly_actuals.anyelement_type)) |
910 | 0 | resolve_anyelement_from_others(&poly_actuals); |
911 | |
|
912 | 0 | if (have_anyarray_result && !OidIsValid(poly_actuals.anyarray_type)) |
913 | 0 | resolve_anyarray_from_others(&poly_actuals); |
914 | |
|
915 | 0 | if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type)) |
916 | 0 | resolve_anyrange_from_others(&poly_actuals); |
917 | |
|
918 | 0 | if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type)) |
919 | 0 | resolve_anymultirange_from_others(&poly_actuals); |
920 | |
|
921 | 0 | if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type)) |
922 | 0 | resolve_anyelement_from_others(&anyc_actuals); |
923 | |
|
924 | 0 | if (have_anycompatible_array_result && !OidIsValid(anyc_actuals.anyarray_type)) |
925 | 0 | resolve_anyarray_from_others(&anyc_actuals); |
926 | |
|
927 | 0 | if (have_anycompatible_range_result && !OidIsValid(anyc_actuals.anyrange_type)) |
928 | 0 | resolve_anyrange_from_others(&anyc_actuals); |
929 | |
|
930 | 0 | if (have_anycompatible_multirange_result && !OidIsValid(anyc_actuals.anymultirange_type)) |
931 | 0 | resolve_anymultirange_from_others(&anyc_actuals); |
932 | | |
933 | | /* |
934 | | * Identify the collation to use for polymorphic OUT parameters. (It'll |
935 | | * necessarily be the same for both anyelement and anyarray, likewise for |
936 | | * anycompatible and anycompatiblearray.) Note that range types are not |
937 | | * collatable, so any possible internal collation of a range type is not |
938 | | * considered here. |
939 | | */ |
940 | 0 | if (OidIsValid(poly_actuals.anyelement_type)) |
941 | 0 | anycollation = get_typcollation(poly_actuals.anyelement_type); |
942 | 0 | else if (OidIsValid(poly_actuals.anyarray_type)) |
943 | 0 | anycollation = get_typcollation(poly_actuals.anyarray_type); |
944 | |
|
945 | 0 | if (OidIsValid(anyc_actuals.anyelement_type)) |
946 | 0 | anycompatcollation = get_typcollation(anyc_actuals.anyelement_type); |
947 | 0 | else if (OidIsValid(anyc_actuals.anyarray_type)) |
948 | 0 | anycompatcollation = get_typcollation(anyc_actuals.anyarray_type); |
949 | |
|
950 | 0 | if (OidIsValid(anycollation) || OidIsValid(anycompatcollation)) |
951 | 0 | { |
952 | | /* |
953 | | * The types are collatable, so consider whether to use a nondefault |
954 | | * collation. We do so if we can identify the input collation used |
955 | | * for the function. |
956 | | */ |
957 | 0 | Oid inputcollation = exprInputCollation(call_expr); |
958 | |
|
959 | 0 | if (OidIsValid(inputcollation)) |
960 | 0 | { |
961 | 0 | if (OidIsValid(anycollation)) |
962 | 0 | anycollation = inputcollation; |
963 | 0 | if (OidIsValid(anycompatcollation)) |
964 | 0 | anycompatcollation = inputcollation; |
965 | 0 | } |
966 | 0 | } |
967 | | |
968 | | /* And finally replace the tuple column types as needed */ |
969 | 0 | for (i = 0; i < natts; i++) |
970 | 0 | { |
971 | 0 | Form_pg_attribute att = TupleDescAttr(tupdesc, i); |
972 | |
|
973 | 0 | switch (att->atttypid) |
974 | 0 | { |
975 | 0 | case ANYELEMENTOID: |
976 | 0 | case ANYNONARRAYOID: |
977 | 0 | case ANYENUMOID: |
978 | 0 | TupleDescInitEntry(tupdesc, i + 1, |
979 | 0 | NameStr(att->attname), |
980 | 0 | poly_actuals.anyelement_type, |
981 | 0 | -1, |
982 | 0 | 0); |
983 | 0 | TupleDescInitEntryCollation(tupdesc, i + 1, anycollation); |
984 | 0 | break; |
985 | 0 | case ANYARRAYOID: |
986 | 0 | TupleDescInitEntry(tupdesc, i + 1, |
987 | 0 | NameStr(att->attname), |
988 | 0 | poly_actuals.anyarray_type, |
989 | 0 | -1, |
990 | 0 | 0); |
991 | 0 | TupleDescInitEntryCollation(tupdesc, i + 1, anycollation); |
992 | 0 | break; |
993 | 0 | case ANYRANGEOID: |
994 | 0 | TupleDescInitEntry(tupdesc, i + 1, |
995 | 0 | NameStr(att->attname), |
996 | 0 | poly_actuals.anyrange_type, |
997 | 0 | -1, |
998 | 0 | 0); |
999 | | /* no collation should be attached to a range type */ |
1000 | 0 | break; |
1001 | 0 | case ANYMULTIRANGEOID: |
1002 | 0 | TupleDescInitEntry(tupdesc, i + 1, |
1003 | 0 | NameStr(att->attname), |
1004 | 0 | poly_actuals.anymultirange_type, |
1005 | 0 | -1, |
1006 | 0 | 0); |
1007 | | /* no collation should be attached to a multirange type */ |
1008 | 0 | break; |
1009 | 0 | case ANYCOMPATIBLEOID: |
1010 | 0 | case ANYCOMPATIBLENONARRAYOID: |
1011 | 0 | TupleDescInitEntry(tupdesc, i + 1, |
1012 | 0 | NameStr(att->attname), |
1013 | 0 | anyc_actuals.anyelement_type, |
1014 | 0 | -1, |
1015 | 0 | 0); |
1016 | 0 | TupleDescInitEntryCollation(tupdesc, i + 1, anycompatcollation); |
1017 | 0 | break; |
1018 | 0 | case ANYCOMPATIBLEARRAYOID: |
1019 | 0 | TupleDescInitEntry(tupdesc, i + 1, |
1020 | 0 | NameStr(att->attname), |
1021 | 0 | anyc_actuals.anyarray_type, |
1022 | 0 | -1, |
1023 | 0 | 0); |
1024 | 0 | TupleDescInitEntryCollation(tupdesc, i + 1, anycompatcollation); |
1025 | 0 | break; |
1026 | 0 | case ANYCOMPATIBLERANGEOID: |
1027 | 0 | TupleDescInitEntry(tupdesc, i + 1, |
1028 | 0 | NameStr(att->attname), |
1029 | 0 | anyc_actuals.anyrange_type, |
1030 | 0 | -1, |
1031 | 0 | 0); |
1032 | | /* no collation should be attached to a range type */ |
1033 | 0 | break; |
1034 | 0 | case ANYCOMPATIBLEMULTIRANGEOID: |
1035 | 0 | TupleDescInitEntry(tupdesc, i + 1, |
1036 | 0 | NameStr(att->attname), |
1037 | 0 | anyc_actuals.anymultirange_type, |
1038 | 0 | -1, |
1039 | 0 | 0); |
1040 | | /* no collation should be attached to a multirange type */ |
1041 | 0 | break; |
1042 | 0 | default: |
1043 | 0 | break; |
1044 | 0 | } |
1045 | 0 | } |
1046 | | |
1047 | 0 | return true; |
1048 | 0 | } |
1049 | | |
1050 | | /* |
1051 | | * Given the declared argument types and modes for a function, replace any |
1052 | | * polymorphic types (ANYELEMENT etc) in argtypes[] with concrete data types |
1053 | | * deduced from the input arguments found in call_expr. |
1054 | | * |
1055 | | * Returns true if able to deduce all types, false if necessary information |
1056 | | * is not provided (call_expr is NULL or arg types aren't identifiable). |
1057 | | * |
1058 | | * This is the same logic as resolve_polymorphic_tupdesc, but with a different |
1059 | | * argument representation, and slightly different output responsibilities. |
1060 | | * |
1061 | | * argmodes may be NULL, in which case all arguments are assumed to be IN mode. |
1062 | | */ |
1063 | | bool |
1064 | | resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, |
1065 | | Node *call_expr) |
1066 | 0 | { |
1067 | 0 | bool have_polymorphic_result = false; |
1068 | 0 | bool have_anyelement_result = false; |
1069 | 0 | bool have_anyarray_result = false; |
1070 | 0 | bool have_anyrange_result = false; |
1071 | 0 | bool have_anymultirange_result = false; |
1072 | 0 | bool have_anycompatible_result = false; |
1073 | 0 | bool have_anycompatible_array_result = false; |
1074 | 0 | bool have_anycompatible_range_result = false; |
1075 | 0 | bool have_anycompatible_multirange_result = false; |
1076 | 0 | polymorphic_actuals poly_actuals; |
1077 | 0 | polymorphic_actuals anyc_actuals; |
1078 | 0 | int inargno; |
1079 | 0 | int i; |
1080 | | |
1081 | | /* |
1082 | | * First pass: resolve polymorphic inputs, check for outputs. As in |
1083 | | * resolve_polymorphic_tupdesc, we rely on the parser to have enforced |
1084 | | * type consistency and coerced ANYCOMPATIBLE args to a common supertype. |
1085 | | */ |
1086 | 0 | memset(&poly_actuals, 0, sizeof(poly_actuals)); |
1087 | 0 | memset(&anyc_actuals, 0, sizeof(anyc_actuals)); |
1088 | 0 | inargno = 0; |
1089 | 0 | for (i = 0; i < numargs; i++) |
1090 | 0 | { |
1091 | 0 | char argmode = argmodes ? argmodes[i] : PROARGMODE_IN; |
1092 | |
|
1093 | 0 | switch (argtypes[i]) |
1094 | 0 | { |
1095 | 0 | case ANYELEMENTOID: |
1096 | 0 | case ANYNONARRAYOID: |
1097 | 0 | case ANYENUMOID: |
1098 | 0 | if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE) |
1099 | 0 | { |
1100 | 0 | have_polymorphic_result = true; |
1101 | 0 | have_anyelement_result = true; |
1102 | 0 | } |
1103 | 0 | else |
1104 | 0 | { |
1105 | 0 | if (!OidIsValid(poly_actuals.anyelement_type)) |
1106 | 0 | { |
1107 | 0 | poly_actuals.anyelement_type = |
1108 | 0 | get_call_expr_argtype(call_expr, inargno); |
1109 | 0 | if (!OidIsValid(poly_actuals.anyelement_type)) |
1110 | 0 | return false; |
1111 | 0 | } |
1112 | 0 | argtypes[i] = poly_actuals.anyelement_type; |
1113 | 0 | } |
1114 | 0 | break; |
1115 | 0 | case ANYARRAYOID: |
1116 | 0 | if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE) |
1117 | 0 | { |
1118 | 0 | have_polymorphic_result = true; |
1119 | 0 | have_anyarray_result = true; |
1120 | 0 | } |
1121 | 0 | else |
1122 | 0 | { |
1123 | 0 | if (!OidIsValid(poly_actuals.anyarray_type)) |
1124 | 0 | { |
1125 | 0 | poly_actuals.anyarray_type = |
1126 | 0 | get_call_expr_argtype(call_expr, inargno); |
1127 | 0 | if (!OidIsValid(poly_actuals.anyarray_type)) |
1128 | 0 | return false; |
1129 | 0 | } |
1130 | 0 | argtypes[i] = poly_actuals.anyarray_type; |
1131 | 0 | } |
1132 | 0 | break; |
1133 | 0 | case ANYRANGEOID: |
1134 | 0 | if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE) |
1135 | 0 | { |
1136 | 0 | have_polymorphic_result = true; |
1137 | 0 | have_anyrange_result = true; |
1138 | 0 | } |
1139 | 0 | else |
1140 | 0 | { |
1141 | 0 | if (!OidIsValid(poly_actuals.anyrange_type)) |
1142 | 0 | { |
1143 | 0 | poly_actuals.anyrange_type = |
1144 | 0 | get_call_expr_argtype(call_expr, inargno); |
1145 | 0 | if (!OidIsValid(poly_actuals.anyrange_type)) |
1146 | 0 | return false; |
1147 | 0 | } |
1148 | 0 | argtypes[i] = poly_actuals.anyrange_type; |
1149 | 0 | } |
1150 | 0 | break; |
1151 | 0 | case ANYMULTIRANGEOID: |
1152 | 0 | if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE) |
1153 | 0 | { |
1154 | 0 | have_polymorphic_result = true; |
1155 | 0 | have_anymultirange_result = true; |
1156 | 0 | } |
1157 | 0 | else |
1158 | 0 | { |
1159 | 0 | if (!OidIsValid(poly_actuals.anymultirange_type)) |
1160 | 0 | { |
1161 | 0 | poly_actuals.anymultirange_type = |
1162 | 0 | get_call_expr_argtype(call_expr, inargno); |
1163 | 0 | if (!OidIsValid(poly_actuals.anymultirange_type)) |
1164 | 0 | return false; |
1165 | 0 | } |
1166 | 0 | argtypes[i] = poly_actuals.anymultirange_type; |
1167 | 0 | } |
1168 | 0 | break; |
1169 | 0 | case ANYCOMPATIBLEOID: |
1170 | 0 | case ANYCOMPATIBLENONARRAYOID: |
1171 | 0 | if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE) |
1172 | 0 | { |
1173 | 0 | have_polymorphic_result = true; |
1174 | 0 | have_anycompatible_result = true; |
1175 | 0 | } |
1176 | 0 | else |
1177 | 0 | { |
1178 | 0 | if (!OidIsValid(anyc_actuals.anyelement_type)) |
1179 | 0 | { |
1180 | 0 | anyc_actuals.anyelement_type = |
1181 | 0 | get_call_expr_argtype(call_expr, inargno); |
1182 | 0 | if (!OidIsValid(anyc_actuals.anyelement_type)) |
1183 | 0 | return false; |
1184 | 0 | } |
1185 | 0 | argtypes[i] = anyc_actuals.anyelement_type; |
1186 | 0 | } |
1187 | 0 | break; |
1188 | 0 | case ANYCOMPATIBLEARRAYOID: |
1189 | 0 | if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE) |
1190 | 0 | { |
1191 | 0 | have_polymorphic_result = true; |
1192 | 0 | have_anycompatible_array_result = true; |
1193 | 0 | } |
1194 | 0 | else |
1195 | 0 | { |
1196 | 0 | if (!OidIsValid(anyc_actuals.anyarray_type)) |
1197 | 0 | { |
1198 | 0 | anyc_actuals.anyarray_type = |
1199 | 0 | get_call_expr_argtype(call_expr, inargno); |
1200 | 0 | if (!OidIsValid(anyc_actuals.anyarray_type)) |
1201 | 0 | return false; |
1202 | 0 | } |
1203 | 0 | argtypes[i] = anyc_actuals.anyarray_type; |
1204 | 0 | } |
1205 | 0 | break; |
1206 | 0 | case ANYCOMPATIBLERANGEOID: |
1207 | 0 | if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE) |
1208 | 0 | { |
1209 | 0 | have_polymorphic_result = true; |
1210 | 0 | have_anycompatible_range_result = true; |
1211 | 0 | } |
1212 | 0 | else |
1213 | 0 | { |
1214 | 0 | if (!OidIsValid(anyc_actuals.anyrange_type)) |
1215 | 0 | { |
1216 | 0 | anyc_actuals.anyrange_type = |
1217 | 0 | get_call_expr_argtype(call_expr, inargno); |
1218 | 0 | if (!OidIsValid(anyc_actuals.anyrange_type)) |
1219 | 0 | return false; |
1220 | 0 | } |
1221 | 0 | argtypes[i] = anyc_actuals.anyrange_type; |
1222 | 0 | } |
1223 | 0 | break; |
1224 | 0 | case ANYCOMPATIBLEMULTIRANGEOID: |
1225 | 0 | if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE) |
1226 | 0 | { |
1227 | 0 | have_polymorphic_result = true; |
1228 | 0 | have_anycompatible_multirange_result = true; |
1229 | 0 | } |
1230 | 0 | else |
1231 | 0 | { |
1232 | 0 | if (!OidIsValid(anyc_actuals.anymultirange_type)) |
1233 | 0 | { |
1234 | 0 | anyc_actuals.anymultirange_type = |
1235 | 0 | get_call_expr_argtype(call_expr, inargno); |
1236 | 0 | if (!OidIsValid(anyc_actuals.anymultirange_type)) |
1237 | 0 | return false; |
1238 | 0 | } |
1239 | 0 | argtypes[i] = anyc_actuals.anymultirange_type; |
1240 | 0 | } |
1241 | 0 | break; |
1242 | 0 | default: |
1243 | 0 | break; |
1244 | 0 | } |
1245 | 0 | if (argmode != PROARGMODE_OUT && argmode != PROARGMODE_TABLE) |
1246 | 0 | inargno++; |
1247 | 0 | } |
1248 | | |
1249 | | /* Done? */ |
1250 | 0 | if (!have_polymorphic_result) |
1251 | 0 | return true; |
1252 | | |
1253 | | /* If needed, deduce one polymorphic type from others */ |
1254 | 0 | if (have_anyelement_result && !OidIsValid(poly_actuals.anyelement_type)) |
1255 | 0 | resolve_anyelement_from_others(&poly_actuals); |
1256 | |
|
1257 | 0 | if (have_anyarray_result && !OidIsValid(poly_actuals.anyarray_type)) |
1258 | 0 | resolve_anyarray_from_others(&poly_actuals); |
1259 | |
|
1260 | 0 | if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type)) |
1261 | 0 | resolve_anyrange_from_others(&poly_actuals); |
1262 | |
|
1263 | 0 | if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type)) |
1264 | 0 | resolve_anymultirange_from_others(&poly_actuals); |
1265 | |
|
1266 | 0 | if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type)) |
1267 | 0 | resolve_anyelement_from_others(&anyc_actuals); |
1268 | |
|
1269 | 0 | if (have_anycompatible_array_result && !OidIsValid(anyc_actuals.anyarray_type)) |
1270 | 0 | resolve_anyarray_from_others(&anyc_actuals); |
1271 | |
|
1272 | 0 | if (have_anycompatible_range_result && !OidIsValid(anyc_actuals.anyrange_type)) |
1273 | 0 | resolve_anyrange_from_others(&anyc_actuals); |
1274 | |
|
1275 | 0 | if (have_anycompatible_multirange_result && !OidIsValid(anyc_actuals.anymultirange_type)) |
1276 | 0 | resolve_anymultirange_from_others(&anyc_actuals); |
1277 | | |
1278 | | /* And finally replace the output column types as needed */ |
1279 | 0 | for (i = 0; i < numargs; i++) |
1280 | 0 | { |
1281 | 0 | switch (argtypes[i]) |
1282 | 0 | { |
1283 | 0 | case ANYELEMENTOID: |
1284 | 0 | case ANYNONARRAYOID: |
1285 | 0 | case ANYENUMOID: |
1286 | 0 | argtypes[i] = poly_actuals.anyelement_type; |
1287 | 0 | break; |
1288 | 0 | case ANYARRAYOID: |
1289 | 0 | argtypes[i] = poly_actuals.anyarray_type; |
1290 | 0 | break; |
1291 | 0 | case ANYRANGEOID: |
1292 | 0 | argtypes[i] = poly_actuals.anyrange_type; |
1293 | 0 | break; |
1294 | 0 | case ANYMULTIRANGEOID: |
1295 | 0 | argtypes[i] = poly_actuals.anymultirange_type; |
1296 | 0 | break; |
1297 | 0 | case ANYCOMPATIBLEOID: |
1298 | 0 | case ANYCOMPATIBLENONARRAYOID: |
1299 | 0 | argtypes[i] = anyc_actuals.anyelement_type; |
1300 | 0 | break; |
1301 | 0 | case ANYCOMPATIBLEARRAYOID: |
1302 | 0 | argtypes[i] = anyc_actuals.anyarray_type; |
1303 | 0 | break; |
1304 | 0 | case ANYCOMPATIBLERANGEOID: |
1305 | 0 | argtypes[i] = anyc_actuals.anyrange_type; |
1306 | 0 | break; |
1307 | 0 | case ANYCOMPATIBLEMULTIRANGEOID: |
1308 | 0 | argtypes[i] = anyc_actuals.anymultirange_type; |
1309 | 0 | break; |
1310 | 0 | default: |
1311 | 0 | break; |
1312 | 0 | } |
1313 | 0 | } |
1314 | | |
1315 | 0 | return true; |
1316 | 0 | } |
1317 | | |
1318 | | /* |
1319 | | * get_type_func_class |
1320 | | * Given the type OID, obtain its TYPEFUNC classification. |
1321 | | * Also, if it's a domain, return the base type OID. |
1322 | | * |
1323 | | * This is intended to centralize a bunch of formerly ad-hoc code for |
1324 | | * classifying types. The categories used here are useful for deciding |
1325 | | * how to handle functions returning the datatype. |
1326 | | */ |
1327 | | static TypeFuncClass |
1328 | | get_type_func_class(Oid typid, Oid *base_typeid) |
1329 | 0 | { |
1330 | 0 | *base_typeid = typid; |
1331 | |
|
1332 | 0 | switch (get_typtype(typid)) |
1333 | 0 | { |
1334 | 0 | case TYPTYPE_COMPOSITE: |
1335 | 0 | return TYPEFUNC_COMPOSITE; |
1336 | 0 | case TYPTYPE_BASE: |
1337 | 0 | case TYPTYPE_ENUM: |
1338 | 0 | case TYPTYPE_RANGE: |
1339 | 0 | case TYPTYPE_MULTIRANGE: |
1340 | 0 | return TYPEFUNC_SCALAR; |
1341 | 0 | case TYPTYPE_DOMAIN: |
1342 | 0 | *base_typeid = typid = getBaseType(typid); |
1343 | 0 | if (get_typtype(typid) == TYPTYPE_COMPOSITE) |
1344 | 0 | return TYPEFUNC_COMPOSITE_DOMAIN; |
1345 | 0 | else /* domain base type can't be a pseudotype */ |
1346 | 0 | return TYPEFUNC_SCALAR; |
1347 | 0 | case TYPTYPE_PSEUDO: |
1348 | 0 | if (typid == RECORDOID) |
1349 | 0 | return TYPEFUNC_RECORD; |
1350 | | |
1351 | | /* |
1352 | | * We treat VOID and CSTRING as legitimate scalar datatypes, |
1353 | | * mostly for the convenience of the JDBC driver (which wants to |
1354 | | * be able to do "SELECT * FROM foo()" for all legitimately |
1355 | | * user-callable functions). |
1356 | | */ |
1357 | 0 | if (typid == VOIDOID || typid == CSTRINGOID) |
1358 | 0 | return TYPEFUNC_SCALAR; |
1359 | 0 | return TYPEFUNC_OTHER; |
1360 | 0 | } |
1361 | | /* shouldn't get here, probably */ |
1362 | 0 | return TYPEFUNC_OTHER; |
1363 | 0 | } |
1364 | | |
1365 | | |
1366 | | /* |
1367 | | * get_func_arg_info |
1368 | | * |
1369 | | * Fetch info about the argument types, names, and IN/OUT modes from the |
1370 | | * pg_proc tuple. Return value is the total number of arguments. |
1371 | | * Other results are palloc'd. *p_argtypes is always filled in, but |
1372 | | * *p_argnames and *p_argmodes will be set NULL in the default cases |
1373 | | * (no names, and all IN arguments, respectively). |
1374 | | * |
1375 | | * Note that this function simply fetches what is in the pg_proc tuple; |
1376 | | * it doesn't do any interpretation of polymorphic types. |
1377 | | */ |
1378 | | int |
1379 | | get_func_arg_info(HeapTuple procTup, |
1380 | | Oid **p_argtypes, char ***p_argnames, char **p_argmodes) |
1381 | 0 | { |
1382 | 0 | Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup); |
1383 | 0 | Datum proallargtypes; |
1384 | 0 | Datum proargmodes; |
1385 | 0 | Datum proargnames; |
1386 | 0 | bool isNull; |
1387 | 0 | ArrayType *arr; |
1388 | 0 | int numargs; |
1389 | 0 | Datum *elems; |
1390 | 0 | int nelems; |
1391 | 0 | int i; |
1392 | | |
1393 | | /* First discover the total number of parameters and get their types */ |
1394 | 0 | proallargtypes = SysCacheGetAttr(PROCOID, procTup, |
1395 | 0 | Anum_pg_proc_proallargtypes, |
1396 | 0 | &isNull); |
1397 | 0 | if (!isNull) |
1398 | 0 | { |
1399 | | /* |
1400 | | * We expect the arrays to be 1-D arrays of the right types; verify |
1401 | | * that. For the OID and char arrays, we don't need to use |
1402 | | * deconstruct_array() since the array data is just going to look like |
1403 | | * a C array of values. |
1404 | | */ |
1405 | 0 | arr = DatumGetArrayTypeP(proallargtypes); /* ensure not toasted */ |
1406 | 0 | numargs = ARR_DIMS(arr)[0]; |
1407 | 0 | if (ARR_NDIM(arr) != 1 || |
1408 | 0 | numargs < 0 || |
1409 | 0 | ARR_HASNULL(arr) || |
1410 | 0 | ARR_ELEMTYPE(arr) != OIDOID) |
1411 | 0 | elog(ERROR, "proallargtypes is not a 1-D Oid array or it contains nulls"); |
1412 | 0 | Assert(numargs >= procStruct->pronargs); |
1413 | 0 | *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid)); |
1414 | 0 | memcpy(*p_argtypes, ARR_DATA_PTR(arr), |
1415 | 0 | numargs * sizeof(Oid)); |
1416 | 0 | } |
1417 | 0 | else |
1418 | 0 | { |
1419 | | /* If no proallargtypes, use proargtypes */ |
1420 | 0 | numargs = procStruct->proargtypes.dim1; |
1421 | 0 | Assert(numargs == procStruct->pronargs); |
1422 | 0 | *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid)); |
1423 | 0 | memcpy(*p_argtypes, procStruct->proargtypes.values, |
1424 | 0 | numargs * sizeof(Oid)); |
1425 | 0 | } |
1426 | | |
1427 | | /* Get argument names, if available */ |
1428 | 0 | proargnames = SysCacheGetAttr(PROCOID, procTup, |
1429 | 0 | Anum_pg_proc_proargnames, |
1430 | 0 | &isNull); |
1431 | 0 | if (isNull) |
1432 | 0 | *p_argnames = NULL; |
1433 | 0 | else |
1434 | 0 | { |
1435 | 0 | deconstruct_array_builtin(DatumGetArrayTypeP(proargnames), TEXTOID, |
1436 | 0 | &elems, NULL, &nelems); |
1437 | 0 | if (nelems != numargs) /* should not happen */ |
1438 | 0 | elog(ERROR, "proargnames must have the same number of elements as the function has arguments"); |
1439 | 0 | *p_argnames = (char **) palloc(sizeof(char *) * numargs); |
1440 | 0 | for (i = 0; i < numargs; i++) |
1441 | 0 | (*p_argnames)[i] = TextDatumGetCString(elems[i]); |
1442 | 0 | } |
1443 | | |
1444 | | /* Get argument modes, if available */ |
1445 | 0 | proargmodes = SysCacheGetAttr(PROCOID, procTup, |
1446 | 0 | Anum_pg_proc_proargmodes, |
1447 | 0 | &isNull); |
1448 | 0 | if (isNull) |
1449 | 0 | *p_argmodes = NULL; |
1450 | 0 | else |
1451 | 0 | { |
1452 | 0 | arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */ |
1453 | 0 | if (ARR_NDIM(arr) != 1 || |
1454 | 0 | ARR_DIMS(arr)[0] != numargs || |
1455 | 0 | ARR_HASNULL(arr) || |
1456 | 0 | ARR_ELEMTYPE(arr) != CHAROID) |
1457 | 0 | elog(ERROR, "proargmodes is not a 1-D char array of length %d or it contains nulls", |
1458 | 0 | numargs); |
1459 | 0 | *p_argmodes = (char *) palloc(numargs * sizeof(char)); |
1460 | 0 | memcpy(*p_argmodes, ARR_DATA_PTR(arr), |
1461 | 0 | numargs * sizeof(char)); |
1462 | 0 | } |
1463 | | |
1464 | 0 | return numargs; |
1465 | 0 | } |
1466 | | |
1467 | | /* |
1468 | | * get_func_trftypes |
1469 | | * |
1470 | | * Returns the number of transformed types used by the function. |
1471 | | * If there are any, a palloc'd array of the type OIDs is returned |
1472 | | * into *p_trftypes. |
1473 | | */ |
1474 | | int |
1475 | | get_func_trftypes(HeapTuple procTup, |
1476 | | Oid **p_trftypes) |
1477 | 0 | { |
1478 | 0 | Datum protrftypes; |
1479 | 0 | ArrayType *arr; |
1480 | 0 | int nelems; |
1481 | 0 | bool isNull; |
1482 | |
|
1483 | 0 | protrftypes = SysCacheGetAttr(PROCOID, procTup, |
1484 | 0 | Anum_pg_proc_protrftypes, |
1485 | 0 | &isNull); |
1486 | 0 | if (!isNull) |
1487 | 0 | { |
1488 | | /* |
1489 | | * We expect the arrays to be 1-D arrays of the right types; verify |
1490 | | * that. For the OID and char arrays, we don't need to use |
1491 | | * deconstruct_array() since the array data is just going to look like |
1492 | | * a C array of values. |
1493 | | */ |
1494 | 0 | arr = DatumGetArrayTypeP(protrftypes); /* ensure not toasted */ |
1495 | 0 | nelems = ARR_DIMS(arr)[0]; |
1496 | 0 | if (ARR_NDIM(arr) != 1 || |
1497 | 0 | nelems < 0 || |
1498 | 0 | ARR_HASNULL(arr) || |
1499 | 0 | ARR_ELEMTYPE(arr) != OIDOID) |
1500 | 0 | elog(ERROR, "protrftypes is not a 1-D Oid array or it contains nulls"); |
1501 | 0 | *p_trftypes = (Oid *) palloc(nelems * sizeof(Oid)); |
1502 | 0 | memcpy(*p_trftypes, ARR_DATA_PTR(arr), |
1503 | 0 | nelems * sizeof(Oid)); |
1504 | |
|
1505 | 0 | return nelems; |
1506 | 0 | } |
1507 | 0 | else |
1508 | 0 | return 0; |
1509 | 0 | } |
1510 | | |
1511 | | /* |
1512 | | * get_func_input_arg_names |
1513 | | * |
1514 | | * Extract the names of input arguments only, given a function's |
1515 | | * proargnames and proargmodes entries in Datum form. |
1516 | | * |
1517 | | * Returns the number of input arguments, which is the length of the |
1518 | | * palloc'd array returned to *arg_names. Entries for unnamed args |
1519 | | * are set to NULL. You don't get anything if proargnames is NULL. |
1520 | | */ |
1521 | | int |
1522 | | get_func_input_arg_names(Datum proargnames, Datum proargmodes, |
1523 | | char ***arg_names) |
1524 | 0 | { |
1525 | 0 | ArrayType *arr; |
1526 | 0 | int numargs; |
1527 | 0 | Datum *argnames; |
1528 | 0 | char *argmodes; |
1529 | 0 | char **inargnames; |
1530 | 0 | int numinargs; |
1531 | 0 | int i; |
1532 | | |
1533 | | /* Do nothing if null proargnames */ |
1534 | 0 | if (proargnames == PointerGetDatum(NULL)) |
1535 | 0 | { |
1536 | 0 | *arg_names = NULL; |
1537 | 0 | return 0; |
1538 | 0 | } |
1539 | | |
1540 | | /* |
1541 | | * We expect the arrays to be 1-D arrays of the right types; verify that. |
1542 | | * For proargmodes, we don't need to use deconstruct_array() since the |
1543 | | * array data is just going to look like a C array of values. |
1544 | | */ |
1545 | 0 | arr = DatumGetArrayTypeP(proargnames); /* ensure not toasted */ |
1546 | 0 | if (ARR_NDIM(arr) != 1 || |
1547 | 0 | ARR_HASNULL(arr) || |
1548 | 0 | ARR_ELEMTYPE(arr) != TEXTOID) |
1549 | 0 | elog(ERROR, "proargnames is not a 1-D text array or it contains nulls"); |
1550 | 0 | deconstruct_array_builtin(arr, TEXTOID, &argnames, NULL, &numargs); |
1551 | 0 | if (proargmodes != PointerGetDatum(NULL)) |
1552 | 0 | { |
1553 | 0 | arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */ |
1554 | 0 | if (ARR_NDIM(arr) != 1 || |
1555 | 0 | ARR_DIMS(arr)[0] != numargs || |
1556 | 0 | ARR_HASNULL(arr) || |
1557 | 0 | ARR_ELEMTYPE(arr) != CHAROID) |
1558 | 0 | elog(ERROR, "proargmodes is not a 1-D char array of length %d or it contains nulls", |
1559 | 0 | numargs); |
1560 | 0 | argmodes = (char *) ARR_DATA_PTR(arr); |
1561 | 0 | } |
1562 | 0 | else |
1563 | 0 | argmodes = NULL; |
1564 | | |
1565 | | /* zero elements probably shouldn't happen, but handle it gracefully */ |
1566 | 0 | if (numargs <= 0) |
1567 | 0 | { |
1568 | 0 | *arg_names = NULL; |
1569 | 0 | return 0; |
1570 | 0 | } |
1571 | | |
1572 | | /* extract input-argument names */ |
1573 | 0 | inargnames = (char **) palloc(numargs * sizeof(char *)); |
1574 | 0 | numinargs = 0; |
1575 | 0 | for (i = 0; i < numargs; i++) |
1576 | 0 | { |
1577 | 0 | if (argmodes == NULL || |
1578 | 0 | argmodes[i] == PROARGMODE_IN || |
1579 | 0 | argmodes[i] == PROARGMODE_INOUT || |
1580 | 0 | argmodes[i] == PROARGMODE_VARIADIC) |
1581 | 0 | { |
1582 | 0 | char *pname = TextDatumGetCString(argnames[i]); |
1583 | |
|
1584 | 0 | if (pname[0] != '\0') |
1585 | 0 | inargnames[numinargs] = pname; |
1586 | 0 | else |
1587 | 0 | inargnames[numinargs] = NULL; |
1588 | 0 | numinargs++; |
1589 | 0 | } |
1590 | 0 | } |
1591 | |
|
1592 | 0 | *arg_names = inargnames; |
1593 | 0 | return numinargs; |
1594 | 0 | } |
1595 | | |
1596 | | |
1597 | | /* |
1598 | | * get_func_result_name |
1599 | | * |
1600 | | * If the function has exactly one output parameter, and that parameter |
1601 | | * is named, return the name (as a palloc'd string). Else return NULL. |
1602 | | * |
1603 | | * This is used to determine the default output column name for functions |
1604 | | * returning scalar types. |
1605 | | */ |
1606 | | char * |
1607 | | get_func_result_name(Oid functionId) |
1608 | 0 | { |
1609 | 0 | char *result; |
1610 | 0 | HeapTuple procTuple; |
1611 | 0 | Datum proargmodes; |
1612 | 0 | Datum proargnames; |
1613 | 0 | ArrayType *arr; |
1614 | 0 | int numargs; |
1615 | 0 | char *argmodes; |
1616 | 0 | Datum *argnames; |
1617 | 0 | int numoutargs; |
1618 | 0 | int nargnames; |
1619 | 0 | int i; |
1620 | | |
1621 | | /* First fetch the function's pg_proc row */ |
1622 | 0 | procTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionId)); |
1623 | 0 | if (!HeapTupleIsValid(procTuple)) |
1624 | 0 | elog(ERROR, "cache lookup failed for function %u", functionId); |
1625 | | |
1626 | | /* If there are no named OUT parameters, return NULL */ |
1627 | 0 | if (heap_attisnull(procTuple, Anum_pg_proc_proargmodes, NULL) || |
1628 | 0 | heap_attisnull(procTuple, Anum_pg_proc_proargnames, NULL)) |
1629 | 0 | result = NULL; |
1630 | 0 | else |
1631 | 0 | { |
1632 | | /* Get the data out of the tuple */ |
1633 | 0 | proargmodes = SysCacheGetAttrNotNull(PROCOID, procTuple, |
1634 | 0 | Anum_pg_proc_proargmodes); |
1635 | 0 | proargnames = SysCacheGetAttrNotNull(PROCOID, procTuple, |
1636 | 0 | Anum_pg_proc_proargnames); |
1637 | | |
1638 | | /* |
1639 | | * We expect the arrays to be 1-D arrays of the right types; verify |
1640 | | * that. For the char array, we don't need to use deconstruct_array() |
1641 | | * since the array data is just going to look like a C array of |
1642 | | * values. |
1643 | | */ |
1644 | 0 | arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */ |
1645 | 0 | numargs = ARR_DIMS(arr)[0]; |
1646 | 0 | if (ARR_NDIM(arr) != 1 || |
1647 | 0 | numargs < 0 || |
1648 | 0 | ARR_HASNULL(arr) || |
1649 | 0 | ARR_ELEMTYPE(arr) != CHAROID) |
1650 | 0 | elog(ERROR, "proargmodes is not a 1-D char array or it contains nulls"); |
1651 | 0 | argmodes = (char *) ARR_DATA_PTR(arr); |
1652 | 0 | arr = DatumGetArrayTypeP(proargnames); /* ensure not toasted */ |
1653 | 0 | if (ARR_NDIM(arr) != 1 || |
1654 | 0 | ARR_DIMS(arr)[0] != numargs || |
1655 | 0 | ARR_HASNULL(arr) || |
1656 | 0 | ARR_ELEMTYPE(arr) != TEXTOID) |
1657 | 0 | elog(ERROR, "proargnames is not a 1-D text array of length %d or it contains nulls", |
1658 | 0 | numargs); |
1659 | 0 | deconstruct_array_builtin(arr, TEXTOID, &argnames, NULL, &nargnames); |
1660 | 0 | Assert(nargnames == numargs); |
1661 | | |
1662 | | /* scan for output argument(s) */ |
1663 | 0 | result = NULL; |
1664 | 0 | numoutargs = 0; |
1665 | 0 | for (i = 0; i < numargs; i++) |
1666 | 0 | { |
1667 | 0 | if (argmodes[i] == PROARGMODE_IN || |
1668 | 0 | argmodes[i] == PROARGMODE_VARIADIC) |
1669 | 0 | continue; |
1670 | 0 | Assert(argmodes[i] == PROARGMODE_OUT || |
1671 | 0 | argmodes[i] == PROARGMODE_INOUT || |
1672 | 0 | argmodes[i] == PROARGMODE_TABLE); |
1673 | 0 | if (++numoutargs > 1) |
1674 | 0 | { |
1675 | | /* multiple out args, so forget it */ |
1676 | 0 | result = NULL; |
1677 | 0 | break; |
1678 | 0 | } |
1679 | 0 | result = TextDatumGetCString(argnames[i]); |
1680 | 0 | if (result == NULL || result[0] == '\0') |
1681 | 0 | { |
1682 | | /* Parameter is not named, so forget it */ |
1683 | 0 | result = NULL; |
1684 | 0 | break; |
1685 | 0 | } |
1686 | 0 | } |
1687 | 0 | } |
1688 | | |
1689 | 0 | ReleaseSysCache(procTuple); |
1690 | |
|
1691 | 0 | return result; |
1692 | 0 | } |
1693 | | |
1694 | | |
1695 | | /* |
1696 | | * build_function_result_tupdesc_t |
1697 | | * |
1698 | | * Given a pg_proc row for a function, return a tuple descriptor for the |
1699 | | * result rowtype, or NULL if the function does not have OUT parameters. |
1700 | | * |
1701 | | * Note that this does not handle resolution of polymorphic types; |
1702 | | * that is deliberate. |
1703 | | */ |
1704 | | TupleDesc |
1705 | | build_function_result_tupdesc_t(HeapTuple procTuple) |
1706 | 0 | { |
1707 | 0 | Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(procTuple); |
1708 | 0 | Datum proallargtypes; |
1709 | 0 | Datum proargmodes; |
1710 | 0 | Datum proargnames; |
1711 | 0 | bool isnull; |
1712 | | |
1713 | | /* Return NULL if the function isn't declared to return RECORD */ |
1714 | 0 | if (procform->prorettype != RECORDOID) |
1715 | 0 | return NULL; |
1716 | | |
1717 | | /* If there are no OUT parameters, return NULL */ |
1718 | 0 | if (heap_attisnull(procTuple, Anum_pg_proc_proallargtypes, NULL) || |
1719 | 0 | heap_attisnull(procTuple, Anum_pg_proc_proargmodes, NULL)) |
1720 | 0 | return NULL; |
1721 | | |
1722 | | /* Get the data out of the tuple */ |
1723 | 0 | proallargtypes = SysCacheGetAttrNotNull(PROCOID, procTuple, |
1724 | 0 | Anum_pg_proc_proallargtypes); |
1725 | 0 | proargmodes = SysCacheGetAttrNotNull(PROCOID, procTuple, |
1726 | 0 | Anum_pg_proc_proargmodes); |
1727 | 0 | proargnames = SysCacheGetAttr(PROCOID, procTuple, |
1728 | 0 | Anum_pg_proc_proargnames, |
1729 | 0 | &isnull); |
1730 | 0 | if (isnull) |
1731 | 0 | proargnames = PointerGetDatum(NULL); /* just to be sure */ |
1732 | |
|
1733 | 0 | return build_function_result_tupdesc_d(procform->prokind, |
1734 | 0 | proallargtypes, |
1735 | 0 | proargmodes, |
1736 | 0 | proargnames); |
1737 | 0 | } |
1738 | | |
1739 | | /* |
1740 | | * build_function_result_tupdesc_d |
1741 | | * |
1742 | | * Build a RECORD function's tupledesc from the pg_proc proallargtypes, |
1743 | | * proargmodes, and proargnames arrays. This is split out for the |
1744 | | * convenience of ProcedureCreate, which needs to be able to compute the |
1745 | | * tupledesc before actually creating the function. |
1746 | | * |
1747 | | * For functions (but not for procedures), returns NULL if there are not at |
1748 | | * least two OUT or INOUT arguments. |
1749 | | */ |
1750 | | TupleDesc |
1751 | | build_function_result_tupdesc_d(char prokind, |
1752 | | Datum proallargtypes, |
1753 | | Datum proargmodes, |
1754 | | Datum proargnames) |
1755 | 0 | { |
1756 | 0 | TupleDesc desc; |
1757 | 0 | ArrayType *arr; |
1758 | 0 | int numargs; |
1759 | 0 | Oid *argtypes; |
1760 | 0 | char *argmodes; |
1761 | 0 | Datum *argnames = NULL; |
1762 | 0 | Oid *outargtypes; |
1763 | 0 | char **outargnames; |
1764 | 0 | int numoutargs; |
1765 | 0 | int nargnames; |
1766 | 0 | int i; |
1767 | | |
1768 | | /* Can't have output args if columns are null */ |
1769 | 0 | if (proallargtypes == PointerGetDatum(NULL) || |
1770 | 0 | proargmodes == PointerGetDatum(NULL)) |
1771 | 0 | return NULL; |
1772 | | |
1773 | | /* |
1774 | | * We expect the arrays to be 1-D arrays of the right types; verify that. |
1775 | | * For the OID and char arrays, we don't need to use deconstruct_array() |
1776 | | * since the array data is just going to look like a C array of values. |
1777 | | */ |
1778 | 0 | arr = DatumGetArrayTypeP(proallargtypes); /* ensure not toasted */ |
1779 | 0 | numargs = ARR_DIMS(arr)[0]; |
1780 | 0 | if (ARR_NDIM(arr) != 1 || |
1781 | 0 | numargs < 0 || |
1782 | 0 | ARR_HASNULL(arr) || |
1783 | 0 | ARR_ELEMTYPE(arr) != OIDOID) |
1784 | 0 | elog(ERROR, "proallargtypes is not a 1-D Oid array or it contains nulls"); |
1785 | 0 | argtypes = (Oid *) ARR_DATA_PTR(arr); |
1786 | 0 | arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */ |
1787 | 0 | if (ARR_NDIM(arr) != 1 || |
1788 | 0 | ARR_DIMS(arr)[0] != numargs || |
1789 | 0 | ARR_HASNULL(arr) || |
1790 | 0 | ARR_ELEMTYPE(arr) != CHAROID) |
1791 | 0 | elog(ERROR, "proargmodes is not a 1-D char array of length %d or it contains nulls", |
1792 | 0 | numargs); |
1793 | 0 | argmodes = (char *) ARR_DATA_PTR(arr); |
1794 | 0 | if (proargnames != PointerGetDatum(NULL)) |
1795 | 0 | { |
1796 | 0 | arr = DatumGetArrayTypeP(proargnames); /* ensure not toasted */ |
1797 | 0 | if (ARR_NDIM(arr) != 1 || |
1798 | 0 | ARR_DIMS(arr)[0] != numargs || |
1799 | 0 | ARR_HASNULL(arr) || |
1800 | 0 | ARR_ELEMTYPE(arr) != TEXTOID) |
1801 | 0 | elog(ERROR, "proargnames is not a 1-D text array of length %d or it contains nulls", |
1802 | 0 | numargs); |
1803 | 0 | deconstruct_array_builtin(arr, TEXTOID, &argnames, NULL, &nargnames); |
1804 | 0 | Assert(nargnames == numargs); |
1805 | 0 | } |
1806 | | |
1807 | | /* zero elements probably shouldn't happen, but handle it gracefully */ |
1808 | 0 | if (numargs <= 0) |
1809 | 0 | return NULL; |
1810 | | |
1811 | | /* extract output-argument types and names */ |
1812 | 0 | outargtypes = (Oid *) palloc(numargs * sizeof(Oid)); |
1813 | 0 | outargnames = (char **) palloc(numargs * sizeof(char *)); |
1814 | 0 | numoutargs = 0; |
1815 | 0 | for (i = 0; i < numargs; i++) |
1816 | 0 | { |
1817 | 0 | char *pname; |
1818 | |
|
1819 | 0 | if (argmodes[i] == PROARGMODE_IN || |
1820 | 0 | argmodes[i] == PROARGMODE_VARIADIC) |
1821 | 0 | continue; |
1822 | 0 | Assert(argmodes[i] == PROARGMODE_OUT || |
1823 | 0 | argmodes[i] == PROARGMODE_INOUT || |
1824 | 0 | argmodes[i] == PROARGMODE_TABLE); |
1825 | 0 | outargtypes[numoutargs] = argtypes[i]; |
1826 | 0 | if (argnames) |
1827 | 0 | pname = TextDatumGetCString(argnames[i]); |
1828 | 0 | else |
1829 | 0 | pname = NULL; |
1830 | 0 | if (pname == NULL || pname[0] == '\0') |
1831 | 0 | { |
1832 | | /* Parameter is not named, so gin up a column name */ |
1833 | 0 | pname = psprintf("column%d", numoutargs + 1); |
1834 | 0 | } |
1835 | 0 | outargnames[numoutargs] = pname; |
1836 | 0 | numoutargs++; |
1837 | 0 | } |
1838 | | |
1839 | | /* |
1840 | | * If there is no output argument, or only one, the function does not |
1841 | | * return tuples. |
1842 | | */ |
1843 | 0 | if (numoutargs < 2 && prokind != PROKIND_PROCEDURE) |
1844 | 0 | return NULL; |
1845 | | |
1846 | 0 | desc = CreateTemplateTupleDesc(numoutargs); |
1847 | 0 | for (i = 0; i < numoutargs; i++) |
1848 | 0 | { |
1849 | 0 | TupleDescInitEntry(desc, i + 1, |
1850 | 0 | outargnames[i], |
1851 | 0 | outargtypes[i], |
1852 | 0 | -1, |
1853 | 0 | 0); |
1854 | 0 | } |
1855 | |
|
1856 | 0 | return desc; |
1857 | 0 | } |
1858 | | |
1859 | | |
1860 | | /* |
1861 | | * RelationNameGetTupleDesc |
1862 | | * |
1863 | | * Given a (possibly qualified) relation name, build a TupleDesc. |
1864 | | * |
1865 | | * Note: while this works as advertised, it's seldom the best way to |
1866 | | * build a tupdesc for a function's result type. It's kept around |
1867 | | * only for backwards compatibility with existing user-written code. |
1868 | | */ |
1869 | | TupleDesc |
1870 | | RelationNameGetTupleDesc(const char *relname) |
1871 | 0 | { |
1872 | 0 | RangeVar *relvar; |
1873 | 0 | Relation rel; |
1874 | 0 | TupleDesc tupdesc; |
1875 | 0 | List *relname_list; |
1876 | | |
1877 | | /* Open relation and copy the tuple description */ |
1878 | 0 | relname_list = stringToQualifiedNameList(relname, NULL); |
1879 | 0 | relvar = makeRangeVarFromNameList(relname_list); |
1880 | 0 | rel = relation_openrv(relvar, AccessShareLock); |
1881 | 0 | tupdesc = CreateTupleDescCopy(RelationGetDescr(rel)); |
1882 | 0 | relation_close(rel, AccessShareLock); |
1883 | |
|
1884 | 0 | return tupdesc; |
1885 | 0 | } |
1886 | | |
1887 | | /* |
1888 | | * TypeGetTupleDesc |
1889 | | * |
1890 | | * Given a type Oid, build a TupleDesc. (In most cases you should be |
1891 | | * using get_call_result_type or one of its siblings instead of this |
1892 | | * routine, so that you can handle OUT parameters, RECORD result type, |
1893 | | * and polymorphic results.) |
1894 | | * |
1895 | | * If the type is composite, *and* a colaliases List is provided, *and* |
1896 | | * the List is of natts length, use the aliases instead of the relation |
1897 | | * attnames. (NB: this usage is deprecated since it may result in |
1898 | | * creation of unnecessary transient record types.) |
1899 | | * |
1900 | | * If the type is a base type, a single item alias List is required. |
1901 | | */ |
1902 | | TupleDesc |
1903 | | TypeGetTupleDesc(Oid typeoid, List *colaliases) |
1904 | 0 | { |
1905 | 0 | Oid base_typeoid; |
1906 | 0 | TypeFuncClass functypclass = get_type_func_class(typeoid, &base_typeoid); |
1907 | 0 | TupleDesc tupdesc = NULL; |
1908 | | |
1909 | | /* |
1910 | | * Build a suitable tupledesc representing the output rows. We |
1911 | | * intentionally do not support TYPEFUNC_COMPOSITE_DOMAIN here, as it's |
1912 | | * unlikely that legacy callers of this obsolete function would be |
1913 | | * prepared to apply domain constraints. |
1914 | | */ |
1915 | 0 | if (functypclass == TYPEFUNC_COMPOSITE) |
1916 | 0 | { |
1917 | | /* Composite data type, e.g. a table's row type */ |
1918 | 0 | tupdesc = lookup_rowtype_tupdesc_copy(base_typeoid, -1); |
1919 | |
|
1920 | 0 | if (colaliases != NIL) |
1921 | 0 | { |
1922 | 0 | int natts = tupdesc->natts; |
1923 | 0 | int varattno; |
1924 | | |
1925 | | /* does the list length match the number of attributes? */ |
1926 | 0 | if (list_length(colaliases) != natts) |
1927 | 0 | ereport(ERROR, |
1928 | 0 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
1929 | 0 | errmsg("number of aliases does not match number of columns"))); |
1930 | | |
1931 | | /* OK, use the aliases instead */ |
1932 | 0 | for (varattno = 0; varattno < natts; varattno++) |
1933 | 0 | { |
1934 | 0 | char *label = strVal(list_nth(colaliases, varattno)); |
1935 | 0 | Form_pg_attribute attr = TupleDescAttr(tupdesc, varattno); |
1936 | |
|
1937 | 0 | if (label != NULL) |
1938 | 0 | namestrcpy(&(attr->attname), label); |
1939 | 0 | } |
1940 | | |
1941 | | /* The tuple type is now an anonymous record type */ |
1942 | 0 | tupdesc->tdtypeid = RECORDOID; |
1943 | 0 | tupdesc->tdtypmod = -1; |
1944 | 0 | } |
1945 | 0 | } |
1946 | 0 | else if (functypclass == TYPEFUNC_SCALAR) |
1947 | 0 | { |
1948 | | /* Base data type, i.e. scalar */ |
1949 | 0 | char *attname; |
1950 | | |
1951 | | /* the alias list is required for base types */ |
1952 | 0 | if (colaliases == NIL) |
1953 | 0 | ereport(ERROR, |
1954 | 0 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
1955 | 0 | errmsg("no column alias was provided"))); |
1956 | | |
1957 | | /* the alias list length must be 1 */ |
1958 | 0 | if (list_length(colaliases) != 1) |
1959 | 0 | ereport(ERROR, |
1960 | 0 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
1961 | 0 | errmsg("number of aliases does not match number of columns"))); |
1962 | | |
1963 | | /* OK, get the column alias */ |
1964 | 0 | attname = strVal(linitial(colaliases)); |
1965 | |
|
1966 | 0 | tupdesc = CreateTemplateTupleDesc(1); |
1967 | 0 | TupleDescInitEntry(tupdesc, |
1968 | 0 | (AttrNumber) 1, |
1969 | 0 | attname, |
1970 | 0 | typeoid, |
1971 | 0 | -1, |
1972 | 0 | 0); |
1973 | 0 | } |
1974 | 0 | else if (functypclass == TYPEFUNC_RECORD) |
1975 | 0 | { |
1976 | | /* XXX can't support this because typmod wasn't passed in ... */ |
1977 | 0 | ereport(ERROR, |
1978 | 0 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
1979 | 0 | errmsg("could not determine row description for function returning record"))); |
1980 | 0 | } |
1981 | 0 | else |
1982 | 0 | { |
1983 | | /* crummy error message, but parser should have caught this */ |
1984 | 0 | elog(ERROR, "function in FROM has unsupported return type"); |
1985 | 0 | } |
1986 | | |
1987 | 0 | return tupdesc; |
1988 | 0 | } |
1989 | | |
1990 | | /* |
1991 | | * extract_variadic_args |
1992 | | * |
1993 | | * Extract a set of argument values, types and NULL markers for a given |
1994 | | * input function which makes use of a VARIADIC input whose argument list |
1995 | | * depends on the caller context. When doing a VARIADIC call, the caller |
1996 | | * has provided one argument made of an array of values, so deconstruct the |
1997 | | * array data before using it for the next processing. If no VARIADIC call |
1998 | | * is used, just fill in the status data based on all the arguments given |
1999 | | * by the caller. |
2000 | | * |
2001 | | * This function returns the number of arguments generated, or -1 in the |
2002 | | * case of "VARIADIC NULL". |
2003 | | */ |
2004 | | int |
2005 | | extract_variadic_args(FunctionCallInfo fcinfo, int variadic_start, |
2006 | | bool convert_unknown, Datum **args, Oid **types, |
2007 | | bool **nulls) |
2008 | 0 | { |
2009 | 0 | bool variadic = get_fn_expr_variadic(fcinfo->flinfo); |
2010 | 0 | Datum *args_res; |
2011 | 0 | bool *nulls_res; |
2012 | 0 | Oid *types_res; |
2013 | 0 | int nargs, |
2014 | 0 | i; |
2015 | |
|
2016 | 0 | *args = NULL; |
2017 | 0 | *types = NULL; |
2018 | 0 | *nulls = NULL; |
2019 | |
|
2020 | 0 | if (variadic) |
2021 | 0 | { |
2022 | 0 | ArrayType *array_in; |
2023 | 0 | Oid element_type; |
2024 | 0 | bool typbyval; |
2025 | 0 | char typalign; |
2026 | 0 | int16 typlen; |
2027 | |
|
2028 | 0 | Assert(PG_NARGS() == variadic_start + 1); |
2029 | |
|
2030 | 0 | if (PG_ARGISNULL(variadic_start)) |
2031 | 0 | return -1; |
2032 | | |
2033 | 0 | array_in = PG_GETARG_ARRAYTYPE_P(variadic_start); |
2034 | 0 | element_type = ARR_ELEMTYPE(array_in); |
2035 | |
|
2036 | 0 | get_typlenbyvalalign(element_type, |
2037 | 0 | &typlen, &typbyval, &typalign); |
2038 | 0 | deconstruct_array(array_in, element_type, typlen, typbyval, |
2039 | 0 | typalign, &args_res, &nulls_res, |
2040 | 0 | &nargs); |
2041 | | |
2042 | | /* All the elements of the array have the same type */ |
2043 | 0 | types_res = (Oid *) palloc0(nargs * sizeof(Oid)); |
2044 | 0 | for (i = 0; i < nargs; i++) |
2045 | 0 | types_res[i] = element_type; |
2046 | 0 | } |
2047 | 0 | else |
2048 | 0 | { |
2049 | 0 | nargs = PG_NARGS() - variadic_start; |
2050 | 0 | Assert(nargs > 0); |
2051 | 0 | nulls_res = (bool *) palloc0(nargs * sizeof(bool)); |
2052 | 0 | args_res = (Datum *) palloc0(nargs * sizeof(Datum)); |
2053 | 0 | types_res = (Oid *) palloc0(nargs * sizeof(Oid)); |
2054 | |
|
2055 | 0 | for (i = 0; i < nargs; i++) |
2056 | 0 | { |
2057 | 0 | nulls_res[i] = PG_ARGISNULL(i + variadic_start); |
2058 | 0 | types_res[i] = get_fn_expr_argtype(fcinfo->flinfo, |
2059 | 0 | i + variadic_start); |
2060 | | |
2061 | | /* |
2062 | | * Turn a constant (more or less literal) value that's of unknown |
2063 | | * type into text if required. Unknowns come in as a cstring |
2064 | | * pointer. Note: for functions declared as taking type "any", the |
2065 | | * parser will not do any type conversion on unknown-type literals |
2066 | | * (that is, undecorated strings or NULLs). |
2067 | | */ |
2068 | 0 | if (convert_unknown && |
2069 | 0 | types_res[i] == UNKNOWNOID && |
2070 | 0 | get_fn_expr_arg_stable(fcinfo->flinfo, i + variadic_start)) |
2071 | 0 | { |
2072 | 0 | types_res[i] = TEXTOID; |
2073 | |
|
2074 | 0 | if (PG_ARGISNULL(i + variadic_start)) |
2075 | 0 | args_res[i] = (Datum) 0; |
2076 | 0 | else |
2077 | 0 | args_res[i] = |
2078 | 0 | CStringGetTextDatum(PG_GETARG_POINTER(i + variadic_start)); |
2079 | 0 | } |
2080 | 0 | else |
2081 | 0 | { |
2082 | | /* no conversion needed, just take the datum as given */ |
2083 | 0 | args_res[i] = PG_GETARG_DATUM(i + variadic_start); |
2084 | 0 | } |
2085 | |
|
2086 | 0 | if (!OidIsValid(types_res[i]) || |
2087 | 0 | (convert_unknown && types_res[i] == UNKNOWNOID)) |
2088 | 0 | ereport(ERROR, |
2089 | 0 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
2090 | 0 | errmsg("could not determine data type for argument %d", |
2091 | 0 | i + 1))); |
2092 | 0 | } |
2093 | 0 | } |
2094 | | |
2095 | | /* Fill in results */ |
2096 | 0 | *args = args_res; |
2097 | 0 | *nulls = nulls_res; |
2098 | 0 | *types = types_res; |
2099 | |
|
2100 | 0 | return nargs; |
2101 | 0 | } |