Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * funcapi.c
4 : : * Utility and convenience functions for fmgr functions that return
5 : : * sets and/or composite types, 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
1054 michael@paquier.xyz 76 :CBC 13762 : InitMaterializedSRF(FunctionCallInfo fcinfo, bits32 flags)
77 : : {
78 : : bool random_access;
1279 79 : 13762 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
80 : : Tuplestorestate *tupstore;
81 : : MemoryContext old_context,
82 : : per_query_ctx;
83 : : TupleDesc stored_tupdesc;
84 : :
85 : : /* check to see if caller supports returning a tuplestore */
86 [ + - - + ]: 13762 : if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
1279 michael@paquier.xyz 87 [ # # ]:UBC 0 : ereport(ERROR,
88 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
89 : : errmsg("set-valued function called in context that cannot accept a set")));
1279 michael@paquier.xyz 90 [ + - ]:CBC 13762 : if (!(rsinfo->allowedModes & SFRM_Materialize) ||
1054 91 [ + + - + ]: 13762 : ((flags & MAT_SRF_USE_EXPECTED_DESC) != 0 && rsinfo->expectedDesc == NULL))
1279 michael@paquier.xyz 92 [ # # ]:UBC 0 : ereport(ERROR,
93 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
94 : : 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 : : */
1279 michael@paquier.xyz 100 :CBC 13762 : per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
101 : 13762 : old_context = MemoryContextSwitchTo(per_query_ctx);
102 : :
103 : : /* build a tuple descriptor for our result type */
1054 104 [ + + ]: 13762 : if ((flags & MAT_SRF_USE_EXPECTED_DESC) != 0)
1279 105 : 980 : stored_tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
106 : : else
107 : : {
108 [ - + ]: 12782 : if (get_call_result_type(fcinfo, NULL, &stored_tupdesc) != TYPEFUNC_COMPOSITE)
1279 michael@paquier.xyz 109 [ # # ]:UBC 0 : elog(ERROR, "return type must be a row type");
110 : : }
111 : :
112 : : /* If requested, bless the tuple descriptor */
1054 michael@paquier.xyz 113 [ + + ]:CBC 13762 : if ((flags & MAT_SRF_BLESS) != 0)
1279 114 : 6330 : BlessTupleDesc(stored_tupdesc);
115 : :
116 : 13762 : random_access = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
117 : :
118 : 13762 : tupstore = tuplestore_begin_heap(random_access, false, work_mem);
119 : 13762 : rsinfo->returnMode = SFRM_Materialize;
120 : 13762 : rsinfo->setResult = tupstore;
121 : 13762 : rsinfo->setDesc = stored_tupdesc;
122 : 13762 : MemoryContextSwitchTo(old_context);
123 : 13762 : }
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 *
8479 bruce@momjian.us 133 : 67898 : init_MultiFuncCall(PG_FUNCTION_ARGS)
134 : : {
135 : : FuncCallContext *retval;
136 : :
137 : : /*
138 : : * Bail if we're called in the wrong context
139 : : */
140 [ + - - + ]: 67898 : if (fcinfo->resultinfo == NULL || !IsA(fcinfo->resultinfo, ReturnSetInfo))
8079 tgl@sss.pgh.pa.us 141 [ # # ]:UBC 0 : ereport(ERROR,
142 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
143 : : errmsg("set-valued function called in context that cannot accept a set")));
144 : :
8479 bruce@momjian.us 145 [ + - ]:CBC 67898 : if (fcinfo->flinfo->fn_extra == NULL)
146 : : {
147 : : /*
148 : : * First call
149 : : */
5931 150 : 67898 : ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
151 : : MemoryContext multi_call_ctx;
152 : :
153 : : /*
154 : : * Create a suitably long-lived context to hold cross-call data
155 : : */
6399 neilc@samurai.com 156 : 67898 : multi_call_ctx = AllocSetContextCreate(fcinfo->flinfo->fn_mcxt,
157 : : "SRF multi-call context",
158 : : ALLOCSET_SMALL_SIZES);
159 : :
160 : : /*
161 : : * Allocate suitably long-lived space and zero it
162 : : */
163 : : retval = (FuncCallContext *)
164 : 67898 : MemoryContextAllocZero(multi_call_ctx,
165 : : sizeof(FuncCallContext));
166 : :
167 : : /*
168 : : * initialize the elements
169 : : */
8479 bruce@momjian.us 170 : 67898 : retval->call_cntr = 0;
171 : 67898 : retval->max_calls = 0;
8451 172 : 67898 : retval->user_fctx = NULL;
8479 173 : 67898 : retval->attinmeta = NULL;
7828 tgl@sss.pgh.pa.us 174 : 67898 : retval->tuple_desc = NULL;
6399 neilc@samurai.com 175 : 67898 : retval->multi_call_memory_ctx = multi_call_ctx;
176 : :
177 : : /*
178 : : * save the pointer for cross-call use
179 : : */
8479 bruce@momjian.us 180 : 67898 : 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 : : */
7932 mail@joeconway.com 186 : 67898 : RegisterExprContextCallback(rsi->econtext,
187 : : shutdown_MultiFuncCall,
188 : 67898 : PointerGetDatum(fcinfo->flinfo));
189 : : }
190 : : else
191 : : {
192 : : /* second and subsequent calls */
6792 bruce@momjian.us 193 [ # # ]:UBC 0 : elog(ERROR, "init_MultiFuncCall cannot be called more than once");
194 : :
195 : : /* never reached, but keep compiler happy */
196 : : retval = NULL;
197 : : }
198 : :
8479 bruce@momjian.us 199 :CBC 67898 : return retval;
200 : : }
201 : :
202 : : /*
203 : : * per_MultiFuncCall
204 : : *
205 : : * Do Multi-function per-call setup
206 : : */
207 : : FuncCallContext *
8451 208 : 11252175 : per_MultiFuncCall(PG_FUNCTION_ARGS)
209 : : {
210 : 11252175 : FuncCallContext *retval = (FuncCallContext *) fcinfo->flinfo->fn_extra;
211 : :
212 : 11252175 : return retval;
213 : : }
214 : :
215 : : /*
216 : : * end_MultiFuncCall
217 : : * Clean up after init_MultiFuncCall
218 : : */
219 : : void
8479 220 : 67008 : end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx)
221 : : {
7678 222 : 67008 : ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
223 : :
224 : : /* Deregister the shutdown callback */
7932 mail@joeconway.com 225 : 67008 : UnregisterExprContextCallback(rsi->econtext,
226 : : shutdown_MultiFuncCall,
227 : 67008 : PointerGetDatum(fcinfo->flinfo));
228 : :
229 : : /* But use it to do the real work */
230 : 67008 : shutdown_MultiFuncCall(PointerGetDatum(fcinfo->flinfo));
231 : 67008 : }
232 : :
233 : : /*
234 : : * shutdown_MultiFuncCall
235 : : * Shutdown function to clean up after init_MultiFuncCall
236 : : */
237 : : static void
238 : 67041 : shutdown_MultiFuncCall(Datum arg)
239 : : {
7678 bruce@momjian.us 240 : 67041 : FmgrInfo *flinfo = (FmgrInfo *) DatumGetPointer(arg);
7932 mail@joeconway.com 241 : 67041 : FuncCallContext *funcctx = (FuncCallContext *) flinfo->fn_extra;
242 : :
243 : : /* unbind from flinfo */
244 : 67041 : flinfo->fn_extra = NULL;
245 : :
246 : : /*
247 : : * Delete context that holds all multi-call data, including the
248 : : * FuncCallContext itself
249 : : */
6399 neilc@samurai.com 250 : 67041 : MemoryContextDelete(funcctx->multi_call_memory_ctx);
8479 bruce@momjian.us 251 : 67041 : }
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
7464 tgl@sss.pgh.pa.us 276 : 45027 : get_call_result_type(FunctionCallInfo fcinfo,
277 : : Oid *resultTypeId,
278 : : TupleDesc *resultTupleDesc)
279 : : {
280 : 90054 : return internal_get_result_type(fcinfo->flinfo->fn_oid,
281 : 45027 : fcinfo->flinfo->fn_expr,
282 : 45027 : (ReturnSetInfo *) fcinfo->resultinfo,
283 : : resultTypeId,
284 : : resultTupleDesc);
285 : : }
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 : 180289 : get_expr_result_type(Node *expr,
300 : : Oid *resultTypeId,
301 : : TupleDesc *resultTupleDesc)
302 : : {
303 : : TypeFuncClass result;
304 : :
305 [ + - + + ]: 180289 : if (expr && IsA(expr, FuncExpr))
306 : 174988 : result = internal_get_result_type(((FuncExpr *) expr)->funcid,
307 : : expr,
308 : : NULL,
309 : : resultTypeId,
310 : : resultTupleDesc);
7439 311 [ + - + + ]: 5301 : else if (expr && IsA(expr, OpExpr))
312 : 12 : result = internal_get_result_type(get_opcode(((OpExpr *) expr)->opno),
313 : : expr,
314 : : NULL,
315 : : resultTypeId,
316 : : resultTupleDesc);
2140 317 [ + - + + ]: 5289 : else if (expr && IsA(expr, RowExpr) &&
318 [ + - ]: 48 : ((RowExpr *) expr)->row_typeid == RECORDOID)
319 : : {
320 : : /* We can resolve the record type by generating the tupdesc directly */
321 : 48 : RowExpr *rexpr = (RowExpr *) expr;
322 : : TupleDesc tupdesc;
323 : 48 : AttrNumber i = 1;
324 : : ListCell *lcc,
325 : : *lcn;
326 : :
327 : 48 : tupdesc = CreateTemplateTupleDesc(list_length(rexpr->args));
328 [ - + ]: 48 : Assert(list_length(rexpr->args) == list_length(rexpr->colnames));
329 [ + - + + : 141 : forboth(lcc, rexpr->args, lcn, rexpr->colnames)
+ - + + +
+ + - +
+ ]
330 : : {
331 : 93 : Node *col = (Node *) lfirst(lcc);
332 : 93 : char *colname = strVal(lfirst(lcn));
333 : :
334 : 93 : TupleDescInitEntry(tupdesc, i,
335 : : colname,
336 : : exprType(col),
337 : : exprTypmod(col),
338 : : 0);
339 : 93 : TupleDescInitEntryCollation(tupdesc, i,
340 : : exprCollation(col));
341 : 93 : i++;
342 : : }
343 [ - + ]: 48 : if (resultTypeId)
2140 tgl@sss.pgh.pa.us 344 :UBC 0 : *resultTypeId = rexpr->row_typeid;
2140 tgl@sss.pgh.pa.us 345 [ + - ]:CBC 48 : if (resultTupleDesc)
346 : 48 : *resultTupleDesc = BlessTupleDesc(tupdesc);
347 : 48 : return TYPEFUNC_COMPOSITE;
348 : : }
1056 349 [ + - + + ]: 5241 : else if (expr && IsA(expr, Const) &&
350 [ + + ]: 222 : ((Const *) expr)->consttype == RECORDOID &&
351 [ + - ]: 9 : !((Const *) expr)->constisnull)
352 : : {
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 : : HeapTupleHeader rec;
359 : : Oid tupType;
360 : : int32 tupTypmod;
361 : :
362 : 9 : rec = DatumGetHeapTupleHeader(((Const *) expr)->constvalue);
363 : 9 : tupType = HeapTupleHeaderGetTypeId(rec);
364 : 9 : tupTypmod = HeapTupleHeaderGetTypMod(rec);
365 [ - + ]: 9 : if (resultTypeId)
1056 tgl@sss.pgh.pa.us 366 :UBC 0 : *resultTypeId = tupType;
1056 tgl@sss.pgh.pa.us 367 [ + - + - ]:CBC 9 : if (tupType != RECORDOID || tupTypmod >= 0)
368 : : {
369 : : /* Should be able to look it up */
370 [ + - ]: 9 : if (resultTupleDesc)
371 : 9 : *resultTupleDesc = lookup_rowtype_tupdesc_copy(tupType,
372 : : tupTypmod);
373 : 9 : return TYPEFUNC_COMPOSITE;
374 : : }
375 : : else
376 : : {
377 : : /* This shouldn't really happen ... */
1056 tgl@sss.pgh.pa.us 378 [ # # ]:UBC 0 : if (resultTupleDesc)
379 : 0 : *resultTupleDesc = NULL;
380 : 0 : return TYPEFUNC_RECORD;
381 : : }
382 : : }
383 : : else
384 : : {
385 : : /* handle as a generic expression; no chance to resolve RECORD */
7266 bruce@momjian.us 386 :CBC 5232 : Oid typid = exprType(expr);
387 : : Oid base_typid;
388 : :
7464 tgl@sss.pgh.pa.us 389 [ + + ]: 5232 : if (resultTypeId)
390 : 342 : *resultTypeId = typid;
391 [ + - ]: 5232 : if (resultTupleDesc)
392 : 5232 : *resultTupleDesc = NULL;
2872 393 : 5232 : result = get_type_func_class(typid, &base_typid);
394 [ + + + + ]: 5232 : if ((result == TYPEFUNC_COMPOSITE ||
395 [ + - ]: 4865 : result == TYPEFUNC_COMPOSITE_DOMAIN) &&
396 : : resultTupleDesc)
397 : 4865 : *resultTupleDesc = lookup_rowtype_tupdesc_copy(base_typid, -1);
398 : : }
399 : :
7464 400 : 180232 : return result;
401 : : }
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 : 3631 : get_func_result_type(Oid functionId,
411 : : Oid *resultTypeId,
412 : : TupleDesc *resultTupleDesc)
413 : : {
414 : 3631 : return internal_get_result_type(functionId,
415 : : NULL,
416 : : NULL,
417 : : resultTypeId,
418 : : resultTupleDesc);
419 : : }
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 : 223658 : internal_get_result_type(Oid funcid,
431 : : Node *call_expr,
432 : : ReturnSetInfo *rsinfo,
433 : : Oid *resultTypeId,
434 : : TupleDesc *resultTupleDesc)
435 : : {
436 : : TypeFuncClass result;
437 : : HeapTuple tp;
438 : : Form_pg_proc procform;
439 : : Oid rettype;
440 : : Oid base_rettype;
441 : : TupleDesc tupdesc;
442 : :
443 : : /* First fetch the function's pg_proc row to inspect its rettype */
5683 rhaas@postgresql.org 444 : 223658 : tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
7464 tgl@sss.pgh.pa.us 445 [ - + ]: 223658 : if (!HeapTupleIsValid(tp))
7464 tgl@sss.pgh.pa.us 446 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for function %u", funcid);
7464 tgl@sss.pgh.pa.us 447 :CBC 223658 : procform = (Form_pg_proc) GETSTRUCT(tp);
448 : :
449 : 223658 : rettype = procform->prorettype;
450 : :
451 : : /* Check for OUT parameters defining a RECORD result */
452 : 223658 : tupdesc = build_function_result_tupdesc_t(tp);
453 [ + + ]: 223658 : if (tupdesc)
454 : : {
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 [ + + ]: 159028 : if (resultTypeId)
461 : 38639 : *resultTypeId = rettype;
462 : :
463 [ + - ]: 159028 : if (resolve_polymorphic_tupdesc(tupdesc,
464 : : &procform->proargtypes,
465 : : call_expr))
466 : : {
467 [ + - ]: 159028 : if (tupdesc->tdtypeid == RECORDOID &&
468 [ + - ]: 159028 : tupdesc->tdtypmod < 0)
469 : 159028 : assign_record_type_typmod(tupdesc);
470 [ + - ]: 159028 : if (resultTupleDesc)
471 : 159028 : *resultTupleDesc = tupdesc;
472 : 159028 : result = TYPEFUNC_COMPOSITE;
473 : : }
474 : : else
475 : : {
7464 tgl@sss.pgh.pa.us 476 [ # # ]:UBC 0 : if (resultTupleDesc)
477 : 0 : *resultTupleDesc = NULL;
478 : 0 : result = TYPEFUNC_RECORD;
479 : : }
480 : :
7464 tgl@sss.pgh.pa.us 481 :CBC 159028 : ReleaseSysCache(tp);
482 : :
483 : 159028 : return result;
484 : : }
485 : :
486 : : /*
487 : : * If scalar polymorphic result, try to resolve it.
488 : : */
6732 489 [ + + + + : 64630 : if (IsPolymorphicType(rettype))
+ - + - +
+ + + + +
+ + + - +
+ + + ]
490 : : {
7266 bruce@momjian.us 491 : 8669 : Oid newrettype = exprType(call_expr);
492 : :
7464 tgl@sss.pgh.pa.us 493 [ - + ]: 8669 : if (newrettype == InvalidOid) /* this probably should not happen */
7464 tgl@sss.pgh.pa.us 494 [ # # ]:UBC 0 : ereport(ERROR,
495 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
496 : : errmsg("could not determine actual result type for function \"%s\" declared to return type %s",
497 : : NameStr(procform->proname),
498 : : format_type_be(rettype))));
7464 tgl@sss.pgh.pa.us 499 :CBC 8669 : rettype = newrettype;
500 : : }
501 : :
502 [ + + ]: 64630 : if (resultTypeId)
503 : 62149 : *resultTypeId = rettype;
504 [ + - ]: 64630 : if (resultTupleDesc)
7266 bruce@momjian.us 505 : 64630 : *resultTupleDesc = NULL; /* default result */
506 : :
507 : : /* Classify the result type */
2872 tgl@sss.pgh.pa.us 508 : 64630 : result = get_type_func_class(rettype, &base_rettype);
7464 509 [ + + + + ]: 64630 : switch (result)
510 : : {
511 : 3106 : case TYPEFUNC_COMPOSITE:
512 : : case TYPEFUNC_COMPOSITE_DOMAIN:
513 [ + - ]: 3106 : if (resultTupleDesc)
2872 514 : 3106 : *resultTupleDesc = lookup_rowtype_tupdesc_copy(base_rettype, -1);
515 : : /* Named composite types can't have any polymorphic columns */
7464 516 : 3106 : break;
517 : 60686 : case TYPEFUNC_SCALAR:
518 : 60686 : break;
519 : 832 : case TYPEFUNC_RECORD:
520 : : /* We must get the tupledesc from call context */
521 [ + + + - ]: 832 : if (rsinfo && IsA(rsinfo, ReturnSetInfo) &&
522 [ + + ]: 331 : rsinfo->expectedDesc != NULL)
523 : : {
524 : 299 : result = TYPEFUNC_COMPOSITE;
525 [ + - ]: 299 : if (resultTupleDesc)
526 : 299 : *resultTupleDesc = rsinfo->expectedDesc;
527 : : /* Assume no polymorphic columns here, either */
528 : : }
529 : 832 : break;
530 : 6 : default:
531 : 6 : break;
532 : : }
533 : :
534 : 64630 : ReleaseSysCache(tp);
535 : :
536 : 64630 : return result;
537 : : }
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
2872 551 : 103149 : get_expr_result_tupdesc(Node *expr, bool noError)
552 : : {
553 : : TupleDesc tupleDesc;
554 : : TypeFuncClass functypclass;
555 : :
556 : 103149 : functypclass = get_expr_result_type(expr, NULL, &tupleDesc);
557 : :
558 [ + + + + ]: 103149 : if (functypclass == TYPEFUNC_COMPOSITE ||
559 : : functypclass == TYPEFUNC_COMPOSITE_DOMAIN)
560 : 102846 : return tupleDesc;
561 : :
562 [ - + ]: 303 : if (!noError)
563 : : {
2872 tgl@sss.pgh.pa.us 564 :UBC 0 : Oid exprTypeId = exprType(expr);
565 : :
566 [ # # ]: 0 : if (exprTypeId != RECORDOID)
567 [ # # ]: 0 : ereport(ERROR,
568 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
569 : : errmsg("type %s is not composite",
570 : : format_type_be(exprTypeId))));
571 : : else
572 [ # # ]: 0 : ereport(ERROR,
573 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
574 : : errmsg("record type has not been registered")));
575 : : }
576 : :
2872 tgl@sss.pgh.pa.us 577 :CBC 303 : return NULL;
578 : : }
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
2002 589 : 1065 : resolve_anyelement_from_others(polymorphic_actuals *actuals)
590 : : {
591 [ + + ]: 1065 : if (OidIsValid(actuals->anyarray_type))
592 : : {
593 : : /* Use the element type corresponding to actual type */
594 : 954 : Oid array_base_type = getBaseType(actuals->anyarray_type);
595 : 954 : Oid array_typelem = get_element_type(array_base_type);
596 : :
597 [ - + ]: 954 : if (!OidIsValid(array_typelem))
2002 tgl@sss.pgh.pa.us 598 [ # # ]:UBC 0 : ereport(ERROR,
599 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
600 : : errmsg("argument declared %s is not an array but type %s",
601 : : "anyarray",
602 : : format_type_be(array_base_type))));
2002 tgl@sss.pgh.pa.us 603 :CBC 954 : actuals->anyelement_type = array_typelem;
604 : : }
605 [ + + ]: 111 : else if (OidIsValid(actuals->anyrange_type))
606 : : {
607 : : /* Use the element type corresponding to actual type */
608 : 87 : Oid range_base_type = getBaseType(actuals->anyrange_type);
609 : 87 : Oid range_typelem = get_range_subtype(range_base_type);
610 : :
611 [ - + ]: 87 : if (!OidIsValid(range_typelem))
2002 tgl@sss.pgh.pa.us 612 [ # # ]:UBC 0 : ereport(ERROR,
613 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
614 : : errmsg("argument declared %s is not a range type but type %s",
615 : : "anyrange",
616 : : format_type_be(range_base_type))));
2002 tgl@sss.pgh.pa.us 617 :CBC 87 : actuals->anyelement_type = range_typelem;
618 : : }
1721 akorotkov@postgresql 619 [ + - ]: 24 : else if (OidIsValid(actuals->anymultirange_type))
620 : : {
621 : : /* Use the element type based on the multirange type */
622 : : Oid multirange_base_type;
623 : : Oid multirange_typelem;
624 : : Oid range_base_type;
625 : : Oid range_typelem;
626 : :
627 : 24 : multirange_base_type = getBaseType(actuals->anymultirange_type);
628 : 24 : multirange_typelem = get_multirange_range(multirange_base_type);
629 [ - + ]: 24 : if (!OidIsValid(multirange_typelem))
1721 akorotkov@postgresql 630 [ # # ]:UBC 0 : ereport(ERROR,
631 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
632 : : errmsg("argument declared %s is not a multirange type but type %s",
633 : : "anymultirange",
634 : : format_type_be(multirange_base_type))));
635 : :
1721 akorotkov@postgresql 636 :CBC 24 : range_base_type = getBaseType(multirange_typelem);
637 : 24 : range_typelem = get_range_subtype(range_base_type);
638 : :
639 [ - + ]: 24 : if (!OidIsValid(range_typelem))
1721 akorotkov@postgresql 640 [ # # ]:UBC 0 : ereport(ERROR,
641 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
642 : : errmsg("argument declared %s does not contain a range type but type %s",
643 : : "anymultirange",
644 : : format_type_be(range_base_type))));
1721 akorotkov@postgresql 645 :CBC 24 : actuals->anyelement_type = range_typelem;
646 : : }
647 : : else
2002 tgl@sss.pgh.pa.us 648 [ # # ]:UBC 0 : elog(ERROR, "could not determine polymorphic type");
2002 tgl@sss.pgh.pa.us 649 :CBC 1065 : }
650 : :
651 : : /*
652 : : * Resolve actual type of ANYARRAY from other polymorphic inputs
653 : : */
654 : : static void
655 : 202 : resolve_anyarray_from_others(polymorphic_actuals *actuals)
656 : : {
657 : : /* If we don't know ANYELEMENT, resolve that first */
658 [ + + ]: 202 : if (!OidIsValid(actuals->anyelement_type))
659 : 36 : resolve_anyelement_from_others(actuals);
660 : :
661 [ + - ]: 202 : if (OidIsValid(actuals->anyelement_type))
662 : : {
663 : : /* Use the array type corresponding to actual type */
664 : 202 : Oid array_typeid = get_array_type(actuals->anyelement_type);
665 : :
666 [ - + ]: 202 : if (!OidIsValid(array_typeid))
2002 tgl@sss.pgh.pa.us 667 [ # # ]:UBC 0 : ereport(ERROR,
668 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
669 : : errmsg("could not find array type for data type %s",
670 : : format_type_be(actuals->anyelement_type))));
2002 tgl@sss.pgh.pa.us 671 :CBC 202 : actuals->anyarray_type = array_typeid;
672 : : }
673 : : else
2002 tgl@sss.pgh.pa.us 674 [ # # ]:UBC 0 : elog(ERROR, "could not determine polymorphic type");
2002 tgl@sss.pgh.pa.us 675 :CBC 202 : }
676 : :
677 : : /*
678 : : * Resolve actual type of ANYRANGE from other polymorphic inputs
679 : : */
680 : : static void
681 : 12 : resolve_anyrange_from_others(polymorphic_actuals *actuals)
682 : : {
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 : : */
1721 akorotkov@postgresql 688 [ + - ]: 12 : if (OidIsValid(actuals->anymultirange_type))
689 : : {
690 : : /* Use the element type based on the multirange type */
691 : 12 : Oid multirange_base_type = getBaseType(actuals->anymultirange_type);
692 : 12 : Oid multirange_typelem = get_multirange_range(multirange_base_type);
693 : :
694 [ - + ]: 12 : if (!OidIsValid(multirange_typelem))
1721 akorotkov@postgresql 695 [ # # ]:UBC 0 : ereport(ERROR,
696 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
697 : : errmsg("argument declared %s is not a multirange type but type %s",
698 : : "anymultirange",
699 : : format_type_be(multirange_base_type))));
1721 akorotkov@postgresql 700 :CBC 12 : actuals->anyrange_type = multirange_typelem;
701 : : }
702 : : else
1721 akorotkov@postgresql 703 [ # # ]:UBC 0 : elog(ERROR, "could not determine polymorphic type");
1721 akorotkov@postgresql 704 :CBC 12 : }
705 : :
706 : : /*
707 : : * Resolve actual type of ANYMULTIRANGE from other polymorphic inputs
708 : : */
709 : : static void
710 : 12 : resolve_anymultirange_from_others(polymorphic_actuals *actuals)
711 : : {
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 [ + - ]: 12 : if (OidIsValid(actuals->anyrange_type))
718 : : {
719 : 12 : Oid range_base_type = getBaseType(actuals->anyrange_type);
720 : 12 : Oid multirange_typeid = get_range_multirange(range_base_type);
721 : :
722 [ - + ]: 12 : if (!OidIsValid(multirange_typeid))
1721 akorotkov@postgresql 723 [ # # ]:UBC 0 : ereport(ERROR,
724 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
725 : : errmsg("could not find multirange type for data type %s",
726 : : format_type_be(actuals->anyrange_type))));
1721 akorotkov@postgresql 727 :CBC 12 : actuals->anymultirange_type = multirange_typeid;
728 : : }
729 : : else
1721 akorotkov@postgresql 730 [ # # ]:UBC 0 : elog(ERROR, "could not determine polymorphic type");
2002 tgl@sss.pgh.pa.us 731 :CBC 12 : }
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
7464 744 : 159028 : resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
745 : : Node *call_expr)
746 : : {
747 : 159028 : int natts = tupdesc->natts;
748 : 159028 : int nargs = declared_args->dim1;
2002 749 : 159028 : bool have_polymorphic_result = false;
7464 750 : 159028 : bool have_anyelement_result = false;
751 : 159028 : bool have_anyarray_result = false;
5056 heikki.linnakangas@i 752 : 159028 : bool have_anyrange_result = false;
1721 akorotkov@postgresql 753 : 159028 : bool have_anymultirange_result = false;
1997 tgl@sss.pgh.pa.us 754 : 159028 : bool have_anycompatible_result = false;
755 : 159028 : bool have_anycompatible_array_result = false;
756 : 159028 : bool have_anycompatible_range_result = false;
1721 akorotkov@postgresql 757 : 159028 : bool have_anycompatible_multirange_result = false;
758 : : polymorphic_actuals poly_actuals;
759 : : polymorphic_actuals anyc_actuals;
5056 heikki.linnakangas@i 760 : 159028 : Oid anycollation = InvalidOid;
1997 tgl@sss.pgh.pa.us 761 : 159028 : Oid anycompatcollation = InvalidOid;
762 : : int i;
763 : :
764 : : /* See if there are any polymorphic outputs; quick out if not */
7464 765 [ + + ]: 2770371 : for (i = 0; i < natts; i++)
766 : : {
2939 andres@anarazel.de 767 [ + + + + : 2611343 : switch (TupleDescAttr(tupdesc, i)->atttypid)
+ + + -
+ ]
768 : : {
7464 tgl@sss.pgh.pa.us 769 : 1120 : case ANYELEMENTOID:
770 : : case ANYNONARRAYOID:
771 : : case ANYENUMOID:
2002 772 : 1120 : have_polymorphic_result = true;
7464 773 : 1120 : have_anyelement_result = true;
774 : 1120 : break;
775 : 166 : case ANYARRAYOID:
2002 776 : 166 : have_polymorphic_result = true;
7464 777 : 166 : have_anyarray_result = true;
778 : 166 : break;
5056 heikki.linnakangas@i 779 : 36 : case ANYRANGEOID:
2002 tgl@sss.pgh.pa.us 780 : 36 : have_polymorphic_result = true;
5056 heikki.linnakangas@i 781 : 36 : have_anyrange_result = true;
782 : 36 : break;
1721 akorotkov@postgresql 783 : 48 : case ANYMULTIRANGEOID:
784 : 48 : have_polymorphic_result = true;
785 : 48 : have_anymultirange_result = true;
786 : 48 : break;
1997 tgl@sss.pgh.pa.us 787 : 63 : case ANYCOMPATIBLEOID:
788 : : case ANYCOMPATIBLENONARRAYOID:
789 : 63 : have_polymorphic_result = true;
790 : 63 : have_anycompatible_result = true;
791 : 63 : break;
792 : 123 : case ANYCOMPATIBLEARRAYOID:
793 : 123 : have_polymorphic_result = true;
794 : 123 : have_anycompatible_array_result = true;
795 : 123 : break;
796 : 18 : case ANYCOMPATIBLERANGEOID:
797 : 18 : have_polymorphic_result = true;
798 : 18 : have_anycompatible_range_result = true;
799 : 18 : break;
1721 akorotkov@postgresql 800 :UBC 0 : case ANYCOMPATIBLEMULTIRANGEOID:
801 : 0 : have_polymorphic_result = true;
802 : 0 : have_anycompatible_multirange_result = true;
803 : 0 : break;
7464 tgl@sss.pgh.pa.us 804 :CBC 2609769 : default:
805 : 2609769 : break;
806 : : }
807 : : }
2002 808 [ + + ]: 159028 : if (!have_polymorphic_result)
7464 809 : 157737 : 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 [ - + ]: 1291 : if (!call_expr)
7464 tgl@sss.pgh.pa.us 819 :UBC 0 : return false; /* no hope */
820 : :
2002 tgl@sss.pgh.pa.us 821 :CBC 1291 : memset(&poly_actuals, 0, sizeof(poly_actuals));
1997 822 : 1291 : memset(&anyc_actuals, 0, sizeof(anyc_actuals));
823 : :
7464 824 [ + + ]: 2813 : for (i = 0; i < nargs; i++)
825 : : {
826 [ + + + + : 1522 : switch (declared_args->values[i])
+ + + -
- ]
827 : : {
828 : 166 : case ANYELEMENTOID:
829 : : case ANYNONARRAYOID:
830 : : case ANYENUMOID:
2002 831 [ + + ]: 166 : if (!OidIsValid(poly_actuals.anyelement_type))
832 : : {
833 : 154 : poly_actuals.anyelement_type =
834 : 154 : get_call_expr_argtype(call_expr, i);
835 [ - + ]: 154 : if (!OidIsValid(poly_actuals.anyelement_type))
2002 tgl@sss.pgh.pa.us 836 :UBC 0 : return false;
837 : : }
7464 tgl@sss.pgh.pa.us 838 :CBC 166 : break;
839 : 1014 : case ANYARRAYOID:
2002 840 [ + - ]: 1014 : if (!OidIsValid(poly_actuals.anyarray_type))
841 : : {
842 : 1014 : poly_actuals.anyarray_type =
843 : 1014 : get_call_expr_argtype(call_expr, i);
844 [ - + ]: 1014 : if (!OidIsValid(poly_actuals.anyarray_type))
2002 tgl@sss.pgh.pa.us 845 :UBC 0 : return false;
846 : : }
7464 tgl@sss.pgh.pa.us 847 :CBC 1014 : break;
5056 heikki.linnakangas@i 848 : 72 : case ANYRANGEOID:
2002 tgl@sss.pgh.pa.us 849 [ + - ]: 72 : if (!OidIsValid(poly_actuals.anyrange_type))
850 : : {
851 : 72 : poly_actuals.anyrange_type =
852 : 72 : get_call_expr_argtype(call_expr, i);
853 [ - + ]: 72 : if (!OidIsValid(poly_actuals.anyrange_type))
2002 tgl@sss.pgh.pa.us 854 :UBC 0 : return false;
855 : : }
5056 heikki.linnakangas@i 856 :CBC 72 : break;
1721 akorotkov@postgresql 857 : 60 : case ANYMULTIRANGEOID:
858 [ + - ]: 60 : if (!OidIsValid(poly_actuals.anymultirange_type))
859 : : {
860 : 60 : poly_actuals.anymultirange_type =
861 : 60 : get_call_expr_argtype(call_expr, i);
862 [ - + ]: 60 : if (!OidIsValid(poly_actuals.anymultirange_type))
1721 akorotkov@postgresql 863 :UBC 0 : return false;
864 : : }
1721 akorotkov@postgresql 865 :CBC 60 : break;
1997 tgl@sss.pgh.pa.us 866 : 147 : case ANYCOMPATIBLEOID:
867 : : case ANYCOMPATIBLENONARRAYOID:
868 [ + + ]: 147 : if (!OidIsValid(anyc_actuals.anyelement_type))
869 : : {
870 : 87 : anyc_actuals.anyelement_type =
871 : 87 : get_call_expr_argtype(call_expr, i);
872 [ - + ]: 87 : if (!OidIsValid(anyc_actuals.anyelement_type))
1997 tgl@sss.pgh.pa.us 873 :UBC 0 : return false;
874 : : }
1997 tgl@sss.pgh.pa.us 875 :CBC 147 : break;
876 : 27 : case ANYCOMPATIBLEARRAYOID:
877 [ + - ]: 27 : if (!OidIsValid(anyc_actuals.anyarray_type))
878 : : {
879 : 27 : anyc_actuals.anyarray_type =
880 : 27 : get_call_expr_argtype(call_expr, i);
881 [ - + ]: 27 : if (!OidIsValid(anyc_actuals.anyarray_type))
1997 tgl@sss.pgh.pa.us 882 :UBC 0 : return false;
883 : : }
1997 tgl@sss.pgh.pa.us 884 :CBC 27 : break;
885 : 36 : case ANYCOMPATIBLERANGEOID:
886 [ + - ]: 36 : if (!OidIsValid(anyc_actuals.anyrange_type))
887 : : {
888 : 36 : anyc_actuals.anyrange_type =
889 : 36 : get_call_expr_argtype(call_expr, i);
890 [ - + ]: 36 : if (!OidIsValid(anyc_actuals.anyrange_type))
1997 tgl@sss.pgh.pa.us 891 :UBC 0 : return false;
892 : : }
1997 tgl@sss.pgh.pa.us 893 :CBC 36 : break;
1721 akorotkov@postgresql 894 :UBC 0 : case ANYCOMPATIBLEMULTIRANGEOID:
895 [ # # ]: 0 : if (!OidIsValid(anyc_actuals.anymultirange_type))
896 : : {
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 : : }
902 : 0 : break;
7464 tgl@sss.pgh.pa.us 903 : 0 : default:
904 : 0 : break;
905 : : }
906 : : }
907 : :
908 : : /* If needed, deduce one polymorphic type from others */
2002 tgl@sss.pgh.pa.us 909 [ + + + + ]:CBC 1291 : if (have_anyelement_result && !OidIsValid(poly_actuals.anyelement_type))
910 : 990 : resolve_anyelement_from_others(&poly_actuals);
911 : :
912 [ + + + + ]: 1291 : if (have_anyarray_result && !OidIsValid(poly_actuals.anyarray_type))
913 : 94 : resolve_anyarray_from_others(&poly_actuals);
914 : :
915 [ + + + + ]: 1291 : if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
916 : 12 : resolve_anyrange_from_others(&poly_actuals);
917 : :
1721 akorotkov@postgresql 918 [ + + + + ]: 1291 : if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type))
919 : 12 : resolve_anymultirange_from_others(&poly_actuals);
920 : :
1997 tgl@sss.pgh.pa.us 921 [ + + + + ]: 1291 : if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
922 : 36 : resolve_anyelement_from_others(&anyc_actuals);
923 : :
924 [ + + + + ]: 1291 : if (have_anycompatible_array_result && !OidIsValid(anyc_actuals.anyarray_type))
925 : 96 : resolve_anyarray_from_others(&anyc_actuals);
926 : :
927 [ + + - + ]: 1291 : if (have_anycompatible_range_result && !OidIsValid(anyc_actuals.anyrange_type))
1997 tgl@sss.pgh.pa.us 928 :UBC 0 : resolve_anyrange_from_others(&anyc_actuals);
929 : :
1721 akorotkov@postgresql 930 [ - + - - ]:CBC 1291 : if (have_anycompatible_multirange_result && !OidIsValid(anyc_actuals.anymultirange_type))
1721 akorotkov@postgresql 931 :UBC 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 : : */
2002 tgl@sss.pgh.pa.us 940 [ + + ]:CBC 1291 : if (OidIsValid(poly_actuals.anyelement_type))
941 : 1180 : anycollation = get_typcollation(poly_actuals.anyelement_type);
942 [ - + ]: 111 : else if (OidIsValid(poly_actuals.anyarray_type))
2002 tgl@sss.pgh.pa.us 943 :UBC 0 : anycollation = get_typcollation(poly_actuals.anyarray_type);
944 : :
1997 tgl@sss.pgh.pa.us 945 [ + + ]:CBC 1291 : if (OidIsValid(anyc_actuals.anyelement_type))
946 : 123 : anycompatcollation = get_typcollation(anyc_actuals.anyelement_type);
947 [ - + ]: 1168 : else if (OidIsValid(anyc_actuals.anyarray_type))
1997 tgl@sss.pgh.pa.us 948 :UBC 0 : anycompatcollation = get_typcollation(anyc_actuals.anyarray_type);
949 : :
1997 tgl@sss.pgh.pa.us 950 [ + + + + ]:CBC 1291 : if (OidIsValid(anycollation) || OidIsValid(anycompatcollation))
951 : : {
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 : : */
5263 bruce@momjian.us 957 : 39 : Oid inputcollation = exprInputCollation(call_expr);
958 : :
5285 tgl@sss.pgh.pa.us 959 [ + + ]: 39 : if (OidIsValid(inputcollation))
960 : : {
1997 961 [ + - ]: 24 : if (OidIsValid(anycollation))
962 : 24 : anycollation = inputcollation;
963 [ - + ]: 24 : if (OidIsValid(anycompatcollation))
1997 tgl@sss.pgh.pa.us 964 :UBC 0 : anycompatcollation = inputcollation;
965 : : }
966 : : }
967 : :
968 : : /* And finally replace the tuple column types as needed */
7464 tgl@sss.pgh.pa.us 969 [ + + ]:CBC 3891 : for (i = 0; i < natts; i++)
970 : : {
2939 andres@anarazel.de 971 : 2600 : Form_pg_attribute att = TupleDescAttr(tupdesc, i);
972 : :
973 [ + + + + : 2600 : switch (att->atttypid)
+ + + -
+ ]
974 : : {
7464 tgl@sss.pgh.pa.us 975 : 1120 : case ANYELEMENTOID:
976 : : case ANYNONARRAYOID:
977 : : case ANYENUMOID:
7266 bruce@momjian.us 978 : 1120 : TupleDescInitEntry(tupdesc, i + 1,
2939 andres@anarazel.de 979 : 1120 : NameStr(att->attname),
980 : : poly_actuals.anyelement_type,
981 : : -1,
982 : : 0);
5285 tgl@sss.pgh.pa.us 983 : 1120 : TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
7464 984 : 1120 : break;
985 : 166 : case ANYARRAYOID:
7266 bruce@momjian.us 986 : 166 : TupleDescInitEntry(tupdesc, i + 1,
2939 andres@anarazel.de 987 : 166 : NameStr(att->attname),
988 : : poly_actuals.anyarray_type,
989 : : -1,
990 : : 0);
5285 tgl@sss.pgh.pa.us 991 : 166 : TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
7464 992 : 166 : break;
5056 heikki.linnakangas@i 993 : 36 : case ANYRANGEOID:
994 : 36 : TupleDescInitEntry(tupdesc, i + 1,
2939 andres@anarazel.de 995 : 36 : NameStr(att->attname),
996 : : poly_actuals.anyrange_type,
997 : : -1,
998 : : 0);
999 : : /* no collation should be attached to a range type */
5056 heikki.linnakangas@i 1000 : 36 : break;
1721 akorotkov@postgresql 1001 : 48 : case ANYMULTIRANGEOID:
1002 : 48 : TupleDescInitEntry(tupdesc, i + 1,
1003 : 48 : NameStr(att->attname),
1004 : : poly_actuals.anymultirange_type,
1005 : : -1,
1006 : : 0);
1007 : : /* no collation should be attached to a multirange type */
1008 : 48 : break;
1997 tgl@sss.pgh.pa.us 1009 : 63 : case ANYCOMPATIBLEOID:
1010 : : case ANYCOMPATIBLENONARRAYOID:
1011 : 63 : TupleDescInitEntry(tupdesc, i + 1,
1012 : 63 : NameStr(att->attname),
1013 : : anyc_actuals.anyelement_type,
1014 : : -1,
1015 : : 0);
1016 : 63 : TupleDescInitEntryCollation(tupdesc, i + 1, anycompatcollation);
1017 : 63 : break;
1018 : 123 : case ANYCOMPATIBLEARRAYOID:
1019 : 123 : TupleDescInitEntry(tupdesc, i + 1,
1020 : 123 : NameStr(att->attname),
1021 : : anyc_actuals.anyarray_type,
1022 : : -1,
1023 : : 0);
1024 : 123 : TupleDescInitEntryCollation(tupdesc, i + 1, anycompatcollation);
1025 : 123 : break;
1026 : 18 : case ANYCOMPATIBLERANGEOID:
1027 : 18 : TupleDescInitEntry(tupdesc, i + 1,
1028 : 18 : NameStr(att->attname),
1029 : : anyc_actuals.anyrange_type,
1030 : : -1,
1031 : : 0);
1032 : : /* no collation should be attached to a range type */
1033 : 18 : break;
1721 akorotkov@postgresql 1034 :UBC 0 : case ANYCOMPATIBLEMULTIRANGEOID:
1035 : 0 : TupleDescInitEntry(tupdesc, i + 1,
1036 : 0 : NameStr(att->attname),
1037 : : anyc_actuals.anymultirange_type,
1038 : : -1,
1039 : : 0);
1040 : : /* no collation should be attached to a multirange type */
1041 : 0 : break;
7464 tgl@sss.pgh.pa.us 1042 :CBC 1026 : default:
1043 : 1026 : break;
1044 : : }
1045 : : }
1046 : :
1047 : 1291 : return true;
1048 : : }
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
7459 1064 : 20019 : resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
1065 : : Node *call_expr)
1066 : : {
2002 1067 : 20019 : bool have_polymorphic_result = false;
7459 1068 : 20019 : bool have_anyelement_result = false;
1069 : 20019 : bool have_anyarray_result = false;
5056 heikki.linnakangas@i 1070 : 20019 : bool have_anyrange_result = false;
1721 akorotkov@postgresql 1071 : 20019 : bool have_anymultirange_result = false;
1997 tgl@sss.pgh.pa.us 1072 : 20019 : bool have_anycompatible_result = false;
1073 : 20019 : bool have_anycompatible_array_result = false;
1074 : 20019 : bool have_anycompatible_range_result = false;
1721 akorotkov@postgresql 1075 : 20019 : bool have_anycompatible_multirange_result = false;
1076 : : polymorphic_actuals poly_actuals;
1077 : : polymorphic_actuals anyc_actuals;
1078 : : int inargno;
1079 : : 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 : : */
2002 tgl@sss.pgh.pa.us 1086 : 20019 : memset(&poly_actuals, 0, sizeof(poly_actuals));
1997 1087 : 20019 : memset(&anyc_actuals, 0, sizeof(anyc_actuals));
7459 1088 : 20019 : inargno = 0;
1089 [ + + ]: 55696 : for (i = 0; i < numargs; i++)
1090 : : {
7266 bruce@momjian.us 1091 [ + + ]: 35677 : char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
1092 : :
7459 tgl@sss.pgh.pa.us 1093 [ + + + + : 35677 : switch (argtypes[i])
+ + + -
+ ]
1094 : : {
1095 : 936 : case ANYELEMENTOID:
1096 : : case ANYNONARRAYOID:
1097 : : case ANYENUMOID:
6259 1098 [ + + - + ]: 936 : if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
1099 : : {
2002 1100 : 3 : have_polymorphic_result = true;
7459 1101 : 3 : have_anyelement_result = true;
1102 : : }
1103 : : else
1104 : : {
2002 1105 [ + + ]: 933 : if (!OidIsValid(poly_actuals.anyelement_type))
1106 : : {
1107 : 622 : poly_actuals.anyelement_type =
1108 : 622 : get_call_expr_argtype(call_expr, inargno);
1109 [ - + ]: 622 : if (!OidIsValid(poly_actuals.anyelement_type))
7459 tgl@sss.pgh.pa.us 1110 :UBC 0 : return false;
1111 : : }
2002 tgl@sss.pgh.pa.us 1112 :CBC 933 : argtypes[i] = poly_actuals.anyelement_type;
1113 : : }
7459 1114 : 936 : break;
1115 : 277 : case ANYARRAYOID:
6259 1116 [ + + - + ]: 277 : if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
1117 : : {
2002 1118 : 9 : have_polymorphic_result = true;
7459 1119 : 9 : have_anyarray_result = true;
1120 : : }
1121 : : else
1122 : : {
2002 1123 [ + + ]: 268 : if (!OidIsValid(poly_actuals.anyarray_type))
1124 : : {
1125 : 262 : poly_actuals.anyarray_type =
1126 : 262 : get_call_expr_argtype(call_expr, inargno);
1127 [ - + ]: 262 : if (!OidIsValid(poly_actuals.anyarray_type))
7459 tgl@sss.pgh.pa.us 1128 :UBC 0 : return false;
1129 : : }
2002 tgl@sss.pgh.pa.us 1130 :CBC 268 : argtypes[i] = poly_actuals.anyarray_type;
1131 : : }
7459 1132 : 277 : break;
5056 heikki.linnakangas@i 1133 : 30 : case ANYRANGEOID:
1134 [ + - - + ]: 30 : if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
1135 : : {
2002 tgl@sss.pgh.pa.us 1136 :UBC 0 : have_polymorphic_result = true;
5056 heikki.linnakangas@i 1137 : 0 : have_anyrange_result = true;
1138 : : }
1139 : : else
1140 : : {
2002 tgl@sss.pgh.pa.us 1141 [ + - ]:CBC 30 : if (!OidIsValid(poly_actuals.anyrange_type))
1142 : : {
1143 : 30 : poly_actuals.anyrange_type =
1144 : 30 : get_call_expr_argtype(call_expr, inargno);
1145 [ - + ]: 30 : if (!OidIsValid(poly_actuals.anyrange_type))
5056 heikki.linnakangas@i 1146 :UBC 0 : return false;
1147 : : }
2002 tgl@sss.pgh.pa.us 1148 :CBC 30 : argtypes[i] = poly_actuals.anyrange_type;
1149 : : }
5056 heikki.linnakangas@i 1150 : 30 : break;
1721 akorotkov@postgresql 1151 : 15 : case ANYMULTIRANGEOID:
1152 [ + - - + ]: 15 : if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
1153 : : {
1721 akorotkov@postgresql 1154 :UBC 0 : have_polymorphic_result = true;
1155 : 0 : have_anymultirange_result = true;
1156 : : }
1157 : : else
1158 : : {
1721 akorotkov@postgresql 1159 [ + - ]:CBC 15 : if (!OidIsValid(poly_actuals.anymultirange_type))
1160 : : {
1161 : 15 : poly_actuals.anymultirange_type =
1162 : 15 : get_call_expr_argtype(call_expr, inargno);
1163 [ - + ]: 15 : if (!OidIsValid(poly_actuals.anymultirange_type))
1721 akorotkov@postgresql 1164 :UBC 0 : return false;
1165 : : }
1721 akorotkov@postgresql 1166 :CBC 15 : argtypes[i] = poly_actuals.anymultirange_type;
1167 : : }
1168 : 15 : break;
1997 tgl@sss.pgh.pa.us 1169 : 111 : case ANYCOMPATIBLEOID:
1170 : : case ANYCOMPATIBLENONARRAYOID:
1171 [ + + - + ]: 111 : if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
1172 : : {
1173 : 3 : have_polymorphic_result = true;
1174 : 3 : have_anycompatible_result = true;
1175 : : }
1176 : : else
1177 : : {
1178 [ + + ]: 108 : if (!OidIsValid(anyc_actuals.anyelement_type))
1179 : : {
1180 : 66 : anyc_actuals.anyelement_type =
1181 : 66 : get_call_expr_argtype(call_expr, inargno);
1182 [ - + ]: 66 : if (!OidIsValid(anyc_actuals.anyelement_type))
1997 tgl@sss.pgh.pa.us 1183 :UBC 0 : return false;
1184 : : }
1997 tgl@sss.pgh.pa.us 1185 :CBC 108 : argtypes[i] = anyc_actuals.anyelement_type;
1186 : : }
1187 : 111 : break;
1188 : 42 : case ANYCOMPATIBLEARRAYOID:
1189 [ + + - + ]: 42 : if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
1190 : : {
1191 : 9 : have_polymorphic_result = true;
1192 : 9 : have_anycompatible_array_result = true;
1193 : : }
1194 : : else
1195 : : {
1196 [ + - ]: 33 : if (!OidIsValid(anyc_actuals.anyarray_type))
1197 : : {
1198 : 33 : anyc_actuals.anyarray_type =
1199 : 33 : get_call_expr_argtype(call_expr, inargno);
1200 [ - + ]: 33 : if (!OidIsValid(anyc_actuals.anyarray_type))
1997 tgl@sss.pgh.pa.us 1201 :UBC 0 : return false;
1202 : : }
1997 tgl@sss.pgh.pa.us 1203 :CBC 33 : argtypes[i] = anyc_actuals.anyarray_type;
1204 : : }
1205 : 42 : break;
1206 : 36 : case ANYCOMPATIBLERANGEOID:
1207 [ + - - + ]: 36 : if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
1208 : : {
1997 tgl@sss.pgh.pa.us 1209 :UBC 0 : have_polymorphic_result = true;
1210 : 0 : have_anycompatible_range_result = true;
1211 : : }
1212 : : else
1213 : : {
1997 tgl@sss.pgh.pa.us 1214 [ + - ]:CBC 36 : if (!OidIsValid(anyc_actuals.anyrange_type))
1215 : : {
1216 : 36 : anyc_actuals.anyrange_type =
1217 : 36 : get_call_expr_argtype(call_expr, inargno);
1218 [ - + ]: 36 : if (!OidIsValid(anyc_actuals.anyrange_type))
1997 tgl@sss.pgh.pa.us 1219 :UBC 0 : return false;
1220 : : }
1997 tgl@sss.pgh.pa.us 1221 :CBC 36 : argtypes[i] = anyc_actuals.anyrange_type;
1222 : : }
1223 : 36 : break;
1721 akorotkov@postgresql 1224 :UBC 0 : case ANYCOMPATIBLEMULTIRANGEOID:
1225 [ # # # # ]: 0 : if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
1226 : : {
1227 : 0 : have_polymorphic_result = true;
1228 : 0 : have_anycompatible_multirange_result = true;
1229 : : }
1230 : : else
1231 : : {
1232 [ # # ]: 0 : if (!OidIsValid(anyc_actuals.anymultirange_type))
1233 : : {
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 : : }
1239 : 0 : argtypes[i] = anyc_actuals.anymultirange_type;
1240 : : }
1241 : 0 : break;
7459 tgl@sss.pgh.pa.us 1242 :CBC 34230 : default:
1243 : 34230 : break;
1244 : : }
6259 1245 [ + + + + ]: 35677 : if (argmode != PROARGMODE_OUT && argmode != PROARGMODE_TABLE)
7459 1246 : 35611 : inargno++;
1247 : : }
1248 : :
1249 : : /* Done? */
2002 1250 [ + + ]: 20019 : if (!have_polymorphic_result)
7459 1251 : 20007 : return true;
1252 : :
1253 : : /* If needed, deduce one polymorphic type from others */
2002 1254 [ + + - + ]: 12 : if (have_anyelement_result && !OidIsValid(poly_actuals.anyelement_type))
2002 tgl@sss.pgh.pa.us 1255 :UBC 0 : resolve_anyelement_from_others(&poly_actuals);
1256 : :
2002 tgl@sss.pgh.pa.us 1257 [ + + + + ]:CBC 12 : if (have_anyarray_result && !OidIsValid(poly_actuals.anyarray_type))
1258 : 3 : resolve_anyarray_from_others(&poly_actuals);
1259 : :
1260 [ - + - - ]: 12 : if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
2002 tgl@sss.pgh.pa.us 1261 :UBC 0 : resolve_anyrange_from_others(&poly_actuals);
1262 : :
1721 akorotkov@postgresql 1263 [ - + - - ]:CBC 12 : if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type))
1721 akorotkov@postgresql 1264 :UBC 0 : resolve_anymultirange_from_others(&poly_actuals);
1265 : :
1997 tgl@sss.pgh.pa.us 1266 [ + + + - ]:CBC 12 : if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
1267 : 3 : resolve_anyelement_from_others(&anyc_actuals);
1268 : :
1269 [ + + + - ]: 12 : if (have_anycompatible_array_result && !OidIsValid(anyc_actuals.anyarray_type))
1270 : 9 : resolve_anyarray_from_others(&anyc_actuals);
1271 : :
1272 [ - + - - ]: 12 : if (have_anycompatible_range_result && !OidIsValid(anyc_actuals.anyrange_type))
1997 tgl@sss.pgh.pa.us 1273 :UBC 0 : resolve_anyrange_from_others(&anyc_actuals);
1274 : :
1721 akorotkov@postgresql 1275 [ - + - - ]:CBC 12 : if (have_anycompatible_multirange_result && !OidIsValid(anyc_actuals.anymultirange_type))
1721 akorotkov@postgresql 1276 :UBC 0 : resolve_anymultirange_from_others(&anyc_actuals);
1277 : :
1278 : : /* And finally replace the output column types as needed */
7459 tgl@sss.pgh.pa.us 1279 [ + + ]:CBC 66 : for (i = 0; i < numargs; i++)
1280 : : {
1281 [ + + - - : 54 : switch (argtypes[i])
+ + - -
+ ]
1282 : : {
1283 : 3 : case ANYELEMENTOID:
1284 : : case ANYNONARRAYOID:
1285 : : case ANYENUMOID:
2002 1286 : 3 : argtypes[i] = poly_actuals.anyelement_type;
7459 1287 : 3 : break;
1288 : 9 : case ANYARRAYOID:
2002 1289 : 9 : argtypes[i] = poly_actuals.anyarray_type;
7459 1290 : 9 : break;
5056 heikki.linnakangas@i 1291 :UBC 0 : case ANYRANGEOID:
2002 tgl@sss.pgh.pa.us 1292 : 0 : argtypes[i] = poly_actuals.anyrange_type;
5056 heikki.linnakangas@i 1293 : 0 : break;
1721 akorotkov@postgresql 1294 : 0 : case ANYMULTIRANGEOID:
1295 : 0 : argtypes[i] = poly_actuals.anymultirange_type;
1296 : 0 : break;
1997 tgl@sss.pgh.pa.us 1297 :CBC 3 : case ANYCOMPATIBLEOID:
1298 : : case ANYCOMPATIBLENONARRAYOID:
1299 : 3 : argtypes[i] = anyc_actuals.anyelement_type;
1300 : 3 : break;
1301 : 9 : case ANYCOMPATIBLEARRAYOID:
1302 : 9 : argtypes[i] = anyc_actuals.anyarray_type;
1303 : 9 : break;
1997 tgl@sss.pgh.pa.us 1304 :UBC 0 : case ANYCOMPATIBLERANGEOID:
1305 : 0 : argtypes[i] = anyc_actuals.anyrange_type;
1306 : 0 : break;
1721 akorotkov@postgresql 1307 : 0 : case ANYCOMPATIBLEMULTIRANGEOID:
1308 : 0 : argtypes[i] = anyc_actuals.anymultirange_type;
1309 : 0 : break;
7459 tgl@sss.pgh.pa.us 1310 :CBC 30 : default:
1311 : 30 : break;
1312 : : }
1313 : : }
1314 : :
1315 : 12 : return true;
1316 : : }
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
2872 1328 : 69862 : get_type_func_class(Oid typid, Oid *base_typeid)
1329 : : {
1330 : 69862 : *base_typeid = typid;
1331 : :
7464 1332 [ + + + + : 69862 : switch (get_typtype(typid))
- ]
1333 : : {
6732 1334 : 7829 : case TYPTYPE_COMPOSITE:
7464 1335 : 7829 : return TYPEFUNC_COMPOSITE;
6732 1336 : 60077 : case TYPTYPE_BASE:
1337 : : case TYPTYPE_ENUM:
1338 : : case TYPTYPE_RANGE:
1339 : : case TYPTYPE_MULTIRANGE:
7464 1340 : 60077 : return TYPEFUNC_SCALAR;
2872 1341 : 427 : case TYPTYPE_DOMAIN:
1342 : 427 : *base_typeid = typid = getBaseType(typid);
1343 [ + + ]: 427 : if (get_typtype(typid) == TYPTYPE_COMPOSITE)
1344 : 142 : return TYPEFUNC_COMPOSITE_DOMAIN;
1345 : : else /* domain base type can't be a pseudotype */
1346 : 285 : return TYPEFUNC_SCALAR;
6732 1347 : 1529 : case TYPTYPE_PSEUDO:
7464 1348 [ + + ]: 1529 : if (typid == RECORDOID)
1349 : 845 : 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 [ + + - + ]: 684 : if (typid == VOIDOID || typid == CSTRINGOID)
1358 : 678 : return TYPEFUNC_SCALAR;
1359 : 6 : return TYPEFUNC_OTHER;
1360 : : }
1361 : : /* shouldn't get here, probably */
7464 tgl@sss.pgh.pa.us 1362 :UBC 0 : return TYPEFUNC_OTHER;
1363 : : }
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
7192 tgl@sss.pgh.pa.us 1379 :CBC 17912 : get_func_arg_info(HeapTuple procTup,
1380 : : Oid **p_argtypes, char ***p_argnames, char **p_argmodes)
1381 : : {
1382 : 17912 : Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
1383 : : Datum proallargtypes;
1384 : : Datum proargmodes;
1385 : : Datum proargnames;
1386 : : bool isNull;
1387 : : ArrayType *arr;
1388 : : int numargs;
1389 : : Datum *elems;
1390 : : int nelems;
1391 : : int i;
1392 : :
1393 : : /* First discover the total number of parameters and get their types */
1394 : 17912 : proallargtypes = SysCacheGetAttr(PROCOID, procTup,
1395 : : Anum_pg_proc_proallargtypes,
1396 : : &isNull);
1397 [ + + ]: 17912 : if (!isNull)
1398 : : {
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 : : */
2999 1405 : 4341 : arr = DatumGetArrayTypeP(proallargtypes); /* ensure not toasted */
7192 1406 : 4341 : numargs = ARR_DIMS(arr)[0];
1407 [ + - + - ]: 4341 : if (ARR_NDIM(arr) != 1 ||
1408 : 4341 : numargs < 0 ||
1409 [ + - ]: 4341 : ARR_HASNULL(arr) ||
1410 [ - + ]: 4341 : ARR_ELEMTYPE(arr) != OIDOID)
1748 alvherre@alvh.no-ip. 1411 [ # # ]:UBC 0 : elog(ERROR, "proallargtypes is not a 1-D Oid array or it contains nulls");
7192 tgl@sss.pgh.pa.us 1412 [ - + ]:CBC 4341 : Assert(numargs >= procStruct->pronargs);
1413 : 4341 : *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid));
1414 [ - + ]: 4341 : memcpy(*p_argtypes, ARR_DATA_PTR(arr),
1415 : : numargs * sizeof(Oid));
1416 : : }
1417 : : else
1418 : : {
1419 : : /* If no proallargtypes, use proargtypes */
1420 : 13571 : numargs = procStruct->proargtypes.dim1;
1421 [ - + ]: 13571 : Assert(numargs == procStruct->pronargs);
1422 : 13571 : *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid));
1423 : 13571 : memcpy(*p_argtypes, procStruct->proargtypes.values,
1424 : : numargs * sizeof(Oid));
1425 : : }
1426 : :
1427 : : /* Get argument names, if available */
1428 : 17912 : proargnames = SysCacheGetAttr(PROCOID, procTup,
1429 : : Anum_pg_proc_proargnames,
1430 : : &isNull);
1431 [ + + ]: 17912 : if (isNull)
1432 : 7062 : *p_argnames = NULL;
1433 : : else
1434 : : {
1163 peter@eisentraut.org 1435 : 10850 : deconstruct_array_builtin(DatumGetArrayTypeP(proargnames), TEXTOID,
1436 : : &elems, NULL, &nelems);
7192 tgl@sss.pgh.pa.us 1437 [ - + ]: 10850 : if (nelems != numargs) /* should not happen */
7192 tgl@sss.pgh.pa.us 1438 [ # # ]:UBC 0 : elog(ERROR, "proargnames must have the same number of elements as the function has arguments");
7192 tgl@sss.pgh.pa.us 1439 :CBC 10850 : *p_argnames = (char **) palloc(sizeof(char *) * numargs);
1440 [ + + ]: 64367 : for (i = 0; i < numargs; i++)
6374 1441 : 53517 : (*p_argnames)[i] = TextDatumGetCString(elems[i]);
1442 : : }
1443 : :
1444 : : /* Get argument modes, if available */
7192 1445 : 17912 : proargmodes = SysCacheGetAttr(PROCOID, procTup,
1446 : : Anum_pg_proc_proargmodes,
1447 : : &isNull);
1448 [ + + ]: 17912 : if (isNull)
1449 : 13571 : *p_argmodes = NULL;
1450 : : else
1451 : : {
1452 : 4341 : arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */
1453 [ + - ]: 4341 : if (ARR_NDIM(arr) != 1 ||
1454 [ + - ]: 4341 : ARR_DIMS(arr)[0] != numargs ||
1455 [ + - ]: 4341 : ARR_HASNULL(arr) ||
1456 [ - + ]: 4341 : ARR_ELEMTYPE(arr) != CHAROID)
1748 alvherre@alvh.no-ip. 1457 [ # # ]:UBC 0 : elog(ERROR, "proargmodes is not a 1-D char array of length %d or it contains nulls",
1458 : : numargs);
7192 tgl@sss.pgh.pa.us 1459 :CBC 4341 : *p_argmodes = (char *) palloc(numargs * sizeof(char));
1460 [ - + ]: 4341 : memcpy(*p_argmodes, ARR_DATA_PTR(arr),
1461 : : numargs * sizeof(char));
1462 : : }
1463 : :
1464 : 17912 : return numargs;
1465 : : }
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
3786 peter_e@gmx.net 1475 : 83 : get_func_trftypes(HeapTuple procTup,
1476 : : Oid **p_trftypes)
1477 : : {
1478 : : Datum protrftypes;
1479 : : ArrayType *arr;
1480 : : int nelems;
1481 : : bool isNull;
1482 : :
1483 : 83 : protrftypes = SysCacheGetAttr(PROCOID, procTup,
1484 : : Anum_pg_proc_protrftypes,
1485 : : &isNull);
1486 [ + + ]: 83 : if (!isNull)
1487 : : {
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 : : */
3759 bruce@momjian.us 1494 : 3 : arr = DatumGetArrayTypeP(protrftypes); /* ensure not toasted */
3786 peter_e@gmx.net 1495 : 3 : nelems = ARR_DIMS(arr)[0];
1496 [ + - + - ]: 3 : if (ARR_NDIM(arr) != 1 ||
1497 : 3 : nelems < 0 ||
1498 [ + - ]: 3 : ARR_HASNULL(arr) ||
1499 [ - + ]: 3 : ARR_ELEMTYPE(arr) != OIDOID)
1748 alvherre@alvh.no-ip. 1500 [ # # ]:UBC 0 : elog(ERROR, "protrftypes is not a 1-D Oid array or it contains nulls");
3786 peter_e@gmx.net 1501 :CBC 3 : *p_trftypes = (Oid *) palloc(nelems * sizeof(Oid));
1502 [ - + ]: 3 : memcpy(*p_trftypes, ARR_DATA_PTR(arr),
1503 : : nelems * sizeof(Oid));
1504 : :
1505 : 3 : return nelems;
1506 : : }
1507 : : else
1508 : 80 : return 0;
1509 : : }
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
1549 tgl@sss.pgh.pa.us 1522 : 4381 : get_func_input_arg_names(Datum proargnames, Datum proargmodes,
1523 : : char ***arg_names)
1524 : : {
1525 : : ArrayType *arr;
1526 : : int numargs;
1527 : : Datum *argnames;
1528 : : char *argmodes;
1529 : : char **inargnames;
1530 : : int numinargs;
1531 : : int i;
1532 : :
1533 : : /* Do nothing if null proargnames */
5812 1534 [ + + ]: 4381 : if (proargnames == PointerGetDatum(NULL))
1535 : : {
1536 : 1924 : *arg_names = NULL;
1537 : 1924 : return 0;
1538 : : }
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 : : */
2999 1545 : 2457 : arr = DatumGetArrayTypeP(proargnames); /* ensure not toasted */
5812 1546 [ + - ]: 2457 : if (ARR_NDIM(arr) != 1 ||
1547 [ + - ]: 2457 : ARR_HASNULL(arr) ||
1548 [ - + ]: 2457 : ARR_ELEMTYPE(arr) != TEXTOID)
1748 alvherre@alvh.no-ip. 1549 [ # # ]:UBC 0 : elog(ERROR, "proargnames is not a 1-D text array or it contains nulls");
1163 peter@eisentraut.org 1550 :CBC 2457 : deconstruct_array_builtin(arr, TEXTOID, &argnames, NULL, &numargs);
5812 tgl@sss.pgh.pa.us 1551 [ + + ]: 2457 : if (proargmodes != PointerGetDatum(NULL))
1552 : : {
1553 : 1226 : arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */
1554 [ + - ]: 1226 : if (ARR_NDIM(arr) != 1 ||
1555 [ + - ]: 1226 : ARR_DIMS(arr)[0] != numargs ||
1556 [ + - ]: 1226 : ARR_HASNULL(arr) ||
1557 [ - + ]: 1226 : ARR_ELEMTYPE(arr) != CHAROID)
1748 alvherre@alvh.no-ip. 1558 [ # # ]:UBC 0 : elog(ERROR, "proargmodes is not a 1-D char array of length %d or it contains nulls",
1559 : : numargs);
5812 tgl@sss.pgh.pa.us 1560 [ - + ]:CBC 1226 : argmodes = (char *) ARR_DATA_PTR(arr);
1561 : : }
1562 : : else
1563 : 1231 : argmodes = NULL;
1564 : :
1565 : : /* zero elements probably shouldn't happen, but handle it gracefully */
1566 [ - + ]: 2457 : if (numargs <= 0)
1567 : : {
5812 tgl@sss.pgh.pa.us 1568 :UBC 0 : *arg_names = NULL;
1569 : 0 : return 0;
1570 : : }
1571 : :
1572 : : /* extract input-argument names */
5812 tgl@sss.pgh.pa.us 1573 :CBC 2457 : inargnames = (char **) palloc(numargs * sizeof(char *));
1574 : 2457 : numinargs = 0;
1575 [ + + ]: 12194 : for (i = 0; i < numargs; i++)
1576 : : {
1577 [ + + ]: 9737 : if (argmodes == NULL ||
1578 [ + + ]: 7068 : argmodes[i] == PROARGMODE_IN ||
1579 [ + + ]: 4339 : argmodes[i] == PROARGMODE_INOUT ||
1580 [ + + ]: 4276 : argmodes[i] == PROARGMODE_VARIADIC)
1581 : : {
1582 : 5884 : char *pname = TextDatumGetCString(argnames[i]);
1583 : :
1584 [ + + ]: 5884 : if (pname[0] != '\0')
1585 : 5842 : inargnames[numinargs] = pname;
1586 : : else
1587 : 42 : inargnames[numinargs] = NULL;
1588 : 5884 : numinargs++;
1589 : : }
1590 : : }
1591 : :
1592 : 2457 : *arg_names = inargnames;
1593 : 2457 : return numinargs;
1594 : : }
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 *
7275 1607 : 11861 : get_func_result_name(Oid functionId)
1608 : : {
1609 : : char *result;
1610 : : HeapTuple procTuple;
1611 : : Datum proargmodes;
1612 : : Datum proargnames;
1613 : : ArrayType *arr;
1614 : : int numargs;
1615 : : char *argmodes;
1616 : : Datum *argnames;
1617 : : int numoutargs;
1618 : : int nargnames;
1619 : : int i;
1620 : :
1621 : : /* First fetch the function's pg_proc row */
5683 rhaas@postgresql.org 1622 : 11861 : procTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionId));
7275 tgl@sss.pgh.pa.us 1623 [ - + ]: 11861 : if (!HeapTupleIsValid(procTuple))
7275 tgl@sss.pgh.pa.us 1624 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for function %u", functionId);
1625 : :
1626 : : /* If there are no named OUT parameters, return NULL */
2719 andrew@dunslane.net 1627 [ + + + + ]:CBC 14230 : if (heap_attisnull(procTuple, Anum_pg_proc_proargmodes, NULL) ||
1628 : 2369 : heap_attisnull(procTuple, Anum_pg_proc_proargnames, NULL))
7275 tgl@sss.pgh.pa.us 1629 : 9507 : result = NULL;
1630 : : else
1631 : : {
1632 : : /* Get the data out of the tuple */
896 dgustafsson@postgres 1633 : 2354 : proargmodes = SysCacheGetAttrNotNull(PROCOID, procTuple,
1634 : : Anum_pg_proc_proargmodes);
1635 : 2354 : proargnames = SysCacheGetAttrNotNull(PROCOID, procTuple,
1636 : : 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 : : */
7266 bruce@momjian.us 1644 : 2354 : arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */
7275 tgl@sss.pgh.pa.us 1645 : 2354 : numargs = ARR_DIMS(arr)[0];
1646 [ + - + - ]: 2354 : if (ARR_NDIM(arr) != 1 ||
1647 : 2354 : numargs < 0 ||
7233 1648 [ + - ]: 2354 : ARR_HASNULL(arr) ||
7275 1649 [ - + ]: 2354 : ARR_ELEMTYPE(arr) != CHAROID)
1748 alvherre@alvh.no-ip. 1650 [ # # ]:UBC 0 : elog(ERROR, "proargmodes is not a 1-D char array or it contains nulls");
7275 tgl@sss.pgh.pa.us 1651 [ - + ]:CBC 2354 : argmodes = (char *) ARR_DATA_PTR(arr);
7266 bruce@momjian.us 1652 : 2354 : arr = DatumGetArrayTypeP(proargnames); /* ensure not toasted */
7275 tgl@sss.pgh.pa.us 1653 [ + - ]: 2354 : if (ARR_NDIM(arr) != 1 ||
1654 [ + - ]: 2354 : ARR_DIMS(arr)[0] != numargs ||
7233 1655 [ + - ]: 2354 : ARR_HASNULL(arr) ||
7275 1656 [ - + ]: 2354 : ARR_ELEMTYPE(arr) != TEXTOID)
1748 alvherre@alvh.no-ip. 1657 [ # # ]:UBC 0 : elog(ERROR, "proargnames is not a 1-D text array of length %d or it contains nulls",
1658 : : numargs);
1163 peter@eisentraut.org 1659 :CBC 2354 : deconstruct_array_builtin(arr, TEXTOID, &argnames, NULL, &nargnames);
7275 tgl@sss.pgh.pa.us 1660 [ - + ]: 2354 : Assert(nargnames == numargs);
1661 : :
1662 : : /* scan for output argument(s) */
1663 : 2354 : result = NULL;
1664 : 2354 : numoutargs = 0;
1665 [ + + ]: 5343 : for (i = 0; i < numargs; i++)
1666 : : {
6261 1667 [ + + ]: 2989 : if (argmodes[i] == PROARGMODE_IN ||
1668 [ + + ]: 2354 : argmodes[i] == PROARGMODE_VARIADIC)
7275 1669 : 2344 : continue;
1670 [ + + + + : 645 : Assert(argmodes[i] == PROARGMODE_OUT ||
- + ]
1671 : : argmodes[i] == PROARGMODE_INOUT ||
1672 : : argmodes[i] == PROARGMODE_TABLE);
1673 [ - + ]: 645 : if (++numoutargs > 1)
1674 : : {
1675 : : /* multiple out args, so forget it */
7275 tgl@sss.pgh.pa.us 1676 :UBC 0 : result = NULL;
1677 : 0 : break;
1678 : : }
6374 tgl@sss.pgh.pa.us 1679 :CBC 645 : result = TextDatumGetCString(argnames[i]);
7275 1680 [ + - - + ]: 645 : if (result == NULL || result[0] == '\0')
1681 : : {
1682 : : /* Parameter is not named, so forget it */
7275 tgl@sss.pgh.pa.us 1683 :UBC 0 : result = NULL;
1684 : 0 : break;
1685 : : }
1686 : : }
1687 : : }
1688 : :
7275 tgl@sss.pgh.pa.us 1689 :CBC 11861 : ReleaseSysCache(procTuple);
1690 : :
1691 : 11861 : return result;
1692 : : }
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
7464 1705 : 224235 : build_function_result_tupdesc_t(HeapTuple procTuple)
1706 : : {
1707 : 224235 : Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(procTuple);
1708 : : Datum proallargtypes;
1709 : : Datum proargmodes;
1710 : : Datum proargnames;
1711 : : bool isnull;
1712 : :
1713 : : /* Return NULL if the function isn't declared to return RECORD */
1714 [ + + ]: 224235 : if (procform->prorettype != RECORDOID)
1715 : 63909 : return NULL;
1716 : :
1717 : : /* If there are no OUT parameters, return NULL */
2719 andrew@dunslane.net 1718 [ + + - + ]: 319919 : if (heap_attisnull(procTuple, Anum_pg_proc_proallargtypes, NULL) ||
1719 : 159593 : heap_attisnull(procTuple, Anum_pg_proc_proargmodes, NULL))
7464 tgl@sss.pgh.pa.us 1720 : 733 : return NULL;
1721 : :
1722 : : /* Get the data out of the tuple */
896 dgustafsson@postgres 1723 : 159593 : proallargtypes = SysCacheGetAttrNotNull(PROCOID, procTuple,
1724 : : Anum_pg_proc_proallargtypes);
1725 : 159593 : proargmodes = SysCacheGetAttrNotNull(PROCOID, procTuple,
1726 : : Anum_pg_proc_proargmodes);
7464 tgl@sss.pgh.pa.us 1727 : 159593 : proargnames = SysCacheGetAttr(PROCOID, procTuple,
1728 : : Anum_pg_proc_proargnames,
1729 : : &isnull);
1730 [ + + ]: 159593 : if (isnull)
7266 bruce@momjian.us 1731 : 62 : proargnames = PointerGetDatum(NULL); /* just to be sure */
1732 : :
2733 peter_e@gmx.net 1733 : 159593 : return build_function_result_tupdesc_d(procform->prokind,
1734 : : proallargtypes,
1735 : : proargmodes,
1736 : : proargnames);
1737 : : }
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 : 160072 : build_function_result_tupdesc_d(char prokind,
1752 : : Datum proallargtypes,
1753 : : Datum proargmodes,
1754 : : Datum proargnames)
1755 : : {
1756 : : TupleDesc desc;
1757 : : ArrayType *arr;
1758 : : int numargs;
1759 : : Oid *argtypes;
1760 : : char *argmodes;
7464 tgl@sss.pgh.pa.us 1761 : 160072 : Datum *argnames = NULL;
1762 : : Oid *outargtypes;
1763 : : char **outargnames;
1764 : : int numoutargs;
1765 : : int nargnames;
1766 : : int i;
1767 : :
1768 : : /* Can't have output args if columns are null */
1769 [ + + - + ]: 320132 : if (proallargtypes == PointerGetDatum(NULL) ||
1770 : 160060 : proargmodes == PointerGetDatum(NULL))
1771 : 12 : 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 : 160060 : arr = DatumGetArrayTypeP(proallargtypes); /* ensure not toasted */
1779 : 160060 : numargs = ARR_DIMS(arr)[0];
1780 [ + - + - ]: 160060 : if (ARR_NDIM(arr) != 1 ||
1781 : 160060 : numargs < 0 ||
7233 1782 [ + - ]: 160060 : ARR_HASNULL(arr) ||
7464 1783 [ - + ]: 160060 : ARR_ELEMTYPE(arr) != OIDOID)
1748 alvherre@alvh.no-ip. 1784 [ # # ]:UBC 0 : elog(ERROR, "proallargtypes is not a 1-D Oid array or it contains nulls");
7464 tgl@sss.pgh.pa.us 1785 [ - + ]:CBC 160060 : argtypes = (Oid *) ARR_DATA_PTR(arr);
2999 1786 : 160060 : arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */
7464 1787 [ + - ]: 160060 : if (ARR_NDIM(arr) != 1 ||
1788 [ + - ]: 160060 : ARR_DIMS(arr)[0] != numargs ||
7233 1789 [ + - ]: 160060 : ARR_HASNULL(arr) ||
7464 1790 [ - + ]: 160060 : ARR_ELEMTYPE(arr) != CHAROID)
1748 alvherre@alvh.no-ip. 1791 [ # # ]:UBC 0 : elog(ERROR, "proargmodes is not a 1-D char array of length %d or it contains nulls",
1792 : : numargs);
7464 tgl@sss.pgh.pa.us 1793 [ - + ]:CBC 160060 : argmodes = (char *) ARR_DATA_PTR(arr);
1794 [ + + ]: 160060 : if (proargnames != PointerGetDatum(NULL))
1795 : : {
1796 : 159992 : arr = DatumGetArrayTypeP(proargnames); /* ensure not toasted */
1797 [ + - ]: 159992 : if (ARR_NDIM(arr) != 1 ||
1798 [ + - ]: 159992 : ARR_DIMS(arr)[0] != numargs ||
7233 1799 [ + - ]: 159992 : ARR_HASNULL(arr) ||
7464 1800 [ - + ]: 159992 : ARR_ELEMTYPE(arr) != TEXTOID)
1748 alvherre@alvh.no-ip. 1801 [ # # ]:UBC 0 : elog(ERROR, "proargnames is not a 1-D text array of length %d or it contains nulls",
1802 : : numargs);
1163 peter@eisentraut.org 1803 :CBC 159992 : deconstruct_array_builtin(arr, TEXTOID, &argnames, NULL, &nargnames);
7464 tgl@sss.pgh.pa.us 1804 [ - + ]: 159992 : Assert(nargnames == numargs);
1805 : : }
1806 : :
1807 : : /* zero elements probably shouldn't happen, but handle it gracefully */
1808 [ - + ]: 160060 : if (numargs <= 0)
7464 tgl@sss.pgh.pa.us 1809 :UBC 0 : return NULL;
1810 : :
1811 : : /* extract output-argument types and names */
7464 tgl@sss.pgh.pa.us 1812 :CBC 160060 : outargtypes = (Oid *) palloc(numargs * sizeof(Oid));
1813 : 160060 : outargnames = (char **) palloc(numargs * sizeof(char *));
1814 : 160060 : numoutargs = 0;
1815 [ + + ]: 3009117 : for (i = 0; i < numargs; i++)
1816 : : {
1817 : : char *pname;
1818 : :
6261 1819 [ + + ]: 2849057 : if (argmodes[i] == PROARGMODE_IN ||
1820 [ + + ]: 2617800 : argmodes[i] == PROARGMODE_VARIADIC)
7464 1821 : 234127 : continue;
1822 [ + + + + : 2614930 : Assert(argmodes[i] == PROARGMODE_OUT ||
- + ]
1823 : : argmodes[i] == PROARGMODE_INOUT ||
1824 : : argmodes[i] == PROARGMODE_TABLE);
1825 : 2614930 : outargtypes[numoutargs] = argtypes[i];
1826 [ + + ]: 2614930 : if (argnames)
6374 1827 : 2614794 : pname = TextDatumGetCString(argnames[i]);
1828 : : else
7464 1829 : 136 : pname = NULL;
1830 [ + + + + ]: 2614930 : if (pname == NULL || pname[0] == '\0')
1831 : : {
1832 : : /* Parameter is not named, so gin up a column name */
4261 peter_e@gmx.net 1833 : 296 : pname = psprintf("column%d", numoutargs + 1);
1834 : : }
7464 tgl@sss.pgh.pa.us 1835 : 2614930 : outargnames[numoutargs] = pname;
1836 : 2614930 : numoutargs++;
1837 : : }
1838 : :
1839 : : /*
1840 : : * If there is no output argument, or only one, the function does not
1841 : : * return tuples.
1842 : : */
2733 peter_e@gmx.net 1843 [ + + - + ]: 160060 : if (numoutargs < 2 && prokind != PROKIND_PROCEDURE)
7464 tgl@sss.pgh.pa.us 1844 :UBC 0 : return NULL;
1845 : :
2482 andres@anarazel.de 1846 :CBC 160060 : desc = CreateTemplateTupleDesc(numoutargs);
7464 tgl@sss.pgh.pa.us 1847 [ + + ]: 2774990 : for (i = 0; i < numoutargs; i++)
1848 : : {
7266 bruce@momjian.us 1849 : 2614930 : TupleDescInitEntry(desc, i + 1,
7464 tgl@sss.pgh.pa.us 1850 : 2614930 : outargnames[i],
1851 : 2614930 : outargtypes[i],
1852 : : -1,
1853 : : 0);
1854 : : }
1855 : :
1856 : 160060 : return desc;
1857 : : }
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
7464 tgl@sss.pgh.pa.us 1870 :UBC 0 : RelationNameGetTupleDesc(const char *relname)
1871 : : {
1872 : : RangeVar *relvar;
1873 : : Relation rel;
1874 : : TupleDesc tupdesc;
1875 : : List *relname_list;
1876 : :
1877 : : /* Open relation and copy the tuple description */
984 1878 : 0 : relname_list = stringToQualifiedNameList(relname, NULL);
7464 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 : : }
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 : 0 : TypeGetTupleDesc(Oid typeoid, List *colaliases)
1904 : : {
1905 : : Oid base_typeoid;
2872 1906 : 0 : TypeFuncClass functypclass = get_type_func_class(typeoid, &base_typeoid);
7464 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 : : {
1917 : : /* Composite data type, e.g. a table's row type */
2872 1918 : 0 : tupdesc = lookup_rowtype_tupdesc_copy(base_typeoid, -1);
1919 : :
7464 1920 [ # # ]: 0 : if (colaliases != NIL)
1921 : : {
1922 : 0 : int natts = tupdesc->natts;
1923 : : 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 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
1929 : : 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 : : {
1934 : 0 : char *label = strVal(list_nth(colaliases, varattno));
2939 andres@anarazel.de 1935 : 0 : Form_pg_attribute attr = TupleDescAttr(tupdesc, varattno);
1936 : :
7464 tgl@sss.pgh.pa.us 1937 [ # # ]: 0 : if (label != NULL)
2939 andres@anarazel.de 1938 : 0 : namestrcpy(&(attr->attname), label);
1939 : : }
1940 : :
1941 : : /* The tuple type is now an anonymous record type */
7464 tgl@sss.pgh.pa.us 1942 : 0 : tupdesc->tdtypeid = RECORDOID;
1943 : 0 : tupdesc->tdtypmod = -1;
1944 : : }
1945 : : }
1946 [ # # ]: 0 : else if (functypclass == TYPEFUNC_SCALAR)
1947 : : {
1948 : : /* Base data type, i.e. scalar */
1949 : : char *attname;
1950 : :
1951 : : /* the alias list is required for base types */
1952 [ # # ]: 0 : if (colaliases == NIL)
1953 [ # # ]: 0 : ereport(ERROR,
1954 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
1955 : : 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 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
1961 : : errmsg("number of aliases does not match number of columns")));
1962 : :
1963 : : /* OK, get the column alias */
1964 : 0 : attname = strVal(linitial(colaliases));
1965 : :
2482 andres@anarazel.de 1966 : 0 : tupdesc = CreateTemplateTupleDesc(1);
7464 tgl@sss.pgh.pa.us 1967 : 0 : TupleDescInitEntry(tupdesc,
1968 : : (AttrNumber) 1,
1969 : : attname,
1970 : : typeoid,
1971 : : -1,
1972 : : 0);
1973 : : }
1974 [ # # ]: 0 : else if (functypclass == TYPEFUNC_RECORD)
1975 : : {
1976 : : /* XXX can't support this because typmod wasn't passed in ... */
1977 [ # # ]: 0 : ereport(ERROR,
1978 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
1979 : : errmsg("could not determine row description for function returning record")));
1980 : : }
1981 : : else
1982 : : {
1983 : : /* crummy error message, but parser should have caught this */
1984 [ # # ]: 0 : elog(ERROR, "function in FROM has unsupported return type");
1985 : : }
1986 : :
1987 : 0 : return tupdesc;
1988 : : }
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
2873 andrew@dunslane.net 2005 :CBC 2237 : extract_variadic_args(FunctionCallInfo fcinfo, int variadic_start,
2006 : : bool convert_unknown, Datum **args, Oid **types,
2007 : : bool **nulls)
2008 : : {
2009 : 2237 : bool variadic = get_fn_expr_variadic(fcinfo->flinfo);
2010 : : Datum *args_res;
2011 : : bool *nulls_res;
2012 : : Oid *types_res;
2013 : : int nargs,
2014 : : i;
2015 : :
2016 : 2237 : *args = NULL;
2017 : 2237 : *types = NULL;
2018 : 2237 : *nulls = NULL;
2019 : :
2020 [ + + ]: 2237 : if (variadic)
2021 : : {
2022 : : ArrayType *array_in;
2023 : : Oid element_type;
2024 : : bool typbyval;
2025 : : char typalign;
2026 : : int16 typlen;
2027 : :
2028 [ - + ]: 90 : Assert(PG_NARGS() == variadic_start + 1);
2029 : :
2030 [ + + ]: 90 : if (PG_ARGISNULL(variadic_start))
2031 : 12 : return -1;
2032 : :
2033 : 78 : array_in = PG_GETARG_ARRAYTYPE_P(variadic_start);
2034 : 78 : element_type = ARR_ELEMTYPE(array_in);
2035 : :
2036 : 78 : get_typlenbyvalalign(element_type,
2037 : : &typlen, &typbyval, &typalign);
2038 : 78 : deconstruct_array(array_in, element_type, typlen, typbyval,
2039 : : typalign, &args_res, &nulls_res,
2040 : : &nargs);
2041 : :
2042 : : /* All the elements of the array have the same type */
2043 : 78 : types_res = (Oid *) palloc0(nargs * sizeof(Oid));
2044 [ + + ]: 318 : for (i = 0; i < nargs; i++)
2045 : 240 : types_res[i] = element_type;
2046 : : }
2047 : : else
2048 : : {
2049 : 2147 : nargs = PG_NARGS() - variadic_start;
2872 tgl@sss.pgh.pa.us 2050 [ - + ]: 2147 : Assert(nargs > 0);
2873 andrew@dunslane.net 2051 : 2147 : nulls_res = (bool *) palloc0(nargs * sizeof(bool));
2052 : 2147 : args_res = (Datum *) palloc0(nargs * sizeof(Datum));
2053 : 2147 : types_res = (Oid *) palloc0(nargs * sizeof(Oid));
2054 : :
2055 [ + + ]: 33934 : for (i = 0; i < nargs; i++)
2056 : : {
2057 : 31787 : nulls_res[i] = PG_ARGISNULL(i + variadic_start);
2058 : 31787 : types_res[i] = get_fn_expr_argtype(fcinfo->flinfo,
2059 : : 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 [ + - ]: 31787 : if (convert_unknown &&
2069 [ + + + - ]: 52176 : types_res[i] == UNKNOWNOID &&
2070 : 20389 : get_fn_expr_arg_stable(fcinfo->flinfo, i + variadic_start))
2071 : : {
2072 : 20389 : types_res[i] = TEXTOID;
2073 : :
2074 [ + + ]: 20389 : if (PG_ARGISNULL(i + variadic_start))
2075 : 45 : args_res[i] = (Datum) 0;
2076 : : else
2077 : 20344 : args_res[i] =
2078 : 20344 : CStringGetTextDatum(PG_GETARG_POINTER(i + variadic_start));
2079 : : }
2080 : : else
2081 : : {
2082 : : /* no conversion needed, just take the datum as given */
2083 : 11398 : args_res[i] = PG_GETARG_DATUM(i + variadic_start);
2084 : : }
2085 : :
2086 [ + - + - ]: 31787 : if (!OidIsValid(types_res[i]) ||
2087 [ - + ]: 31787 : (convert_unknown && types_res[i] == UNKNOWNOID))
2873 andrew@dunslane.net 2088 [ # # ]:UBC 0 : ereport(ERROR,
2089 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2090 : : errmsg("could not determine data type for argument %d",
2091 : : i + 1)));
2092 : : }
2093 : : }
2094 : :
2095 : : /* Fill in results */
2873 andrew@dunslane.net 2096 :CBC 2225 : *args = args_res;
2097 : 2225 : *nulls = nulls_res;
2098 : 2225 : *types = types_res;
2099 : :
2100 : 2225 : return nargs;
2101 : : }
|