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-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
36 nathan@postgresql.or 76 :GNC 16779 : InitMaterializedSRF(FunctionCallInfo fcinfo, uint32 flags)
77 : : {
78 : : bool random_access;
1520 michael@paquier.xyz 79 :CBC 16779 : 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 [ + - - + ]: 16779 : if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
1520 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")));
1520 michael@paquier.xyz 90 [ + - ]:CBC 16779 : if (!(rsinfo->allowedModes & SFRM_Materialize) ||
1295 91 [ + + - + ]: 16779 : ((flags & MAT_SRF_USE_EXPECTED_DESC) != 0 && rsinfo->expectedDesc == NULL))
1520 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 : : */
1520 michael@paquier.xyz 100 :CBC 16779 : per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
101 : 16779 : old_context = MemoryContextSwitchTo(per_query_ctx);
102 : :
103 : : /* build a tuple descriptor for our result type */
1295 104 [ + + ]: 16779 : if ((flags & MAT_SRF_USE_EXPECTED_DESC) != 0)
1520 105 : 1173 : stored_tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
106 : : else
107 : : {
108 [ - + ]: 15606 : if (get_call_result_type(fcinfo, NULL, &stored_tupdesc) != TYPEFUNC_COMPOSITE)
1520 michael@paquier.xyz 109 [ # # ]:UBC 0 : elog(ERROR, "return type must be a row type");
110 : : }
111 : :
112 : : /* If requested, bless the tuple descriptor */
1295 michael@paquier.xyz 113 [ + + ]:CBC 16779 : if ((flags & MAT_SRF_BLESS) != 0)
1520 114 : 8440 : BlessTupleDesc(stored_tupdesc);
115 : :
116 : 16779 : random_access = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
117 : :
118 : 16779 : tupstore = tuplestore_begin_heap(random_access, false, work_mem);
119 : 16779 : rsinfo->returnMode = SFRM_Materialize;
120 : 16779 : rsinfo->setResult = tupstore;
121 : 16779 : rsinfo->setDesc = stored_tupdesc;
122 : 16779 : MemoryContextSwitchTo(old_context);
123 : 16779 : }
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 *
8720 bruce@momjian.us 133 : 176162 : init_MultiFuncCall(PG_FUNCTION_ARGS)
134 : : {
135 : : FuncCallContext *retval;
136 : :
137 : : /*
138 : : * Bail if we're called in the wrong context
139 : : */
140 [ + - - + ]: 176162 : if (fcinfo->resultinfo == NULL || !IsA(fcinfo->resultinfo, ReturnSetInfo))
8320 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 : :
8720 bruce@momjian.us 145 [ + - ]:CBC 176162 : if (fcinfo->flinfo->fn_extra == NULL)
146 : : {
147 : : /*
148 : : * First call
149 : : */
6172 150 : 176162 : 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 : : */
6640 neilc@samurai.com 156 : 176162 : 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 : 176162 : MemoryContextAllocZero(multi_call_ctx,
165 : : sizeof(FuncCallContext));
166 : :
167 : : /*
168 : : * initialize the elements
169 : : */
8720 bruce@momjian.us 170 : 176162 : retval->call_cntr = 0;
171 : 176162 : retval->max_calls = 0;
8692 172 : 176162 : retval->user_fctx = NULL;
8720 173 : 176162 : retval->attinmeta = NULL;
8069 tgl@sss.pgh.pa.us 174 : 176162 : retval->tuple_desc = NULL;
6640 neilc@samurai.com 175 : 176162 : retval->multi_call_memory_ctx = multi_call_ctx;
176 : :
177 : : /*
178 : : * save the pointer for cross-call use
179 : : */
8720 bruce@momjian.us 180 : 176162 : 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 : : */
8173 mail@joeconway.com 186 : 176162 : RegisterExprContextCallback(rsi->econtext,
187 : : shutdown_MultiFuncCall,
188 : 176162 : PointerGetDatum(fcinfo->flinfo));
189 : : }
190 : : else
191 : : {
192 : : /* second and subsequent calls */
7033 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 : :
8720 bruce@momjian.us 199 :CBC 176162 : return retval;
200 : : }
201 : :
202 : : /*
203 : : * per_MultiFuncCall
204 : : *
205 : : * Do Multi-function per-call setup
206 : : */
207 : : FuncCallContext *
8692 208 : 14112683 : per_MultiFuncCall(PG_FUNCTION_ARGS)
209 : : {
210 : 14112683 : FuncCallContext *retval = (FuncCallContext *) fcinfo->flinfo->fn_extra;
211 : :
212 : 14112683 : return retval;
213 : : }
214 : :
215 : : /*
216 : : * end_MultiFuncCall
217 : : * Clean up after init_MultiFuncCall
218 : : */
219 : : void
8720 220 : 174848 : end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx)
221 : : {
7919 222 : 174848 : ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
223 : :
224 : : /* Deregister the shutdown callback */
8173 mail@joeconway.com 225 : 174848 : UnregisterExprContextCallback(rsi->econtext,
226 : : shutdown_MultiFuncCall,
227 : 174848 : PointerGetDatum(fcinfo->flinfo));
228 : :
229 : : /* But use it to do the real work */
230 : 174848 : shutdown_MultiFuncCall(PointerGetDatum(fcinfo->flinfo));
231 : 174848 : }
232 : :
233 : : /*
234 : : * shutdown_MultiFuncCall
235 : : * Shutdown function to clean up after init_MultiFuncCall
236 : : */
237 : : static void
238 : 174891 : shutdown_MultiFuncCall(Datum arg)
239 : : {
7919 bruce@momjian.us 240 : 174891 : FmgrInfo *flinfo = (FmgrInfo *) DatumGetPointer(arg);
8173 mail@joeconway.com 241 : 174891 : FuncCallContext *funcctx = (FuncCallContext *) flinfo->fn_extra;
242 : :
243 : : /* unbind from flinfo */
244 : 174891 : flinfo->fn_extra = NULL;
245 : :
246 : : /*
247 : : * Delete context that holds all multi-call data, including the
248 : : * FuncCallContext itself
249 : : */
6640 neilc@samurai.com 250 : 174891 : MemoryContextDelete(funcctx->multi_call_memory_ctx);
8720 bruce@momjian.us 251 : 174891 : }
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
7705 tgl@sss.pgh.pa.us 276 : 63368 : get_call_result_type(FunctionCallInfo fcinfo,
277 : : Oid *resultTypeId,
278 : : TupleDesc *resultTupleDesc)
279 : : {
280 : 126736 : return internal_get_result_type(fcinfo->flinfo->fn_oid,
281 : 63368 : fcinfo->flinfo->fn_expr,
282 : 63368 : (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 : 248901 : get_expr_result_type(Node *expr,
300 : : Oid *resultTypeId,
301 : : TupleDesc *resultTupleDesc)
302 : : {
303 : : TypeFuncClass result;
304 : :
305 [ + - + + ]: 248901 : if (expr && IsA(expr, FuncExpr))
306 : 240545 : result = internal_get_result_type(((FuncExpr *) expr)->funcid,
307 : : expr,
308 : : NULL,
309 : : resultTypeId,
310 : : resultTupleDesc);
7680 311 [ + - + + ]: 8356 : else if (expr && IsA(expr, OpExpr))
312 : 20 : result = internal_get_result_type(get_opcode(((OpExpr *) expr)->opno),
313 : : expr,
314 : : NULL,
315 : : resultTypeId,
316 : : resultTupleDesc);
2381 317 [ + - + + ]: 8336 : else if (expr && IsA(expr, RowExpr) &&
318 [ + - ]: 68 : ((RowExpr *) expr)->row_typeid == RECORDOID)
319 : : {
320 : : /* We can resolve the record type by generating the tupdesc directly */
321 : 68 : RowExpr *rexpr = (RowExpr *) expr;
322 : : TupleDesc tupdesc;
323 : 68 : AttrNumber i = 1;
324 : : ListCell *lcc,
325 : : *lcn;
326 : :
327 : 68 : tupdesc = CreateTemplateTupleDesc(list_length(rexpr->args));
328 [ - + ]: 68 : Assert(list_length(rexpr->args) == list_length(rexpr->colnames));
329 [ + - + + : 199 : forboth(lcc, rexpr->args, lcn, rexpr->colnames)
+ - + + +
+ + - +
+ ]
330 : : {
331 : 131 : Node *col = (Node *) lfirst(lcc);
332 : 131 : char *colname = strVal(lfirst(lcn));
333 : :
334 : 131 : TupleDescInitEntry(tupdesc, i,
335 : : colname,
336 : : exprType(col),
337 : : exprTypmod(col),
338 : : 0);
339 : 131 : TupleDescInitEntryCollation(tupdesc, i,
340 : : exprCollation(col));
341 : 131 : i++;
342 : : }
50 drowley@postgresql.o 343 :GNC 68 : TupleDescFinalize(tupdesc);
344 : :
2381 tgl@sss.pgh.pa.us 345 [ - + ]:CBC 68 : if (resultTypeId)
2381 tgl@sss.pgh.pa.us 346 :UBC 0 : *resultTypeId = rexpr->row_typeid;
2381 tgl@sss.pgh.pa.us 347 [ + - ]:CBC 68 : if (resultTupleDesc)
348 : 68 : *resultTupleDesc = BlessTupleDesc(tupdesc);
349 : 68 : return TYPEFUNC_COMPOSITE;
350 : : }
1297 351 [ + - + + ]: 8268 : else if (expr && IsA(expr, Const) &&
352 [ + + ]: 353 : ((Const *) expr)->consttype == RECORDOID &&
353 [ + - ]: 12 : !((Const *) expr)->constisnull)
354 : : {
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 : : HeapTupleHeader rec;
361 : : Oid tupType;
362 : : int32 tupTypmod;
363 : :
364 : 12 : rec = DatumGetHeapTupleHeader(((Const *) expr)->constvalue);
365 : 12 : tupType = HeapTupleHeaderGetTypeId(rec);
366 : 12 : tupTypmod = HeapTupleHeaderGetTypMod(rec);
367 [ - + ]: 12 : if (resultTypeId)
1297 tgl@sss.pgh.pa.us 368 :UBC 0 : *resultTypeId = tupType;
1297 tgl@sss.pgh.pa.us 369 [ + - + - ]:CBC 12 : if (tupType != RECORDOID || tupTypmod >= 0)
370 : : {
371 : : /* Should be able to look it up */
372 [ + - ]: 12 : if (resultTupleDesc)
373 : 12 : *resultTupleDesc = lookup_rowtype_tupdesc_copy(tupType,
374 : : tupTypmod);
375 : 12 : return TYPEFUNC_COMPOSITE;
376 : : }
377 : : else
378 : : {
379 : : /* This shouldn't really happen ... */
1297 tgl@sss.pgh.pa.us 380 [ # # ]:UBC 0 : if (resultTupleDesc)
381 : 0 : *resultTupleDesc = NULL;
382 : 0 : return TYPEFUNC_RECORD;
383 : : }
384 : : }
385 : : else
386 : : {
387 : : /* handle as a generic expression; no chance to resolve RECORD */
7507 bruce@momjian.us 388 :CBC 8256 : Oid typid = exprType(expr);
389 : : Oid base_typid;
390 : :
7705 tgl@sss.pgh.pa.us 391 [ + + ]: 8256 : if (resultTypeId)
392 : 515 : *resultTypeId = typid;
393 [ + - ]: 8256 : if (resultTupleDesc)
394 : 8256 : *resultTupleDesc = NULL;
3113 395 : 8256 : result = get_type_func_class(typid, &base_typid);
396 [ + + + + ]: 8256 : if ((result == TYPEFUNC_COMPOSITE ||
397 [ + - ]: 7638 : result == TYPEFUNC_COMPOSITE_DOMAIN) &&
398 : : resultTupleDesc)
399 : 7638 : *resultTupleDesc = lookup_rowtype_tupdesc_copy(base_typid, -1);
400 : : }
401 : :
7705 402 : 248821 : return result;
403 : : }
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 : 4329 : get_func_result_type(Oid functionId,
413 : : Oid *resultTypeId,
414 : : TupleDesc *resultTupleDesc)
415 : : {
416 : 4329 : return internal_get_result_type(functionId,
417 : : NULL,
418 : : NULL,
419 : : resultTypeId,
420 : : resultTupleDesc);
421 : : }
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 : 308262 : internal_get_result_type(Oid funcid,
433 : : Node *call_expr,
434 : : ReturnSetInfo *rsinfo,
435 : : Oid *resultTypeId,
436 : : TupleDesc *resultTupleDesc)
437 : : {
438 : : TypeFuncClass result;
439 : : HeapTuple tp;
440 : : Form_pg_proc procform;
441 : : Oid rettype;
442 : : Oid base_rettype;
443 : : TupleDesc tupdesc;
444 : :
445 : : /* First fetch the function's pg_proc row to inspect its rettype */
5924 rhaas@postgresql.org 446 : 308262 : tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
7705 tgl@sss.pgh.pa.us 447 [ - + ]: 308262 : if (!HeapTupleIsValid(tp))
7705 tgl@sss.pgh.pa.us 448 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for function %u", funcid);
7705 tgl@sss.pgh.pa.us 449 :CBC 308262 : procform = (Form_pg_proc) GETSTRUCT(tp);
450 : :
451 : 308262 : rettype = procform->prorettype;
452 : :
453 : : /* Check for OUT parameters defining a RECORD result */
454 : 308262 : tupdesc = build_function_result_tupdesc_t(tp);
455 [ + + ]: 308262 : if (tupdesc)
456 : : {
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 [ + + ]: 220308 : if (resultTypeId)
463 : 45998 : *resultTypeId = rettype;
464 : :
465 [ + - ]: 220308 : if (resolve_polymorphic_tupdesc(tupdesc,
466 : : &procform->proargtypes,
467 : : call_expr))
468 : : {
469 [ + - ]: 220308 : if (tupdesc->tdtypeid == RECORDOID &&
470 [ + - ]: 220308 : tupdesc->tdtypmod < 0)
471 : 220308 : assign_record_type_typmod(tupdesc);
472 [ + - ]: 220308 : if (resultTupleDesc)
473 : 220308 : *resultTupleDesc = tupdesc;
474 : 220308 : result = TYPEFUNC_COMPOSITE;
475 : : }
476 : : else
477 : : {
7705 tgl@sss.pgh.pa.us 478 [ # # ]:UBC 0 : if (resultTupleDesc)
479 : 0 : *resultTupleDesc = NULL;
480 : 0 : result = TYPEFUNC_RECORD;
481 : : }
482 : :
7705 tgl@sss.pgh.pa.us 483 :CBC 220308 : ReleaseSysCache(tp);
484 : :
485 : 220308 : return result;
486 : : }
487 : :
488 : : /*
489 : : * If scalar polymorphic result, try to resolve it.
490 : : */
6973 491 [ + + + + : 87954 : if (IsPolymorphicType(rettype))
+ - + - +
+ + + + +
+ + + - +
+ + + ]
492 : : {
7507 bruce@momjian.us 493 : 12877 : Oid newrettype = exprType(call_expr);
494 : :
7705 tgl@sss.pgh.pa.us 495 [ - + ]: 12877 : if (newrettype == InvalidOid) /* this probably should not happen */
7705 tgl@sss.pgh.pa.us 496 [ # # ]:UBC 0 : ereport(ERROR,
497 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
498 : : errmsg("could not determine actual result type for function \"%s\" declared to return type %s",
499 : : NameStr(procform->proname),
500 : : format_type_be(rettype))));
7705 tgl@sss.pgh.pa.us 501 :CBC 12877 : rettype = newrettype;
502 : : }
503 : :
504 [ + + ]: 87954 : if (resultTypeId)
505 : 83357 : *resultTypeId = rettype;
506 [ + - ]: 87954 : if (resultTupleDesc)
7507 bruce@momjian.us 507 : 87954 : *resultTupleDesc = NULL; /* default result */
508 : :
509 : : /* Classify the result type */
3113 tgl@sss.pgh.pa.us 510 : 87954 : result = get_type_func_class(rettype, &base_rettype);
7705 511 [ + + + + ]: 87954 : switch (result)
512 : : {
513 : 5019 : case TYPEFUNC_COMPOSITE:
514 : : case TYPEFUNC_COMPOSITE_DOMAIN:
515 [ + - ]: 5019 : if (resultTupleDesc)
3113 516 : 5019 : *resultTupleDesc = lookup_rowtype_tupdesc_copy(base_rettype, -1);
517 : : /* Named composite types can't have any polymorphic columns */
7705 518 : 5019 : break;
519 : 81896 : case TYPEFUNC_SCALAR:
520 : 81896 : break;
521 : 1031 : case TYPEFUNC_RECORD:
522 : : /* We must get the tupledesc from call context */
523 [ + + + - ]: 1031 : if (rsinfo && IsA(rsinfo, ReturnSetInfo) &&
524 [ + + ]: 414 : rsinfo->expectedDesc != NULL)
525 : : {
526 : 372 : result = TYPEFUNC_COMPOSITE;
527 [ + - ]: 372 : if (resultTupleDesc)
528 : 372 : *resultTupleDesc = rsinfo->expectedDesc;
529 : : /* Assume no polymorphic columns here, either */
530 : : }
531 : 1031 : break;
532 : 8 : default:
533 : 8 : break;
534 : : }
535 : :
536 : 87954 : ReleaseSysCache(tp);
537 : :
538 : 87954 : return result;
539 : : }
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
3113 553 : 145907 : get_expr_result_tupdesc(Node *expr, bool noError)
554 : : {
555 : : TupleDesc tupleDesc;
556 : : TypeFuncClass functypclass;
557 : :
558 : 145907 : functypclass = get_expr_result_type(expr, NULL, &tupleDesc);
559 : :
560 [ + + + + ]: 145907 : if (functypclass == TYPEFUNC_COMPOSITE ||
561 : : functypclass == TYPEFUNC_COMPOSITE_DOMAIN)
562 : 145054 : return tupleDesc;
563 : :
564 [ - + ]: 853 : if (!noError)
565 : : {
3113 tgl@sss.pgh.pa.us 566 :UBC 0 : Oid exprTypeId = exprType(expr);
567 : :
568 [ # # ]: 0 : if (exprTypeId != RECORDOID)
569 [ # # ]: 0 : ereport(ERROR,
570 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
571 : : errmsg("type %s is not composite",
572 : : format_type_be(exprTypeId))));
573 : : else
574 [ # # ]: 0 : ereport(ERROR,
575 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
576 : : errmsg("record type has not been registered")));
577 : : }
578 : :
3113 tgl@sss.pgh.pa.us 579 :CBC 853 : return NULL;
580 : : }
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
2243 591 : 1568 : resolve_anyelement_from_others(polymorphic_actuals *actuals)
592 : : {
593 [ + + ]: 1568 : if (OidIsValid(actuals->anyarray_type))
594 : : {
595 : : /* Use the element type corresponding to actual type */
596 : 1420 : Oid array_base_type = getBaseType(actuals->anyarray_type);
597 : 1420 : Oid array_typelem = get_element_type(array_base_type);
598 : :
599 [ - + ]: 1420 : if (!OidIsValid(array_typelem))
2243 tgl@sss.pgh.pa.us 600 [ # # ]:UBC 0 : ereport(ERROR,
601 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
602 : : errmsg("argument declared %s is not an array but type %s",
603 : : "anyarray",
604 : : format_type_be(array_base_type))));
2243 tgl@sss.pgh.pa.us 605 :CBC 1420 : actuals->anyelement_type = array_typelem;
606 : : }
607 [ + + ]: 148 : else if (OidIsValid(actuals->anyrange_type))
608 : : {
609 : : /* Use the element type corresponding to actual type */
610 : 116 : Oid range_base_type = getBaseType(actuals->anyrange_type);
611 : 116 : Oid range_typelem = get_range_subtype(range_base_type);
612 : :
613 [ - + ]: 116 : if (!OidIsValid(range_typelem))
2243 tgl@sss.pgh.pa.us 614 [ # # ]:UBC 0 : ereport(ERROR,
615 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
616 : : errmsg("argument declared %s is not a range type but type %s",
617 : : "anyrange",
618 : : format_type_be(range_base_type))));
2243 tgl@sss.pgh.pa.us 619 :CBC 116 : actuals->anyelement_type = range_typelem;
620 : : }
1962 akorotkov@postgresql 621 [ + - ]: 32 : else if (OidIsValid(actuals->anymultirange_type))
622 : : {
623 : : /* Use the element type based on the multirange type */
624 : : Oid multirange_base_type;
625 : : Oid multirange_typelem;
626 : : Oid range_base_type;
627 : : Oid range_typelem;
628 : :
629 : 32 : multirange_base_type = getBaseType(actuals->anymultirange_type);
630 : 32 : multirange_typelem = get_multirange_range(multirange_base_type);
631 [ - + ]: 32 : if (!OidIsValid(multirange_typelem))
1962 akorotkov@postgresql 632 [ # # ]:UBC 0 : ereport(ERROR,
633 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
634 : : errmsg("argument declared %s is not a multirange type but type %s",
635 : : "anymultirange",
636 : : format_type_be(multirange_base_type))));
637 : :
1962 akorotkov@postgresql 638 :CBC 32 : range_base_type = getBaseType(multirange_typelem);
639 : 32 : range_typelem = get_range_subtype(range_base_type);
640 : :
641 [ - + ]: 32 : if (!OidIsValid(range_typelem))
1962 akorotkov@postgresql 642 [ # # ]:UBC 0 : ereport(ERROR,
643 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
644 : : errmsg("argument declared %s does not contain a range type but type %s",
645 : : "anymultirange",
646 : : format_type_be(range_base_type))));
1962 akorotkov@postgresql 647 :CBC 32 : actuals->anyelement_type = range_typelem;
648 : : }
649 : : else
2243 tgl@sss.pgh.pa.us 650 [ # # ]:UBC 0 : elog(ERROR, "could not determine polymorphic type");
2243 tgl@sss.pgh.pa.us 651 :CBC 1568 : }
652 : :
653 : : /*
654 : : * Resolve actual type of ANYARRAY from other polymorphic inputs
655 : : */
656 : : static void
657 : 269 : resolve_anyarray_from_others(polymorphic_actuals *actuals)
658 : : {
659 : : /* If we don't know ANYELEMENT, resolve that first */
660 [ + + ]: 269 : if (!OidIsValid(actuals->anyelement_type))
661 : 48 : resolve_anyelement_from_others(actuals);
662 : :
663 [ + - ]: 269 : if (OidIsValid(actuals->anyelement_type))
664 : : {
665 : : /* Use the array type corresponding to actual type */
666 : 269 : Oid array_typeid = get_array_type(actuals->anyelement_type);
667 : :
668 [ - + ]: 269 : if (!OidIsValid(array_typeid))
2243 tgl@sss.pgh.pa.us 669 [ # # ]:UBC 0 : ereport(ERROR,
670 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
671 : : errmsg("could not find array type for data type %s",
672 : : format_type_be(actuals->anyelement_type))));
2243 tgl@sss.pgh.pa.us 673 :CBC 269 : actuals->anyarray_type = array_typeid;
674 : : }
675 : : else
2243 tgl@sss.pgh.pa.us 676 [ # # ]:UBC 0 : elog(ERROR, "could not determine polymorphic type");
2243 tgl@sss.pgh.pa.us 677 :CBC 269 : }
678 : :
679 : : /*
680 : : * Resolve actual type of ANYRANGE from other polymorphic inputs
681 : : */
682 : : static void
683 : 16 : resolve_anyrange_from_others(polymorphic_actuals *actuals)
684 : : {
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 : : */
1962 akorotkov@postgresql 690 [ + - ]: 16 : if (OidIsValid(actuals->anymultirange_type))
691 : : {
692 : : /* Use the element type based on the multirange type */
693 : 16 : Oid multirange_base_type = getBaseType(actuals->anymultirange_type);
694 : 16 : Oid multirange_typelem = get_multirange_range(multirange_base_type);
695 : :
696 [ - + ]: 16 : if (!OidIsValid(multirange_typelem))
1962 akorotkov@postgresql 697 [ # # ]:UBC 0 : ereport(ERROR,
698 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
699 : : errmsg("argument declared %s is not a multirange type but type %s",
700 : : "anymultirange",
701 : : format_type_be(multirange_base_type))));
1962 akorotkov@postgresql 702 :CBC 16 : actuals->anyrange_type = multirange_typelem;
703 : : }
704 : : else
1962 akorotkov@postgresql 705 [ # # ]:UBC 0 : elog(ERROR, "could not determine polymorphic type");
1962 akorotkov@postgresql 706 :CBC 16 : }
707 : :
708 : : /*
709 : : * Resolve actual type of ANYMULTIRANGE from other polymorphic inputs
710 : : */
711 : : static void
712 : 16 : resolve_anymultirange_from_others(polymorphic_actuals *actuals)
713 : : {
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 [ + - ]: 16 : if (OidIsValid(actuals->anyrange_type))
720 : : {
721 : 16 : Oid range_base_type = getBaseType(actuals->anyrange_type);
722 : 16 : Oid multirange_typeid = get_range_multirange(range_base_type);
723 : :
724 [ - + ]: 16 : if (!OidIsValid(multirange_typeid))
1962 akorotkov@postgresql 725 [ # # ]:UBC 0 : ereport(ERROR,
726 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
727 : : errmsg("could not find multirange type for data type %s",
728 : : format_type_be(actuals->anyrange_type))));
1962 akorotkov@postgresql 729 :CBC 16 : actuals->anymultirange_type = multirange_typeid;
730 : : }
731 : : else
1962 akorotkov@postgresql 732 [ # # ]:UBC 0 : elog(ERROR, "could not determine polymorphic type");
2243 tgl@sss.pgh.pa.us 733 :CBC 16 : }
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
7705 746 : 220308 : resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
747 : : Node *call_expr)
748 : : {
749 : 220308 : int natts = tupdesc->natts;
750 : 220308 : int nargs = declared_args->dim1;
2243 751 : 220308 : bool have_polymorphic_result = false;
7705 752 : 220308 : bool have_anyelement_result = false;
753 : 220308 : bool have_anyarray_result = false;
5297 heikki.linnakangas@i 754 : 220308 : bool have_anyrange_result = false;
1962 akorotkov@postgresql 755 : 220308 : bool have_anymultirange_result = false;
2238 tgl@sss.pgh.pa.us 756 : 220308 : bool have_anycompatible_result = false;
757 : 220308 : bool have_anycompatible_array_result = false;
758 : 220308 : bool have_anycompatible_range_result = false;
1962 akorotkov@postgresql 759 : 220308 : bool have_anycompatible_multirange_result = false;
760 : : polymorphic_actuals poly_actuals;
761 : : polymorphic_actuals anyc_actuals;
5297 heikki.linnakangas@i 762 : 220308 : Oid anycollation = InvalidOid;
2238 tgl@sss.pgh.pa.us 763 : 220308 : Oid anycompatcollation = InvalidOid;
764 : : int i;
765 : :
766 : : /* See if there are any polymorphic outputs; quick out if not */
7705 767 [ + + ]: 3633312 : for (i = 0; i < natts; i++)
768 : : {
3180 andres@anarazel.de 769 [ + + + + : 3413004 : switch (TupleDescAttr(tupdesc, i)->atttypid)
+ + + -
+ ]
770 : : {
7705 tgl@sss.pgh.pa.us 771 : 1641 : case ANYELEMENTOID:
772 : : case ANYNONARRAYOID:
773 : : case ANYENUMOID:
2243 774 : 1641 : have_polymorphic_result = true;
7705 775 : 1641 : have_anyelement_result = true;
776 : 1641 : break;
777 : 221 : case ANYARRAYOID:
2243 778 : 221 : have_polymorphic_result = true;
7705 779 : 221 : have_anyarray_result = true;
780 : 221 : break;
5297 heikki.linnakangas@i 781 : 48 : case ANYRANGEOID:
2243 tgl@sss.pgh.pa.us 782 : 48 : have_polymorphic_result = true;
5297 heikki.linnakangas@i 783 : 48 : have_anyrange_result = true;
784 : 48 : break;
1962 akorotkov@postgresql 785 : 64 : case ANYMULTIRANGEOID:
786 : 64 : have_polymorphic_result = true;
787 : 64 : have_anymultirange_result = true;
788 : 64 : break;
2238 tgl@sss.pgh.pa.us 789 : 84 : case ANYCOMPATIBLEOID:
790 : : case ANYCOMPATIBLENONARRAYOID:
791 : 84 : have_polymorphic_result = true;
792 : 84 : have_anycompatible_result = true;
793 : 84 : break;
794 : 164 : case ANYCOMPATIBLEARRAYOID:
795 : 164 : have_polymorphic_result = true;
796 : 164 : have_anycompatible_array_result = true;
797 : 164 : break;
798 : 24 : case ANYCOMPATIBLERANGEOID:
799 : 24 : have_polymorphic_result = true;
800 : 24 : have_anycompatible_range_result = true;
801 : 24 : break;
1962 akorotkov@postgresql 802 :UBC 0 : case ANYCOMPATIBLEMULTIRANGEOID:
803 : 0 : have_polymorphic_result = true;
804 : 0 : have_anycompatible_multirange_result = true;
805 : 0 : break;
7705 tgl@sss.pgh.pa.us 806 :CBC 3410758 : default:
807 : 3410758 : break;
808 : : }
809 : : }
2243 810 [ + + ]: 220308 : if (!have_polymorphic_result)
7705 811 : 218439 : 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 [ - + ]: 1869 : if (!call_expr)
7705 tgl@sss.pgh.pa.us 821 :UBC 0 : return false; /* no hope */
822 : :
2243 tgl@sss.pgh.pa.us 823 :CBC 1869 : memset(&poly_actuals, 0, sizeof(poly_actuals));
2238 824 : 1869 : memset(&anyc_actuals, 0, sizeof(anyc_actuals));
825 : :
7705 826 [ + + ]: 4046 : for (i = 0; i < nargs; i++)
827 : : {
828 [ + + + + : 2177 : switch (declared_args->values[i])
+ + + -
- ]
829 : : {
830 : 221 : case ANYELEMENTOID:
831 : : case ANYNONARRAYOID:
832 : : case ANYENUMOID:
2243 833 [ + + ]: 221 : if (!OidIsValid(poly_actuals.anyelement_type))
834 : : {
835 : 205 : poly_actuals.anyelement_type =
836 : 205 : get_call_expr_argtype(call_expr, i);
837 [ - + ]: 205 : if (!OidIsValid(poly_actuals.anyelement_type))
2243 tgl@sss.pgh.pa.us 838 :UBC 0 : return false;
839 : : }
7705 tgl@sss.pgh.pa.us 840 :CBC 221 : break;
841 : 1500 : case ANYARRAYOID:
2243 842 [ + - ]: 1500 : if (!OidIsValid(poly_actuals.anyarray_type))
843 : : {
844 : 1500 : poly_actuals.anyarray_type =
845 : 1500 : get_call_expr_argtype(call_expr, i);
846 [ - + ]: 1500 : if (!OidIsValid(poly_actuals.anyarray_type))
2243 tgl@sss.pgh.pa.us 847 :UBC 0 : return false;
848 : : }
7705 tgl@sss.pgh.pa.us 849 :CBC 1500 : break;
5297 heikki.linnakangas@i 850 : 96 : case ANYRANGEOID:
2243 tgl@sss.pgh.pa.us 851 [ + - ]: 96 : if (!OidIsValid(poly_actuals.anyrange_type))
852 : : {
853 : 96 : poly_actuals.anyrange_type =
854 : 96 : get_call_expr_argtype(call_expr, i);
855 [ - + ]: 96 : if (!OidIsValid(poly_actuals.anyrange_type))
2243 tgl@sss.pgh.pa.us 856 :UBC 0 : return false;
857 : : }
5297 heikki.linnakangas@i 858 :CBC 96 : break;
1962 akorotkov@postgresql 859 : 80 : case ANYMULTIRANGEOID:
860 [ + - ]: 80 : if (!OidIsValid(poly_actuals.anymultirange_type))
861 : : {
862 : 80 : poly_actuals.anymultirange_type =
863 : 80 : get_call_expr_argtype(call_expr, i);
864 [ - + ]: 80 : if (!OidIsValid(poly_actuals.anymultirange_type))
1962 akorotkov@postgresql 865 :UBC 0 : return false;
866 : : }
1962 akorotkov@postgresql 867 :CBC 80 : break;
2238 tgl@sss.pgh.pa.us 868 : 196 : case ANYCOMPATIBLEOID:
869 : : case ANYCOMPATIBLENONARRAYOID:
870 [ + + ]: 196 : if (!OidIsValid(anyc_actuals.anyelement_type))
871 : : {
872 : 116 : anyc_actuals.anyelement_type =
873 : 116 : get_call_expr_argtype(call_expr, i);
874 [ - + ]: 116 : if (!OidIsValid(anyc_actuals.anyelement_type))
2238 tgl@sss.pgh.pa.us 875 :UBC 0 : return false;
876 : : }
2238 tgl@sss.pgh.pa.us 877 :CBC 196 : break;
878 : 36 : case ANYCOMPATIBLEARRAYOID:
879 [ + - ]: 36 : if (!OidIsValid(anyc_actuals.anyarray_type))
880 : : {
881 : 36 : anyc_actuals.anyarray_type =
882 : 36 : get_call_expr_argtype(call_expr, i);
883 [ - + ]: 36 : if (!OidIsValid(anyc_actuals.anyarray_type))
2238 tgl@sss.pgh.pa.us 884 :UBC 0 : return false;
885 : : }
2238 tgl@sss.pgh.pa.us 886 :CBC 36 : break;
887 : 48 : case ANYCOMPATIBLERANGEOID:
888 [ + - ]: 48 : if (!OidIsValid(anyc_actuals.anyrange_type))
889 : : {
890 : 48 : anyc_actuals.anyrange_type =
891 : 48 : get_call_expr_argtype(call_expr, i);
892 [ - + ]: 48 : if (!OidIsValid(anyc_actuals.anyrange_type))
2238 tgl@sss.pgh.pa.us 893 :UBC 0 : return false;
894 : : }
2238 tgl@sss.pgh.pa.us 895 :CBC 48 : break;
1962 akorotkov@postgresql 896 :UBC 0 : case ANYCOMPATIBLEMULTIRANGEOID:
897 [ # # ]: 0 : if (!OidIsValid(anyc_actuals.anymultirange_type))
898 : : {
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 : : }
904 : 0 : break;
7705 tgl@sss.pgh.pa.us 905 : 0 : default:
906 : 0 : break;
907 : : }
908 : : }
909 : :
910 : : /* If needed, deduce one polymorphic type from others */
2243 tgl@sss.pgh.pa.us 911 [ + + + + ]:CBC 1869 : if (have_anyelement_result && !OidIsValid(poly_actuals.anyelement_type))
912 : 1468 : resolve_anyelement_from_others(&poly_actuals);
913 : :
914 [ + + + + ]: 1869 : if (have_anyarray_result && !OidIsValid(poly_actuals.anyarray_type))
915 : 125 : resolve_anyarray_from_others(&poly_actuals);
916 : :
917 [ + + + + ]: 1869 : if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
918 : 16 : resolve_anyrange_from_others(&poly_actuals);
919 : :
1962 akorotkov@postgresql 920 [ + + + + ]: 1869 : if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type))
921 : 16 : resolve_anymultirange_from_others(&poly_actuals);
922 : :
2238 tgl@sss.pgh.pa.us 923 [ + + + + ]: 1869 : if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
924 : 48 : resolve_anyelement_from_others(&anyc_actuals);
925 : :
926 [ + + + + ]: 1869 : if (have_anycompatible_array_result && !OidIsValid(anyc_actuals.anyarray_type))
927 : 128 : resolve_anyarray_from_others(&anyc_actuals);
928 : :
929 [ + + - + ]: 1869 : if (have_anycompatible_range_result && !OidIsValid(anyc_actuals.anyrange_type))
2238 tgl@sss.pgh.pa.us 930 :UBC 0 : resolve_anyrange_from_others(&anyc_actuals);
931 : :
1962 akorotkov@postgresql 932 [ - + - - ]:CBC 1869 : if (have_anycompatible_multirange_result && !OidIsValid(anyc_actuals.anymultirange_type))
1962 akorotkov@postgresql 933 :UBC 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 : : */
2243 tgl@sss.pgh.pa.us 942 [ + + ]:CBC 1869 : if (OidIsValid(poly_actuals.anyelement_type))
943 : 1721 : anycollation = get_typcollation(poly_actuals.anyelement_type);
944 [ - + ]: 148 : else if (OidIsValid(poly_actuals.anyarray_type))
2243 tgl@sss.pgh.pa.us 945 :UBC 0 : anycollation = get_typcollation(poly_actuals.anyarray_type);
946 : :
2238 tgl@sss.pgh.pa.us 947 [ + + ]:CBC 1869 : if (OidIsValid(anyc_actuals.anyelement_type))
948 : 164 : anycompatcollation = get_typcollation(anyc_actuals.anyelement_type);
949 [ - + ]: 1705 : else if (OidIsValid(anyc_actuals.anyarray_type))
2238 tgl@sss.pgh.pa.us 950 :UBC 0 : anycompatcollation = get_typcollation(anyc_actuals.anyarray_type);
951 : :
2238 tgl@sss.pgh.pa.us 952 [ + + + + ]:CBC 1869 : if (OidIsValid(anycollation) || OidIsValid(anycompatcollation))
953 : : {
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 : : */
5504 bruce@momjian.us 959 : 52 : Oid inputcollation = exprInputCollation(call_expr);
960 : :
5526 tgl@sss.pgh.pa.us 961 [ + + ]: 52 : if (OidIsValid(inputcollation))
962 : : {
2238 963 [ + - ]: 32 : if (OidIsValid(anycollation))
964 : 32 : anycollation = inputcollation;
965 [ - + ]: 32 : if (OidIsValid(anycompatcollation))
2238 tgl@sss.pgh.pa.us 966 :UBC 0 : anycompatcollation = inputcollation;
967 : : }
968 : : }
969 : :
970 : : /* And finally replace the tuple column types as needed */
7705 tgl@sss.pgh.pa.us 971 [ + + ]:CBC 5631 : for (i = 0; i < natts; i++)
972 : : {
3180 andres@anarazel.de 973 : 3762 : Form_pg_attribute att = TupleDescAttr(tupdesc, i);
974 : :
975 [ + + + + : 3762 : switch (att->atttypid)
+ + + -
+ ]
976 : : {
7705 tgl@sss.pgh.pa.us 977 : 1641 : case ANYELEMENTOID:
978 : : case ANYNONARRAYOID:
979 : : case ANYENUMOID:
7507 bruce@momjian.us 980 : 1641 : TupleDescInitEntry(tupdesc, i + 1,
3180 andres@anarazel.de 981 : 1641 : NameStr(att->attname),
982 : : poly_actuals.anyelement_type,
983 : : -1,
984 : : 0);
5526 tgl@sss.pgh.pa.us 985 : 1641 : TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
7705 986 : 1641 : break;
987 : 221 : case ANYARRAYOID:
7507 bruce@momjian.us 988 : 221 : TupleDescInitEntry(tupdesc, i + 1,
3180 andres@anarazel.de 989 : 221 : NameStr(att->attname),
990 : : poly_actuals.anyarray_type,
991 : : -1,
992 : : 0);
5526 tgl@sss.pgh.pa.us 993 : 221 : TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
7705 994 : 221 : break;
5297 heikki.linnakangas@i 995 : 48 : case ANYRANGEOID:
996 : 48 : TupleDescInitEntry(tupdesc, i + 1,
3180 andres@anarazel.de 997 : 48 : NameStr(att->attname),
998 : : poly_actuals.anyrange_type,
999 : : -1,
1000 : : 0);
1001 : : /* no collation should be attached to a range type */
5297 heikki.linnakangas@i 1002 : 48 : break;
1962 akorotkov@postgresql 1003 : 64 : case ANYMULTIRANGEOID:
1004 : 64 : TupleDescInitEntry(tupdesc, i + 1,
1005 : 64 : NameStr(att->attname),
1006 : : poly_actuals.anymultirange_type,
1007 : : -1,
1008 : : 0);
1009 : : /* no collation should be attached to a multirange type */
1010 : 64 : break;
2238 tgl@sss.pgh.pa.us 1011 : 84 : case ANYCOMPATIBLEOID:
1012 : : case ANYCOMPATIBLENONARRAYOID:
1013 : 84 : TupleDescInitEntry(tupdesc, i + 1,
1014 : 84 : NameStr(att->attname),
1015 : : anyc_actuals.anyelement_type,
1016 : : -1,
1017 : : 0);
1018 : 84 : TupleDescInitEntryCollation(tupdesc, i + 1, anycompatcollation);
1019 : 84 : break;
1020 : 164 : case ANYCOMPATIBLEARRAYOID:
1021 : 164 : TupleDescInitEntry(tupdesc, i + 1,
1022 : 164 : NameStr(att->attname),
1023 : : anyc_actuals.anyarray_type,
1024 : : -1,
1025 : : 0);
1026 : 164 : TupleDescInitEntryCollation(tupdesc, i + 1, anycompatcollation);
1027 : 164 : break;
1028 : 24 : case ANYCOMPATIBLERANGEOID:
1029 : 24 : TupleDescInitEntry(tupdesc, i + 1,
1030 : 24 : NameStr(att->attname),
1031 : : anyc_actuals.anyrange_type,
1032 : : -1,
1033 : : 0);
1034 : : /* no collation should be attached to a range type */
1035 : 24 : break;
1962 akorotkov@postgresql 1036 :UBC 0 : case ANYCOMPATIBLEMULTIRANGEOID:
1037 : 0 : TupleDescInitEntry(tupdesc, i + 1,
1038 : 0 : NameStr(att->attname),
1039 : : anyc_actuals.anymultirange_type,
1040 : : -1,
1041 : : 0);
1042 : : /* no collation should be attached to a multirange type */
1043 : 0 : break;
7705 tgl@sss.pgh.pa.us 1044 :CBC 1516 : default:
1045 : 1516 : break;
1046 : : }
1047 : : }
1048 : :
50 drowley@postgresql.o 1049 :GNC 1869 : TupleDescFinalize(tupdesc);
7705 tgl@sss.pgh.pa.us 1050 :CBC 1869 : return true;
1051 : : }
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
7700 1067 : 23482 : resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
1068 : : Node *call_expr)
1069 : : {
2243 1070 : 23482 : bool have_polymorphic_result = false;
7700 1071 : 23482 : bool have_anyelement_result = false;
1072 : 23482 : bool have_anyarray_result = false;
5297 heikki.linnakangas@i 1073 : 23482 : bool have_anyrange_result = false;
1962 akorotkov@postgresql 1074 : 23482 : bool have_anymultirange_result = false;
2238 tgl@sss.pgh.pa.us 1075 : 23482 : bool have_anycompatible_result = false;
1076 : 23482 : bool have_anycompatible_array_result = false;
1077 : 23482 : bool have_anycompatible_range_result = false;
1962 akorotkov@postgresql 1078 : 23482 : bool have_anycompatible_multirange_result = false;
1079 : : polymorphic_actuals poly_actuals;
1080 : : polymorphic_actuals anyc_actuals;
1081 : : int inargno;
1082 : : 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 : : */
2243 tgl@sss.pgh.pa.us 1089 : 23482 : memset(&poly_actuals, 0, sizeof(poly_actuals));
2238 1090 : 23482 : memset(&anyc_actuals, 0, sizeof(anyc_actuals));
7700 1091 : 23482 : inargno = 0;
1092 [ + + ]: 64489 : for (i = 0; i < numargs; i++)
1093 : : {
7507 bruce@momjian.us 1094 [ + + ]: 41007 : char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
1095 : :
7700 tgl@sss.pgh.pa.us 1096 [ + + + + : 41007 : switch (argtypes[i])
+ + + -
+ ]
1097 : : {
1098 : 1060 : case ANYELEMENTOID:
1099 : : case ANYNONARRAYOID:
1100 : : case ANYENUMOID:
6500 1101 [ + + - + ]: 1060 : if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
1102 : : {
2243 1103 : 4 : have_polymorphic_result = true;
7700 1104 : 4 : have_anyelement_result = true;
1105 : : }
1106 : : else
1107 : : {
2243 1108 [ + + ]: 1056 : if (!OidIsValid(poly_actuals.anyelement_type))
1109 : : {
1110 : 736 : poly_actuals.anyelement_type =
1111 : 736 : get_call_expr_argtype(call_expr, inargno);
1112 [ - + ]: 736 : if (!OidIsValid(poly_actuals.anyelement_type))
7700 tgl@sss.pgh.pa.us 1113 :UBC 0 : return false;
1114 : : }
2243 tgl@sss.pgh.pa.us 1115 :CBC 1056 : argtypes[i] = poly_actuals.anyelement_type;
1116 : : }
7700 1117 : 1060 : break;
1118 : 402 : case ANYARRAYOID:
6500 1119 [ + + - + ]: 402 : if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
1120 : : {
2243 1121 : 12 : have_polymorphic_result = true;
7700 1122 : 12 : have_anyarray_result = true;
1123 : : }
1124 : : else
1125 : : {
2243 1126 [ + + ]: 390 : if (!OidIsValid(poly_actuals.anyarray_type))
1127 : : {
1128 : 382 : poly_actuals.anyarray_type =
1129 : 382 : get_call_expr_argtype(call_expr, inargno);
1130 [ - + ]: 382 : if (!OidIsValid(poly_actuals.anyarray_type))
7700 tgl@sss.pgh.pa.us 1131 :UBC 0 : return false;
1132 : : }
2243 tgl@sss.pgh.pa.us 1133 :CBC 390 : argtypes[i] = poly_actuals.anyarray_type;
1134 : : }
7700 1135 : 402 : break;
5297 heikki.linnakangas@i 1136 : 40 : case ANYRANGEOID:
1137 [ + - - + ]: 40 : if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
1138 : : {
2243 tgl@sss.pgh.pa.us 1139 :UBC 0 : have_polymorphic_result = true;
5297 heikki.linnakangas@i 1140 : 0 : have_anyrange_result = true;
1141 : : }
1142 : : else
1143 : : {
2243 tgl@sss.pgh.pa.us 1144 [ + - ]:CBC 40 : if (!OidIsValid(poly_actuals.anyrange_type))
1145 : : {
1146 : 40 : poly_actuals.anyrange_type =
1147 : 40 : get_call_expr_argtype(call_expr, inargno);
1148 [ - + ]: 40 : if (!OidIsValid(poly_actuals.anyrange_type))
5297 heikki.linnakangas@i 1149 :UBC 0 : return false;
1150 : : }
2243 tgl@sss.pgh.pa.us 1151 :CBC 40 : argtypes[i] = poly_actuals.anyrange_type;
1152 : : }
5297 heikki.linnakangas@i 1153 : 40 : break;
1962 akorotkov@postgresql 1154 : 92 : case ANYMULTIRANGEOID:
1155 [ + - - + ]: 92 : if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
1156 : : {
1962 akorotkov@postgresql 1157 :UBC 0 : have_polymorphic_result = true;
1158 : 0 : have_anymultirange_result = true;
1159 : : }
1160 : : else
1161 : : {
1962 akorotkov@postgresql 1162 [ + - ]:CBC 92 : if (!OidIsValid(poly_actuals.anymultirange_type))
1163 : : {
1164 : 92 : poly_actuals.anymultirange_type =
1165 : 92 : get_call_expr_argtype(call_expr, inargno);
1166 [ - + ]: 92 : if (!OidIsValid(poly_actuals.anymultirange_type))
1962 akorotkov@postgresql 1167 :UBC 0 : return false;
1168 : : }
1962 akorotkov@postgresql 1169 :CBC 92 : argtypes[i] = poly_actuals.anymultirange_type;
1170 : : }
1171 : 92 : break;
2238 tgl@sss.pgh.pa.us 1172 : 148 : case ANYCOMPATIBLEOID:
1173 : : case ANYCOMPATIBLENONARRAYOID:
1174 [ + + - + ]: 148 : if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
1175 : : {
1176 : 4 : have_polymorphic_result = true;
1177 : 4 : have_anycompatible_result = true;
1178 : : }
1179 : : else
1180 : : {
1181 [ + + ]: 144 : if (!OidIsValid(anyc_actuals.anyelement_type))
1182 : : {
1183 : 88 : anyc_actuals.anyelement_type =
1184 : 88 : get_call_expr_argtype(call_expr, inargno);
1185 [ - + ]: 88 : if (!OidIsValid(anyc_actuals.anyelement_type))
2238 tgl@sss.pgh.pa.us 1186 :UBC 0 : return false;
1187 : : }
2238 tgl@sss.pgh.pa.us 1188 :CBC 144 : argtypes[i] = anyc_actuals.anyelement_type;
1189 : : }
1190 : 148 : break;
1191 : 56 : case ANYCOMPATIBLEARRAYOID:
1192 [ + + - + ]: 56 : if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
1193 : : {
1194 : 12 : have_polymorphic_result = true;
1195 : 12 : have_anycompatible_array_result = true;
1196 : : }
1197 : : else
1198 : : {
1199 [ + - ]: 44 : if (!OidIsValid(anyc_actuals.anyarray_type))
1200 : : {
1201 : 44 : anyc_actuals.anyarray_type =
1202 : 44 : get_call_expr_argtype(call_expr, inargno);
1203 [ - + ]: 44 : if (!OidIsValid(anyc_actuals.anyarray_type))
2238 tgl@sss.pgh.pa.us 1204 :UBC 0 : return false;
1205 : : }
2238 tgl@sss.pgh.pa.us 1206 :CBC 44 : argtypes[i] = anyc_actuals.anyarray_type;
1207 : : }
1208 : 56 : break;
1209 : 48 : case ANYCOMPATIBLERANGEOID:
1210 [ + - - + ]: 48 : if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
1211 : : {
2238 tgl@sss.pgh.pa.us 1212 :UBC 0 : have_polymorphic_result = true;
1213 : 0 : have_anycompatible_range_result = true;
1214 : : }
1215 : : else
1216 : : {
2238 tgl@sss.pgh.pa.us 1217 [ + - ]:CBC 48 : if (!OidIsValid(anyc_actuals.anyrange_type))
1218 : : {
1219 : 48 : anyc_actuals.anyrange_type =
1220 : 48 : get_call_expr_argtype(call_expr, inargno);
1221 [ - + ]: 48 : if (!OidIsValid(anyc_actuals.anyrange_type))
2238 tgl@sss.pgh.pa.us 1222 :UBC 0 : return false;
1223 : : }
2238 tgl@sss.pgh.pa.us 1224 :CBC 48 : argtypes[i] = anyc_actuals.anyrange_type;
1225 : : }
1226 : 48 : break;
1962 akorotkov@postgresql 1227 :UBC 0 : case ANYCOMPATIBLEMULTIRANGEOID:
1228 [ # # # # ]: 0 : if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
1229 : : {
1230 : 0 : have_polymorphic_result = true;
1231 : 0 : have_anycompatible_multirange_result = true;
1232 : : }
1233 : : else
1234 : : {
1235 [ # # ]: 0 : if (!OidIsValid(anyc_actuals.anymultirange_type))
1236 : : {
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 : : }
1242 : 0 : argtypes[i] = anyc_actuals.anymultirange_type;
1243 : : }
1244 : 0 : break;
7700 tgl@sss.pgh.pa.us 1245 :CBC 39161 : default:
1246 : 39161 : break;
1247 : : }
6500 1248 [ + + + + ]: 41007 : if (argmode != PROARGMODE_OUT && argmode != PROARGMODE_TABLE)
7700 1249 : 40919 : inargno++;
1250 : : }
1251 : :
1252 : : /* Done? */
2243 1253 [ + + ]: 23482 : if (!have_polymorphic_result)
7700 1254 : 23466 : return true;
1255 : :
1256 : : /* If needed, deduce one polymorphic type from others */
2243 1257 [ + + - + ]: 16 : if (have_anyelement_result && !OidIsValid(poly_actuals.anyelement_type))
2243 tgl@sss.pgh.pa.us 1258 :UBC 0 : resolve_anyelement_from_others(&poly_actuals);
1259 : :
2243 tgl@sss.pgh.pa.us 1260 [ + + + + ]:CBC 16 : if (have_anyarray_result && !OidIsValid(poly_actuals.anyarray_type))
1261 : 4 : resolve_anyarray_from_others(&poly_actuals);
1262 : :
1263 [ - + - - ]: 16 : if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
2243 tgl@sss.pgh.pa.us 1264 :UBC 0 : resolve_anyrange_from_others(&poly_actuals);
1265 : :
1962 akorotkov@postgresql 1266 [ - + - - ]:CBC 16 : if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type))
1962 akorotkov@postgresql 1267 :UBC 0 : resolve_anymultirange_from_others(&poly_actuals);
1268 : :
2238 tgl@sss.pgh.pa.us 1269 [ + + + - ]:CBC 16 : if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
1270 : 4 : resolve_anyelement_from_others(&anyc_actuals);
1271 : :
1272 [ + + + - ]: 16 : if (have_anycompatible_array_result && !OidIsValid(anyc_actuals.anyarray_type))
1273 : 12 : resolve_anyarray_from_others(&anyc_actuals);
1274 : :
1275 [ - + - - ]: 16 : if (have_anycompatible_range_result && !OidIsValid(anyc_actuals.anyrange_type))
2238 tgl@sss.pgh.pa.us 1276 :UBC 0 : resolve_anyrange_from_others(&anyc_actuals);
1277 : :
1962 akorotkov@postgresql 1278 [ - + - - ]:CBC 16 : if (have_anycompatible_multirange_result && !OidIsValid(anyc_actuals.anymultirange_type))
1962 akorotkov@postgresql 1279 :UBC 0 : resolve_anymultirange_from_others(&anyc_actuals);
1280 : :
1281 : : /* And finally replace the output column types as needed */
7700 tgl@sss.pgh.pa.us 1282 [ + + ]:CBC 88 : for (i = 0; i < numargs; i++)
1283 : : {
1284 [ + + - - : 72 : switch (argtypes[i])
+ + - -
+ ]
1285 : : {
1286 : 4 : case ANYELEMENTOID:
1287 : : case ANYNONARRAYOID:
1288 : : case ANYENUMOID:
2243 1289 : 4 : argtypes[i] = poly_actuals.anyelement_type;
7700 1290 : 4 : break;
1291 : 12 : case ANYARRAYOID:
2243 1292 : 12 : argtypes[i] = poly_actuals.anyarray_type;
7700 1293 : 12 : break;
5297 heikki.linnakangas@i 1294 :UBC 0 : case ANYRANGEOID:
2243 tgl@sss.pgh.pa.us 1295 : 0 : argtypes[i] = poly_actuals.anyrange_type;
5297 heikki.linnakangas@i 1296 : 0 : break;
1962 akorotkov@postgresql 1297 : 0 : case ANYMULTIRANGEOID:
1298 : 0 : argtypes[i] = poly_actuals.anymultirange_type;
1299 : 0 : break;
2238 tgl@sss.pgh.pa.us 1300 :CBC 4 : case ANYCOMPATIBLEOID:
1301 : : case ANYCOMPATIBLENONARRAYOID:
1302 : 4 : argtypes[i] = anyc_actuals.anyelement_type;
1303 : 4 : break;
1304 : 12 : case ANYCOMPATIBLEARRAYOID:
1305 : 12 : argtypes[i] = anyc_actuals.anyarray_type;
1306 : 12 : break;
2238 tgl@sss.pgh.pa.us 1307 :UBC 0 : case ANYCOMPATIBLERANGEOID:
1308 : 0 : argtypes[i] = anyc_actuals.anyrange_type;
1309 : 0 : break;
1962 akorotkov@postgresql 1310 : 0 : case ANYCOMPATIBLEMULTIRANGEOID:
1311 : 0 : argtypes[i] = anyc_actuals.anymultirange_type;
1312 : 0 : break;
7700 tgl@sss.pgh.pa.us 1313 :CBC 40 : default:
1314 : 40 : break;
1315 : : }
1316 : : }
1317 : :
1318 : 16 : return true;
1319 : : }
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
3113 1331 : 96210 : get_type_func_class(Oid typid, Oid *base_typeid)
1332 : : {
1333 : 96210 : *base_typeid = typid;
1334 : :
7705 1335 [ + + + + : 96210 : switch (get_typtype(typid))
- ]
1336 : : {
6973 1337 : 12472 : case TYPTYPE_COMPOSITE:
7705 1338 : 12472 : return TYPEFUNC_COMPOSITE;
6973 1339 : 81278 : case TYPTYPE_BASE:
1340 : : case TYPTYPE_ENUM:
1341 : : case TYPTYPE_RANGE:
1342 : : case TYPTYPE_MULTIRANGE:
7705 1343 : 81278 : return TYPEFUNC_SCALAR;
3113 1344 : 473 : case TYPTYPE_DOMAIN:
1345 : 473 : *base_typeid = typid = getBaseType(typid);
1346 [ + + ]: 473 : if (get_typtype(typid) == TYPTYPE_COMPOSITE)
1347 : 185 : return TYPEFUNC_COMPOSITE_DOMAIN;
1348 : : else /* domain base type can't be a pseudotype */
1349 : 288 : return TYPEFUNC_SCALAR;
6973 1350 : 1987 : case TYPTYPE_PSEUDO:
7705 1351 [ + + ]: 1987 : if (typid == RECORDOID)
1352 : 1048 : 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 [ + + - + ]: 939 : if (typid == VOIDOID || typid == CSTRINGOID)
1361 : 931 : return TYPEFUNC_SCALAR;
1362 : 8 : return TYPEFUNC_OTHER;
1363 : : }
1364 : : /* shouldn't get here, probably */
7705 tgl@sss.pgh.pa.us 1365 :UBC 0 : return TYPEFUNC_OTHER;
1366 : : }
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
7433 tgl@sss.pgh.pa.us 1382 :CBC 20411 : get_func_arg_info(HeapTuple procTup,
1383 : : Oid **p_argtypes, char ***p_argnames, char **p_argmodes)
1384 : : {
1385 : 20411 : Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
1386 : : Datum proallargtypes;
1387 : : Datum proargmodes;
1388 : : Datum proargnames;
1389 : : bool isNull;
1390 : : ArrayType *arr;
1391 : : int numargs;
1392 : : Datum *elems;
1393 : : int nelems;
1394 : : int i;
1395 : :
1396 : : /* First discover the total number of parameters and get their types */
1397 : 20411 : proallargtypes = SysCacheGetAttr(PROCOID, procTup,
1398 : : Anum_pg_proc_proallargtypes,
1399 : : &isNull);
1400 [ + + ]: 20411 : if (!isNull)
1401 : : {
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 : : */
3240 1408 : 4723 : arr = DatumGetArrayTypeP(proallargtypes); /* ensure not toasted */
7433 1409 : 4723 : numargs = ARR_DIMS(arr)[0];
1410 [ + - + - ]: 4723 : if (ARR_NDIM(arr) != 1 ||
1411 : 4723 : numargs < 0 ||
1412 [ + - ]: 4723 : ARR_HASNULL(arr) ||
1413 [ - + ]: 4723 : ARR_ELEMTYPE(arr) != OIDOID)
1989 alvherre@alvh.no-ip. 1414 [ # # ]:UBC 0 : elog(ERROR, "proallargtypes is not a 1-D Oid array or it contains nulls");
7433 tgl@sss.pgh.pa.us 1415 [ - + ]:CBC 4723 : Assert(numargs >= procStruct->pronargs);
1416 : 4723 : *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid));
1417 [ - + ]: 4723 : memcpy(*p_argtypes, ARR_DATA_PTR(arr),
1418 : : numargs * sizeof(Oid));
1419 : : }
1420 : : else
1421 : : {
1422 : : /* If no proallargtypes, use proargtypes */
1423 : 15688 : numargs = procStruct->proargtypes.dim1;
1424 [ - + ]: 15688 : Assert(numargs == procStruct->pronargs);
1425 : 15688 : *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid));
1426 : 15688 : memcpy(*p_argtypes, procStruct->proargtypes.values,
1427 : : numargs * sizeof(Oid));
1428 : : }
1429 : :
1430 : : /* Get argument names, if available */
1431 : 20411 : proargnames = SysCacheGetAttr(PROCOID, procTup,
1432 : : Anum_pg_proc_proargnames,
1433 : : &isNull);
1434 [ + + ]: 20411 : if (isNull)
1435 : 8021 : *p_argnames = NULL;
1436 : : else
1437 : : {
1404 peter@eisentraut.org 1438 : 12390 : deconstruct_array_builtin(DatumGetArrayTypeP(proargnames), TEXTOID,
1439 : : &elems, NULL, &nelems);
7433 tgl@sss.pgh.pa.us 1440 [ - + ]: 12390 : if (nelems != numargs) /* should not happen */
7433 tgl@sss.pgh.pa.us 1441 [ # # ]:UBC 0 : elog(ERROR, "proargnames must have the same number of elements as the function has arguments");
146 michael@paquier.xyz 1442 :GNC 12390 : *p_argnames = palloc_array(char *, numargs);
7433 tgl@sss.pgh.pa.us 1443 [ + + ]:CBC 71731 : for (i = 0; i < numargs; i++)
6615 1444 : 59341 : (*p_argnames)[i] = TextDatumGetCString(elems[i]);
1445 : : }
1446 : :
1447 : : /* Get argument modes, if available */
7433 1448 : 20411 : proargmodes = SysCacheGetAttr(PROCOID, procTup,
1449 : : Anum_pg_proc_proargmodes,
1450 : : &isNull);
1451 [ + + ]: 20411 : if (isNull)
1452 : 15688 : *p_argmodes = NULL;
1453 : : else
1454 : : {
1455 : 4723 : arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */
1456 [ + - ]: 4723 : if (ARR_NDIM(arr) != 1 ||
1457 [ + - ]: 4723 : ARR_DIMS(arr)[0] != numargs ||
1458 [ + - ]: 4723 : ARR_HASNULL(arr) ||
1459 [ - + ]: 4723 : ARR_ELEMTYPE(arr) != CHAROID)
1989 alvherre@alvh.no-ip. 1460 [ # # ]:UBC 0 : elog(ERROR, "proargmodes is not a 1-D char array of length %d or it contains nulls",
1461 : : numargs);
7433 tgl@sss.pgh.pa.us 1462 :CBC 4723 : *p_argmodes = (char *) palloc(numargs * sizeof(char));
1463 [ - + ]: 4723 : memcpy(*p_argmodes, ARR_DATA_PTR(arr),
1464 : : numargs * sizeof(char));
1465 : : }
1466 : :
1467 : 20411 : return numargs;
1468 : : }
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
4027 peter_e@gmx.net 1478 : 123 : get_func_trftypes(HeapTuple procTup,
1479 : : Oid **p_trftypes)
1480 : : {
1481 : : Datum protrftypes;
1482 : : ArrayType *arr;
1483 : : int nelems;
1484 : : bool isNull;
1485 : :
1486 : 123 : protrftypes = SysCacheGetAttr(PROCOID, procTup,
1487 : : Anum_pg_proc_protrftypes,
1488 : : &isNull);
1489 [ + + ]: 123 : if (!isNull)
1490 : : {
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 : : */
4000 bruce@momjian.us 1497 : 3 : arr = DatumGetArrayTypeP(protrftypes); /* ensure not toasted */
4027 peter_e@gmx.net 1498 : 3 : nelems = ARR_DIMS(arr)[0];
1499 [ + - + - ]: 3 : if (ARR_NDIM(arr) != 1 ||
1500 : 3 : nelems < 0 ||
1501 [ + - ]: 3 : ARR_HASNULL(arr) ||
1502 [ - + ]: 3 : ARR_ELEMTYPE(arr) != OIDOID)
1989 alvherre@alvh.no-ip. 1503 [ # # ]:UBC 0 : elog(ERROR, "protrftypes is not a 1-D Oid array or it contains nulls");
4027 peter_e@gmx.net 1504 :CBC 3 : *p_trftypes = (Oid *) palloc(nelems * sizeof(Oid));
1505 [ - + ]: 3 : memcpy(*p_trftypes, ARR_DATA_PTR(arr),
1506 : : nelems * sizeof(Oid));
1507 : :
1508 : 3 : return nelems;
1509 : : }
1510 : : else
1511 : 120 : return 0;
1512 : : }
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
1790 tgl@sss.pgh.pa.us 1525 : 4801 : get_func_input_arg_names(Datum proargnames, Datum proargmodes,
1526 : : char ***arg_names)
1527 : : {
1528 : : ArrayType *arr;
1529 : : int numargs;
1530 : : Datum *argnames;
1531 : : char *argmodes;
1532 : : char **inargnames;
1533 : : int numinargs;
1534 : : int i;
1535 : :
1536 : : /* Do nothing if null proargnames */
6053 1537 [ + + ]: 4801 : if (proargnames == PointerGetDatum(NULL))
1538 : : {
1539 : 2882 : *arg_names = NULL;
1540 : 2882 : return 0;
1541 : : }
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 : : */
3240 1548 : 1919 : arr = DatumGetArrayTypeP(proargnames); /* ensure not toasted */
6053 1549 [ + - ]: 1919 : if (ARR_NDIM(arr) != 1 ||
1550 [ + - ]: 1919 : ARR_HASNULL(arr) ||
1551 [ - + ]: 1919 : ARR_ELEMTYPE(arr) != TEXTOID)
1989 alvherre@alvh.no-ip. 1552 [ # # ]:UBC 0 : elog(ERROR, "proargnames is not a 1-D text array or it contains nulls");
1404 peter@eisentraut.org 1553 :CBC 1919 : deconstruct_array_builtin(arr, TEXTOID, &argnames, NULL, &numargs);
6053 tgl@sss.pgh.pa.us 1554 [ + + ]: 1919 : if (proargmodes != PointerGetDatum(NULL))
1555 : : {
1556 : 757 : arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */
1557 [ + - ]: 757 : if (ARR_NDIM(arr) != 1 ||
1558 [ + - ]: 757 : ARR_DIMS(arr)[0] != numargs ||
1559 [ + - ]: 757 : ARR_HASNULL(arr) ||
1560 [ - + ]: 757 : ARR_ELEMTYPE(arr) != CHAROID)
1989 alvherre@alvh.no-ip. 1561 [ # # ]:UBC 0 : elog(ERROR, "proargmodes is not a 1-D char array of length %d or it contains nulls",
1562 : : numargs);
6053 tgl@sss.pgh.pa.us 1563 [ - + ]:CBC 757 : argmodes = (char *) ARR_DATA_PTR(arr);
1564 : : }
1565 : : else
1566 : 1162 : argmodes = NULL;
1567 : :
1568 : : /* zero elements probably shouldn't happen, but handle it gracefully */
1569 [ - + ]: 1919 : if (numargs <= 0)
1570 : : {
6053 tgl@sss.pgh.pa.us 1571 :UBC 0 : *arg_names = NULL;
1572 : 0 : return 0;
1573 : : }
1574 : :
1575 : : /* extract input-argument names */
6053 tgl@sss.pgh.pa.us 1576 :CBC 1919 : inargnames = (char **) palloc(numargs * sizeof(char *));
1577 : 1919 : numinargs = 0;
1578 [ + + ]: 7575 : for (i = 0; i < numargs; i++)
1579 : : {
1580 [ + + ]: 5656 : if (argmodes == NULL ||
1581 [ + + ]: 3611 : argmodes[i] == PROARGMODE_IN ||
1582 [ + + ]: 2650 : argmodes[i] == PROARGMODE_INOUT ||
1583 [ + + ]: 2566 : argmodes[i] == PROARGMODE_VARIADIC)
1584 : : {
1585 : 3165 : char *pname = TextDatumGetCString(argnames[i]);
1586 : :
1587 [ + + ]: 3165 : if (pname[0] != '\0')
1588 : 3099 : inargnames[numinargs] = pname;
1589 : : else
1590 : 66 : inargnames[numinargs] = NULL;
1591 : 3165 : numinargs++;
1592 : : }
1593 : : }
1594 : :
1595 : 1919 : *arg_names = inargnames;
1596 : 1919 : return numinargs;
1597 : : }
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 *
7516 1610 : 15350 : get_func_result_name(Oid functionId)
1611 : : {
1612 : : char *result;
1613 : : HeapTuple procTuple;
1614 : : Datum proargmodes;
1615 : : Datum proargnames;
1616 : : ArrayType *arr;
1617 : : int numargs;
1618 : : char *argmodes;
1619 : : Datum *argnames;
1620 : : int numoutargs;
1621 : : int nargnames;
1622 : : int i;
1623 : :
1624 : : /* First fetch the function's pg_proc row */
5924 rhaas@postgresql.org 1625 : 15350 : procTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionId));
7516 tgl@sss.pgh.pa.us 1626 [ - + ]: 15350 : if (!HeapTupleIsValid(procTuple))
7516 tgl@sss.pgh.pa.us 1627 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for function %u", functionId);
1628 : :
1629 : : /* If there are no named OUT parameters, return NULL */
2960 andrew@dunslane.net 1630 [ + + + + ]:CBC 18228 : if (heap_attisnull(procTuple, Anum_pg_proc_proargmodes, NULL) ||
1631 : 2878 : heap_attisnull(procTuple, Anum_pg_proc_proargnames, NULL))
7516 tgl@sss.pgh.pa.us 1632 : 12519 : result = NULL;
1633 : : else
1634 : : {
1635 : : /* Get the data out of the tuple */
1137 dgustafsson@postgres 1636 : 2831 : proargmodes = SysCacheGetAttrNotNull(PROCOID, procTuple,
1637 : : Anum_pg_proc_proargmodes);
1638 : 2831 : proargnames = SysCacheGetAttrNotNull(PROCOID, procTuple,
1639 : : 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 : : */
7507 bruce@momjian.us 1647 : 2831 : arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */
7516 tgl@sss.pgh.pa.us 1648 : 2831 : numargs = ARR_DIMS(arr)[0];
1649 [ + - + - ]: 2831 : if (ARR_NDIM(arr) != 1 ||
1650 : 2831 : numargs < 0 ||
7474 1651 [ + - ]: 2831 : ARR_HASNULL(arr) ||
7516 1652 [ - + ]: 2831 : ARR_ELEMTYPE(arr) != CHAROID)
1989 alvherre@alvh.no-ip. 1653 [ # # ]:UBC 0 : elog(ERROR, "proargmodes is not a 1-D char array or it contains nulls");
7516 tgl@sss.pgh.pa.us 1654 [ - + ]:CBC 2831 : argmodes = (char *) ARR_DATA_PTR(arr);
7507 bruce@momjian.us 1655 : 2831 : arr = DatumGetArrayTypeP(proargnames); /* ensure not toasted */
7516 tgl@sss.pgh.pa.us 1656 [ + - ]: 2831 : if (ARR_NDIM(arr) != 1 ||
1657 [ + - ]: 2831 : ARR_DIMS(arr)[0] != numargs ||
7474 1658 [ + - ]: 2831 : ARR_HASNULL(arr) ||
7516 1659 [ - + ]: 2831 : ARR_ELEMTYPE(arr) != TEXTOID)
1989 alvherre@alvh.no-ip. 1660 [ # # ]:UBC 0 : elog(ERROR, "proargnames is not a 1-D text array of length %d or it contains nulls",
1661 : : numargs);
1404 peter@eisentraut.org 1662 :CBC 2831 : deconstruct_array_builtin(arr, TEXTOID, &argnames, NULL, &nargnames);
7516 tgl@sss.pgh.pa.us 1663 [ - + ]: 2831 : Assert(nargnames == numargs);
1664 : :
1665 : : /* scan for output argument(s) */
1666 : 2831 : result = NULL;
1667 : 2831 : numoutargs = 0;
1668 [ + + ]: 6653 : for (i = 0; i < numargs; i++)
1669 : : {
6502 1670 [ + + ]: 3822 : if (argmodes[i] == PROARGMODE_IN ||
1671 [ + + ]: 2831 : argmodes[i] == PROARGMODE_VARIADIC)
7516 1672 : 2818 : continue;
1673 [ + + + + : 1004 : Assert(argmodes[i] == PROARGMODE_OUT ||
- + ]
1674 : : argmodes[i] == PROARGMODE_INOUT ||
1675 : : argmodes[i] == PROARGMODE_TABLE);
1676 [ - + ]: 1004 : if (++numoutargs > 1)
1677 : : {
1678 : : /* multiple out args, so forget it */
7516 tgl@sss.pgh.pa.us 1679 :UBC 0 : result = NULL;
1680 : 0 : break;
1681 : : }
6615 tgl@sss.pgh.pa.us 1682 :CBC 1004 : result = TextDatumGetCString(argnames[i]);
7516 1683 [ + - - + ]: 1004 : if (result == NULL || result[0] == '\0')
1684 : : {
1685 : : /* Parameter is not named, so forget it */
7516 tgl@sss.pgh.pa.us 1686 :UBC 0 : result = NULL;
1687 : 0 : break;
1688 : : }
1689 : : }
1690 : : }
1691 : :
7516 tgl@sss.pgh.pa.us 1692 :CBC 15350 : ReleaseSysCache(procTuple);
1693 : :
1694 : 15350 : return result;
1695 : : }
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
7705 1708 : 308543 : build_function_result_tupdesc_t(HeapTuple procTuple)
1709 : : {
1710 : 308543 : Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(procTuple);
1711 : : Datum proallargtypes;
1712 : : Datum proargmodes;
1713 : : Datum proargnames;
1714 : : bool isnull;
1715 : :
1716 : : /* Return NULL if the function isn't declared to return RECORD */
1717 [ + + ]: 308543 : if (procform->prorettype != RECORDOID)
1718 : 87071 : return NULL;
1719 : :
1720 : : /* If there are no OUT parameters, return NULL */
2960 andrew@dunslane.net 1721 [ + + - + ]: 442047 : if (heap_attisnull(procTuple, Anum_pg_proc_proallargtypes, NULL) ||
1722 : 220575 : heap_attisnull(procTuple, Anum_pg_proc_proargmodes, NULL))
7705 tgl@sss.pgh.pa.us 1723 : 897 : return NULL;
1724 : :
1725 : : /* Get the data out of the tuple */
1137 dgustafsson@postgres 1726 : 220575 : proallargtypes = SysCacheGetAttrNotNull(PROCOID, procTuple,
1727 : : Anum_pg_proc_proallargtypes);
1728 : 220575 : proargmodes = SysCacheGetAttrNotNull(PROCOID, procTuple,
1729 : : Anum_pg_proc_proargmodes);
7705 tgl@sss.pgh.pa.us 1730 : 220575 : proargnames = SysCacheGetAttr(PROCOID, procTuple,
1731 : : Anum_pg_proc_proargnames,
1732 : : &isnull);
1733 [ + + ]: 220575 : if (isnull)
7507 bruce@momjian.us 1734 : 81 : proargnames = PointerGetDatum(NULL); /* just to be sure */
1735 : :
2974 peter_e@gmx.net 1736 : 220575 : return build_function_result_tupdesc_d(procform->prokind,
1737 : : proallargtypes,
1738 : : proargmodes,
1739 : : proargnames);
1740 : : }
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 : 220742 : build_function_result_tupdesc_d(char prokind,
1755 : : Datum proallargtypes,
1756 : : Datum proargmodes,
1757 : : Datum proargnames)
1758 : : {
1759 : : TupleDesc desc;
1760 : : ArrayType *arr;
1761 : : int numargs;
1762 : : Oid *argtypes;
1763 : : char *argmodes;
7705 tgl@sss.pgh.pa.us 1764 : 220742 : Datum *argnames = NULL;
1765 : : Oid *outargtypes;
1766 : : char **outargnames;
1767 : : int numoutargs;
1768 : : int nargnames;
1769 : : int i;
1770 : :
1771 : : /* Can't have output args if columns are null */
1772 [ + + - + ]: 220742 : if (proallargtypes == PointerGetDatum(NULL) ||
7705 tgl@sss.pgh.pa.us 1773 :ECB (163196) : proargmodes == PointerGetDatum(NULL))
7705 tgl@sss.pgh.pa.us 1774 :CBC 14 : 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 : 220728 : arr = DatumGetArrayTypeP(proallargtypes); /* ensure not toasted */
1782 : 220728 : numargs = ARR_DIMS(arr)[0];
1783 [ + - + - ]: 220728 : if (ARR_NDIM(arr) != 1 ||
1784 : 220728 : numargs < 0 ||
7474 1785 [ + - ]: 220728 : ARR_HASNULL(arr) ||
7705 1786 [ - + ]: 220728 : ARR_ELEMTYPE(arr) != OIDOID)
1989 alvherre@alvh.no-ip. 1787 [ # # ]:UBC 0 : elog(ERROR, "proallargtypes is not a 1-D Oid array or it contains nulls");
7705 tgl@sss.pgh.pa.us 1788 [ - + ]:CBC 220728 : argtypes = (Oid *) ARR_DATA_PTR(arr);
3240 1789 : 220728 : arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */
7705 1790 [ + - ]: 220728 : if (ARR_NDIM(arr) != 1 ||
1791 [ + - ]: 220728 : ARR_DIMS(arr)[0] != numargs ||
7474 1792 [ + - ]: 220728 : ARR_HASNULL(arr) ||
7705 1793 [ - + ]: 220728 : ARR_ELEMTYPE(arr) != CHAROID)
1989 alvherre@alvh.no-ip. 1794 [ # # ]:UBC 0 : elog(ERROR, "proargmodes is not a 1-D char array of length %d or it contains nulls",
1795 : : numargs);
7705 tgl@sss.pgh.pa.us 1796 [ - + ]:CBC 220728 : argmodes = (char *) ARR_DATA_PTR(arr);
1797 [ + + ]: 220728 : if (proargnames != PointerGetDatum(NULL))
1798 : : {
1799 : 220639 : arr = DatumGetArrayTypeP(proargnames); /* ensure not toasted */
1800 [ + - ]: 220639 : if (ARR_NDIM(arr) != 1 ||
1801 [ + - ]: 220639 : ARR_DIMS(arr)[0] != numargs ||
7474 1802 [ + - ]: 220639 : ARR_HASNULL(arr) ||
7705 1803 [ - + ]: 220639 : ARR_ELEMTYPE(arr) != TEXTOID)
1989 alvherre@alvh.no-ip. 1804 [ # # ]:UBC 0 : elog(ERROR, "proargnames is not a 1-D text array of length %d or it contains nulls",
1805 : : numargs);
1404 peter@eisentraut.org 1806 :CBC 220639 : deconstruct_array_builtin(arr, TEXTOID, &argnames, NULL, &nargnames);
7705 tgl@sss.pgh.pa.us 1807 [ - + ]: 220639 : Assert(nargnames == numargs);
1808 : : }
1809 : :
1810 : : /* zero elements probably shouldn't happen, but handle it gracefully */
1811 [ - + ]: 220728 : if (numargs <= 0)
7705 tgl@sss.pgh.pa.us 1812 :UBC 0 : return NULL;
1813 : :
1814 : : /* extract output-argument types and names */
7705 tgl@sss.pgh.pa.us 1815 :CBC 220728 : outargtypes = (Oid *) palloc(numargs * sizeof(Oid));
1816 : 220728 : outargnames = (char **) palloc(numargs * sizeof(char *));
1817 : 220728 : numoutargs = 0;
1818 [ + + ]: 3918880 : for (i = 0; i < numargs; i++)
1819 : : {
1820 : : char *pname;
1821 : :
6502 1822 [ + + ]: 3698152 : if (argmodes[i] == PROARGMODE_IN ||
1823 [ + + ]: 3416783 : argmodes[i] == PROARGMODE_VARIADIC)
7705 1824 : 283114 : continue;
1825 [ + + + + : 3415038 : Assert(argmodes[i] == PROARGMODE_OUT ||
- + ]
1826 : : argmodes[i] == PROARGMODE_INOUT ||
1827 : : argmodes[i] == PROARGMODE_TABLE);
1828 : 3415038 : outargtypes[numoutargs] = argtypes[i];
1829 [ + + ]: 3415038 : if (argnames)
6615 1830 : 3414860 : pname = TextDatumGetCString(argnames[i]);
1831 : : else
7705 1832 : 178 : pname = NULL;
1833 [ + + + + ]: 3415038 : if (pname == NULL || pname[0] == '\0')
1834 : : {
1835 : : /* Parameter is not named, so gin up a column name */
4502 peter_e@gmx.net 1836 : 356 : pname = psprintf("column%d", numoutargs + 1);
1837 : : }
7705 tgl@sss.pgh.pa.us 1838 : 3415038 : outargnames[numoutargs] = pname;
1839 : 3415038 : numoutargs++;
1840 : : }
1841 : :
1842 : : /*
1843 : : * If there is no output argument, or only one, the function does not
1844 : : * return tuples.
1845 : : */
2974 peter_e@gmx.net 1846 [ + + - + ]: 220728 : if (numoutargs < 2 && prokind != PROKIND_PROCEDURE)
7705 tgl@sss.pgh.pa.us 1847 :UBC 0 : return NULL;
1848 : :
2723 andres@anarazel.de 1849 :CBC 220728 : desc = CreateTemplateTupleDesc(numoutargs);
7705 tgl@sss.pgh.pa.us 1850 [ + + ]: 3635766 : for (i = 0; i < numoutargs; i++)
1851 : : {
7507 bruce@momjian.us 1852 : 3415038 : TupleDescInitEntry(desc, i + 1,
7705 tgl@sss.pgh.pa.us 1853 : 3415038 : outargnames[i],
1854 : 3415038 : outargtypes[i],
1855 : : -1,
1856 : : 0);
1857 : : }
1858 : :
50 drowley@postgresql.o 1859 :GNC 220728 : TupleDescFinalize(desc);
1860 : :
7705 tgl@sss.pgh.pa.us 1861 :CBC 220728 : return desc;
1862 : : }
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
7705 tgl@sss.pgh.pa.us 1875 :UBC 0 : RelationNameGetTupleDesc(const char *relname)
1876 : : {
1877 : : RangeVar *relvar;
1878 : : Relation rel;
1879 : : TupleDesc tupdesc;
1880 : : List *relname_list;
1881 : :
1882 : : /* Open relation and copy the tuple description */
1225 1883 : 0 : relname_list = stringToQualifiedNameList(relname, NULL);
7705 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 : : }
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 : 0 : TypeGetTupleDesc(Oid typeoid, List *colaliases)
1909 : : {
1910 : : Oid base_typeoid;
3113 1911 : 0 : TypeFuncClass functypclass = get_type_func_class(typeoid, &base_typeoid);
7705 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 : : {
1922 : : /* Composite data type, e.g. a table's row type */
3113 1923 : 0 : tupdesc = lookup_rowtype_tupdesc_copy(base_typeoid, -1);
1924 : :
7705 1925 [ # # ]: 0 : if (colaliases != NIL)
1926 : : {
1927 : 0 : int natts = tupdesc->natts;
1928 : : 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 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
1934 : : 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 : : {
1939 : 0 : char *label = strVal(list_nth(colaliases, varattno));
3180 andres@anarazel.de 1940 : 0 : Form_pg_attribute attr = TupleDescAttr(tupdesc, varattno);
1941 : :
7705 tgl@sss.pgh.pa.us 1942 [ # # ]: 0 : if (label != NULL)
3180 andres@anarazel.de 1943 : 0 : namestrcpy(&(attr->attname), label);
1944 : : }
1945 : :
1946 : : /* The tuple type is now an anonymous record type */
7705 tgl@sss.pgh.pa.us 1947 : 0 : tupdesc->tdtypeid = RECORDOID;
1948 : 0 : tupdesc->tdtypmod = -1;
1949 : : }
1950 : : }
1951 [ # # ]: 0 : else if (functypclass == TYPEFUNC_SCALAR)
1952 : : {
1953 : : /* Base data type, i.e. scalar */
1954 : : char *attname;
1955 : :
1956 : : /* the alias list is required for base types */
1957 [ # # ]: 0 : if (colaliases == NIL)
1958 [ # # ]: 0 : ereport(ERROR,
1959 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
1960 : : 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 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
1966 : : errmsg("number of aliases does not match number of columns")));
1967 : :
1968 : : /* OK, get the column alias */
1969 : 0 : attname = strVal(linitial(colaliases));
1970 : :
2723 andres@anarazel.de 1971 : 0 : tupdesc = CreateTemplateTupleDesc(1);
7705 tgl@sss.pgh.pa.us 1972 : 0 : TupleDescInitEntry(tupdesc,
1973 : : (AttrNumber) 1,
1974 : : attname,
1975 : : typeoid,
1976 : : -1,
1977 : : 0);
50 drowley@postgresql.o 1978 :UNC 0 : TupleDescFinalize(tupdesc);
1979 : : }
7705 tgl@sss.pgh.pa.us 1980 [ # # ]:UBC 0 : else if (functypclass == TYPEFUNC_RECORD)
1981 : : {
1982 : : /* XXX can't support this because typmod wasn't passed in ... */
1983 [ # # ]: 0 : ereport(ERROR,
1984 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
1985 : : errmsg("could not determine row description for function returning record")));
1986 : : }
1987 : : else
1988 : : {
1989 : : /* crummy error message, but parser should have caught this */
1990 [ # # ]: 0 : elog(ERROR, "function in FROM has unsupported return type");
1991 : : }
1992 : :
1993 : 0 : return tupdesc;
1994 : : }
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
3114 andrew@dunslane.net 2011 :CBC 2911 : extract_variadic_args(FunctionCallInfo fcinfo, int variadic_start,
2012 : : bool convert_unknown, Datum **args, Oid **types,
2013 : : bool **nulls)
2014 : : {
2015 : 2911 : bool variadic = get_fn_expr_variadic(fcinfo->flinfo);
2016 : : Datum *args_res;
2017 : : bool *nulls_res;
2018 : : Oid *types_res;
2019 : : int nargs,
2020 : : i;
2021 : :
2022 : 2911 : *args = NULL;
2023 : 2911 : *types = NULL;
2024 : 2911 : *nulls = NULL;
2025 : :
2026 [ + + ]: 2911 : if (variadic)
2027 : : {
2028 : : ArrayType *array_in;
2029 : : Oid element_type;
2030 : : bool typbyval;
2031 : : char typalign;
2032 : : int16 typlen;
2033 : :
2034 [ - + ]: 127 : Assert(PG_NARGS() == variadic_start + 1);
2035 : :
2036 [ + + ]: 127 : if (PG_ARGISNULL(variadic_start))
2037 : 16 : return -1;
2038 : :
2039 : 111 : array_in = PG_GETARG_ARRAYTYPE_P(variadic_start);
2040 : 111 : element_type = ARR_ELEMTYPE(array_in);
2041 : :
2042 : 111 : get_typlenbyvalalign(element_type,
2043 : : &typlen, &typbyval, &typalign);
2044 : 111 : deconstruct_array(array_in, element_type, typlen, typbyval,
2045 : : typalign, &args_res, &nulls_res,
2046 : : &nargs);
2047 : :
2048 : : /* All the elements of the array have the same type */
2049 : 111 : types_res = (Oid *) palloc0(nargs * sizeof(Oid));
2050 [ + + ]: 449 : for (i = 0; i < nargs; i++)
2051 : 338 : types_res[i] = element_type;
2052 : : }
2053 : : else
2054 : : {
2055 : 2784 : nargs = PG_NARGS() - variadic_start;
3113 tgl@sss.pgh.pa.us 2056 [ - + ]: 2784 : Assert(nargs > 0);
3114 andrew@dunslane.net 2057 : 2784 : nulls_res = (bool *) palloc0(nargs * sizeof(bool));
2058 : 2784 : args_res = (Datum *) palloc0(nargs * sizeof(Datum));
2059 : 2784 : types_res = (Oid *) palloc0(nargs * sizeof(Oid));
2060 : :
2061 [ + + ]: 42713 : for (i = 0; i < nargs; i++)
2062 : : {
2063 : 39929 : nulls_res[i] = PG_ARGISNULL(i + variadic_start);
2064 : 39929 : types_res[i] = get_fn_expr_argtype(fcinfo->flinfo,
2065 : : 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 [ + - ]: 39929 : if (convert_unknown &&
2075 [ + + + - ]: 65947 : types_res[i] == UNKNOWNOID &&
2076 : 26018 : get_fn_expr_arg_stable(fcinfo->flinfo, i + variadic_start))
2077 : : {
2078 : 26018 : types_res[i] = TEXTOID;
2079 : :
2080 [ + + ]: 26018 : if (PG_ARGISNULL(i + variadic_start))
2081 : 80 : args_res[i] = (Datum) 0;
2082 : : else
2083 : 25938 : args_res[i] =
2084 : 25938 : CStringGetTextDatum(PG_GETARG_POINTER(i + variadic_start));
2085 : : }
2086 : : else
2087 : : {
2088 : : /* no conversion needed, just take the datum as given */
2089 : 13911 : args_res[i] = PG_GETARG_DATUM(i + variadic_start);
2090 : : }
2091 : :
2092 [ + - + - ]: 39929 : if (!OidIsValid(types_res[i]) ||
2093 [ - + ]: 39929 : (convert_unknown && types_res[i] == UNKNOWNOID))
3114 andrew@dunslane.net 2094 [ # # ]:UBC 0 : ereport(ERROR,
2095 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2096 : : errmsg("could not determine data type for argument %d",
2097 : : i + 1)));
2098 : : }
2099 : : }
2100 : :
2101 : : /* Fill in results */
3114 andrew@dunslane.net 2102 :CBC 2895 : *args = args_res;
2103 : 2895 : *nulls = nulls_res;
2104 : 2895 : *types = types_res;
2105 : :
2106 : 2895 : return nargs;
2107 : : }
|