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