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