Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * execSRF.c
4 : : * Routines implementing the API for set-returning functions
5 : : *
6 : : * This file serves nodeFunctionscan.c and nodeProjectSet.c, providing
7 : : * common code for calling set-returning functions according to the
8 : : * ReturnSetInfo API.
9 : : *
10 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
11 : : * Portions Copyright (c) 1994, Regents of the University of California
12 : : *
13 : : *
14 : : * IDENTIFICATION
15 : : * src/backend/executor/execSRF.c
16 : : *
17 : : *-------------------------------------------------------------------------
18 : : */
19 : : #include "postgres.h"
20 : :
21 : : #include "access/htup_details.h"
22 : : #include "catalog/objectaccess.h"
23 : : #include "catalog/pg_proc.h"
24 : : #include "funcapi.h"
25 : : #include "miscadmin.h"
26 : : #include "nodes/nodeFuncs.h"
27 : : #include "parser/parse_coerce.h"
28 : : #include "pgstat.h"
29 : : #include "utils/acl.h"
30 : : #include "utils/builtins.h"
31 : : #include "utils/lsyscache.h"
32 : : #include "utils/memutils.h"
33 : : #include "utils/tuplestore.h"
34 : : #include "utils/typcache.h"
35 : :
36 : :
37 : : /* static function decls */
38 : : static void init_sexpr(Oid foid, Oid input_collation, Expr *node,
39 : : SetExprState *sexpr, PlanState *parent,
40 : : MemoryContext sexprCxt, bool allowSRF, bool needDescForSRF);
41 : : static void ShutdownSetExpr(Datum arg);
42 : : static void ExecEvalFuncArgs(FunctionCallInfo fcinfo,
43 : : List *argList, ExprContext *econtext);
44 : : static void ExecPrepareTuplestoreResult(SetExprState *sexpr,
45 : : ExprContext *econtext,
46 : : Tuplestorestate *resultStore,
47 : : TupleDesc resultDesc);
48 : : static void tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc);
49 : :
50 : :
51 : : /*
52 : : * Prepare function call in FROM (ROWS FROM) for execution.
53 : : *
54 : : * This is used by nodeFunctionscan.c.
55 : : */
56 : : SetExprState *
3339 andres@anarazel.de 57 :CBC 44602 : ExecInitTableFunctionResult(Expr *expr,
58 : : ExprContext *econtext, PlanState *parent)
59 : : {
60 : 44602 : SetExprState *state = makeNode(SetExprState);
61 : :
62 : 44602 : state->funcReturnsSet = false;
63 : 44602 : state->expr = expr;
64 : 44602 : state->func.fn_oid = InvalidOid;
65 : :
66 : : /*
67 : : * Normally the passed expression tree will be a FuncExpr, since the
68 : : * grammar only allows a function call at the top level of a table
69 : : * function reference. However, if the function doesn't return set then
70 : : * the planner might have replaced the function call via constant-folding
71 : : * or inlining. So if we see any other kind of expression node, execute
72 : : * it via the general ExecEvalExpr() code. That code path will not
73 : : * support set-returning functions buried in the expression, though.
74 : : */
75 [ + + ]: 44602 : if (IsA(expr, FuncExpr))
76 : : {
77 : 44526 : FuncExpr *func = (FuncExpr *) expr;
78 : :
79 : 44526 : state->funcReturnsSet = func->funcretset;
80 : 44526 : state->args = ExecInitExprList(func->args, parent);
81 : :
3304 tgl@sss.pgh.pa.us 82 : 44526 : init_sexpr(func->funcid, func->inputcollid, expr, state, parent,
3339 andres@anarazel.de 83 : 44526 : econtext->ecxt_per_query_memory, func->funcretset, false);
84 : : }
85 : : else
86 : : {
87 : 76 : state->elidedFuncState = ExecInitExpr(expr, parent);
88 : : }
89 : :
90 : 44598 : return state;
91 : : }
92 : :
93 : : /*
94 : : * ExecMakeTableFunctionResult
95 : : *
96 : : * Evaluate a table function, producing a materialized result in a Tuplestore
97 : : * object.
98 : : *
99 : : * This is used by nodeFunctionscan.c.
100 : : */
101 : : Tuplestorestate *
102 : 101800 : ExecMakeTableFunctionResult(SetExprState *setexpr,
103 : : ExprContext *econtext,
104 : : MemoryContext argContext,
105 : : TupleDesc expectedDesc,
106 : : bool randomAccess)
107 : : {
108 : 101800 : Tuplestorestate *tupstore = NULL;
109 : 101800 : TupleDesc tupdesc = NULL;
110 : : Oid funcrettype;
111 : : bool returnsTuple;
112 : 101800 : bool returnsSet = false;
113 : : FunctionCallInfo fcinfo;
114 : : PgStat_FunctionCallUsage fcusage;
115 : : ReturnSetInfo rsinfo;
116 : : HeapTupleData tmptup;
117 : : MemoryContext callerContext;
118 : 101800 : bool first_time = true;
119 : :
120 : : /*
121 : : * Execute per-tablefunc actions in appropriate context.
122 : : *
123 : : * The FunctionCallInfo needs to live across all the calls to a
124 : : * ValuePerCall function, so it can't be allocated in the per-tuple
125 : : * context. Similarly, the function arguments need to be evaluated in a
126 : : * context that is longer lived than the per-tuple context: The argument
127 : : * values would otherwise disappear when we reset that context in the
128 : : * inner loop. As the caller's CurrentMemoryContext is typically a
129 : : * query-lifespan context, we don't want to leak memory there. We require
130 : : * the caller to pass a separate memory context that can be used for this,
131 : : * and can be reset each time through to avoid bloat.
132 : : */
2204 133 : 101800 : MemoryContextReset(argContext);
134 : 101800 : callerContext = MemoryContextSwitchTo(argContext);
135 : :
3339 136 : 101800 : funcrettype = exprType((Node *) setexpr->expr);
137 : :
138 : 101800 : returnsTuple = type_is_rowtype(funcrettype);
139 : :
140 : : /*
141 : : * Prepare a resultinfo node for communication. We always do this even if
142 : : * not expecting a set result, so that we can pass expectedDesc. In the
143 : : * generic-expression case, the expression doesn't actually get to see the
144 : : * resultinfo, but set it up anyway because we use some of the fields as
145 : : * our own state variables.
146 : : */
147 : 101800 : rsinfo.type = T_ReturnSetInfo;
148 : 101800 : rsinfo.econtext = econtext;
149 : 101800 : rsinfo.expectedDesc = expectedDesc;
150 : 101800 : rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize | SFRM_Materialize_Preferred);
151 [ + + ]: 101800 : if (randomAccess)
152 : 63 : rsinfo.allowedModes |= (int) SFRM_Materialize_Random;
153 : 101800 : rsinfo.returnMode = SFRM_ValuePerCall;
154 : : /* isDone is filled below */
155 : 101800 : rsinfo.setResult = NULL;
156 : 101800 : rsinfo.setDesc = NULL;
157 : :
2656 158 : 101800 : fcinfo = palloc(SizeForFunctionCallInfo(list_length(setexpr->args)));
159 : :
160 : : /*
161 : : * Normally the passed expression tree will be a SetExprState, since the
162 : : * grammar only allows a function call at the top level of a table
163 : : * function reference. However, if the function doesn't return set then
164 : : * the planner might have replaced the function call via constant-folding
165 : : * or inlining. So if we see any other kind of expression node, execute
166 : : * it via the general ExecEvalExpr() code; the only difference is that we
167 : : * don't get a chance to pass a special ReturnSetInfo to any functions
168 : : * buried in the expression.
169 : : */
3339 170 [ + + ]: 101800 : if (!setexpr->elidedFuncState)
171 : : {
172 : : /*
173 : : * This path is similar to ExecMakeFunctionResultSet.
174 : : */
175 : 101724 : returnsSet = setexpr->funcReturnsSet;
2656 176 : 101724 : InitFunctionCallInfoData(*fcinfo, &(setexpr->func),
177 : : list_length(setexpr->args),
178 : : setexpr->fcinfo->fncollation,
179 : : NULL, (Node *) &rsinfo);
180 : : /* evaluate the function's argument list */
2204 181 [ - + ]: 101724 : Assert(CurrentMemoryContext == argContext);
2656 182 : 101724 : ExecEvalFuncArgs(fcinfo, setexpr->args, econtext);
183 : :
184 : : /*
185 : : * If function is strict, and there are any NULL arguments, skip
186 : : * calling the function and act like it returned NULL (or an empty
187 : : * set, in the returns-set case).
188 : : */
3339 189 [ + + ]: 101716 : if (setexpr->func.fn_strict)
190 : : {
191 : : int i;
192 : :
2656 193 [ + + ]: 194675 : for (i = 0; i < fcinfo->nargs; i++)
194 : : {
195 [ + + ]: 132989 : if (fcinfo->args[i].isnull)
3339 196 : 27880 : goto no_function_result;
197 : : }
198 : : }
199 : : }
200 : : else
201 : : {
202 : : /* Treat setexpr as a generic expression */
2656 203 : 76 : InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
204 : : }
205 : :
206 : : /*
207 : : * Switch to short-lived context for calling the function or expression.
208 : : */
3339 209 : 73912 : MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
210 : :
211 : : /*
212 : : * Loop to handle the ValuePerCall protocol (which is also the same
213 : : * behavior needed in the generic ExecEvalExpr path).
214 : : */
215 : : for (;;)
216 : 10636657 : {
217 : : Datum result;
218 : :
219 [ - + ]: 10710569 : CHECK_FOR_INTERRUPTS();
220 : :
221 : : /*
222 : : * Reset per-tuple memory context before each call of the function or
223 : : * expression. This cleans up any local memory the function may leak
224 : : * when called.
225 : : */
226 : 10710569 : ResetExprContext(econtext);
227 : :
228 : : /* Call the function or expression one time */
229 [ + + ]: 10710569 : if (!setexpr->elidedFuncState)
230 : : {
2656 231 : 10710493 : pgstat_init_function_usage(fcinfo, &fcusage);
232 : :
233 : 10710493 : fcinfo->isnull = false;
3339 234 : 10710493 : rsinfo.isDone = ExprSingleResult;
2656 235 : 10710493 : result = FunctionCallInvoke(fcinfo);
236 : :
3339 237 : 10707773 : pgstat_end_function_usage(&fcusage,
238 : 10707773 : rsinfo.isDone != ExprMultipleResult);
239 : : }
240 : : else
241 : : {
242 : 76 : result =
2656 243 : 76 : ExecEvalExpr(setexpr->elidedFuncState, econtext, &fcinfo->isnull);
3339 244 : 76 : rsinfo.isDone = ExprSingleResult;
245 : : }
246 : :
247 : : /* Which protocol does function want to use? */
248 [ + + ]: 10707849 : if (rsinfo.returnMode == SFRM_ValuePerCall)
249 : : {
250 : : /*
251 : : * Check for end of result set.
252 : : */
253 [ + + ]: 10696809 : if (rsinfo.isDone == ExprEndResult)
254 : 71192 : break;
255 : :
256 : : /*
257 : : * If first time through, build tuplestore for result. For a
258 : : * scalar function result type, also make a suitable tupdesc.
259 : : */
260 [ + + ]: 10661068 : if (first_time)
261 : : {
262 : : MemoryContext oldcontext =
1082 tgl@sss.pgh.pa.us 263 : 59021 : MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
264 : :
3339 andres@anarazel.de 265 : 59021 : tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
266 : 59021 : rsinfo.setResult = tupstore;
267 [ + + ]: 59021 : if (!returnsTuple)
268 : : {
2723 269 : 31433 : tupdesc = CreateTemplateTupleDesc(1);
3339 270 : 31433 : TupleDescInitEntry(tupdesc,
271 : : (AttrNumber) 1,
272 : : "column",
273 : : funcrettype,
274 : : -1,
275 : : 0);
50 drowley@postgresql.o 276 :GNC 31433 : TupleDescFinalize(tupdesc);
3339 andres@anarazel.de 277 :CBC 31433 : rsinfo.setDesc = tupdesc;
278 : : }
279 : 59021 : MemoryContextSwitchTo(oldcontext);
280 : : }
281 : :
282 : : /*
283 : : * Store current resultset item.
284 : : */
285 [ + + ]: 10661068 : if (returnsTuple)
286 : : {
2656 287 [ + + ]: 1158691 : if (!fcinfo->isnull)
288 : : {
3339 289 : 1158641 : HeapTupleHeader td = DatumGetHeapTupleHeader(result);
290 : :
291 [ + + ]: 1158641 : if (tupdesc == NULL)
292 : : {
293 : : MemoryContext oldcontext =
1082 tgl@sss.pgh.pa.us 294 : 27554 : MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
295 : :
296 : : /*
297 : : * This is the first non-NULL result from the
298 : : * function. Use the type info embedded in the
299 : : * rowtype Datum to look up the needed tupdesc. Make
300 : : * a copy for the query.
301 : : */
3339 andres@anarazel.de 302 : 27554 : tupdesc = lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td),
303 : : HeapTupleHeaderGetTypMod(td));
304 : 27554 : rsinfo.setDesc = tupdesc;
305 : 27554 : MemoryContextSwitchTo(oldcontext);
306 : : }
307 : : else
308 : : {
309 : : /*
310 : : * Verify all later returned rows have same subtype;
311 : : * necessary in case the type is RECORD.
312 : : */
313 [ + - ]: 1131087 : if (HeapTupleHeaderGetTypeId(td) != tupdesc->tdtypeid ||
314 [ - + ]: 1131087 : HeapTupleHeaderGetTypMod(td) != tupdesc->tdtypmod)
3339 andres@anarazel.de 315 [ # # ]:UBC 0 : ereport(ERROR,
316 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
317 : : errmsg("rows returned by function are not all of the same row type")));
318 : : }
319 : :
320 : : /*
321 : : * tuplestore_puttuple needs a HeapTuple not a bare
322 : : * HeapTupleHeader, but it doesn't need all the fields.
323 : : */
3339 andres@anarazel.de 324 :CBC 1158641 : tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
325 : 1158641 : tmptup.t_data = td;
326 : :
327 : 1158641 : tuplestore_puttuple(tupstore, &tmptup);
328 : : }
329 : : else
330 : : {
331 : : /*
332 : : * NULL result from a tuple-returning function; expand it
333 : : * to a row of all nulls. We rely on the expectedDesc to
334 : : * form such rows. (Note: this would be problematic if
335 : : * tuplestore_putvalues saved the tdtypeid/tdtypmod from
336 : : * the provided descriptor, since that might not match
337 : : * what we get from the function itself. But it doesn't.)
338 : : */
339 : 50 : int natts = expectedDesc->natts;
340 : : bool *nullflags;
341 : :
342 : 50 : nullflags = (bool *) palloc(natts * sizeof(bool));
343 : 50 : memset(nullflags, true, natts * sizeof(bool));
344 : 50 : tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags);
345 : : }
346 : : }
347 : : else
348 : : {
349 : : /* Scalar-type case: just store the function result */
2656 350 : 9502377 : tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo->isnull);
351 : : }
352 : :
353 : : /*
354 : : * Are we done?
355 : : */
3339 356 [ + + ]: 10661068 : if (rsinfo.isDone != ExprMultipleResult)
357 : 24411 : break;
358 : :
359 : : /*
360 : : * Check that set-returning functions were properly declared.
361 : : * (Note: for historical reasons, we don't complain if a non-SRF
362 : : * returns ExprEndResult; that's treated as returning NULL.)
363 : : */
1884 tgl@sss.pgh.pa.us 364 [ - + ]: 10636657 : if (!returnsSet)
1884 tgl@sss.pgh.pa.us 365 [ # # ]:UBC 0 : ereport(ERROR,
366 : : (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
367 : : errmsg("table-function protocol for value-per-call mode was not followed")));
368 : : }
3339 andres@anarazel.de 369 [ + - ]:CBC 11040 : else if (rsinfo.returnMode == SFRM_Materialize)
370 : : {
371 : : /* check we're on the same page as the function author */
1884 tgl@sss.pgh.pa.us 372 [ + - + - : 11040 : if (!first_time || rsinfo.isDone != ExprSingleResult || !returnsSet)
- + ]
3339 andres@anarazel.de 373 [ # # ]:UBC 0 : ereport(ERROR,
374 : : (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
375 : : errmsg("table-function protocol for materialize mode was not followed")));
376 : : /* Done evaluating the set result */
3339 andres@anarazel.de 377 :CBC 11040 : break;
378 : : }
379 : : else
3339 andres@anarazel.de 380 [ # # ]:UBC 0 : ereport(ERROR,
381 : : (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
382 : : errmsg("unrecognized table-function returnMode: %d",
383 : : (int) rsinfo.returnMode)));
384 : :
3339 andres@anarazel.de 385 :CBC 10636657 : first_time = false;
386 : : }
387 : :
388 : 99072 : no_function_result:
389 : :
390 : : /*
391 : : * If we got nothing from the function (ie, an empty-set or NULL result),
392 : : * we have to create the tuplestore to return, and if it's a
393 : : * non-set-returning function then insert a single all-nulls row. As
394 : : * above, we depend on the expectedDesc to manufacture the dummy row.
395 : : */
396 [ + + ]: 99072 : if (rsinfo.setResult == NULL)
397 : : {
398 : : MemoryContext oldcontext =
1082 tgl@sss.pgh.pa.us 399 : 29028 : MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
400 : :
3339 andres@anarazel.de 401 : 29028 : tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
402 : 29028 : rsinfo.setResult = tupstore;
2204 403 : 29028 : MemoryContextSwitchTo(oldcontext);
404 : :
3339 405 [ + + ]: 29028 : if (!returnsSet)
406 : : {
407 : 12 : int natts = expectedDesc->natts;
408 : : bool *nullflags;
409 : :
410 : 12 : nullflags = (bool *) palloc(natts * sizeof(bool));
411 : 12 : memset(nullflags, true, natts * sizeof(bool));
412 : 12 : tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags);
413 : : }
414 : : }
415 : :
416 : : /*
417 : : * If function provided a tupdesc, cross-check it. We only really need to
418 : : * do this for functions returning RECORD, but might as well do it always.
419 : : */
420 [ + + ]: 99072 : if (rsinfo.setDesc)
421 : : {
422 : 70010 : tupledesc_match(expectedDesc, rsinfo.setDesc);
423 : :
424 : : /*
425 : : * If it is a dynamically-allocated TupleDesc, free it: it is
426 : : * typically allocated in a per-query context, so we must avoid
427 : : * leaking it across multiple usages.
428 : : */
429 [ + - ]: 69954 : if (rsinfo.setDesc->tdrefcount == -1)
430 : 69954 : FreeTupleDesc(rsinfo.setDesc);
431 : : }
432 : :
433 : 99016 : MemoryContextSwitchTo(callerContext);
434 : :
435 : : /* All done, pass back the tuplestore */
436 : 99016 : return rsinfo.setResult;
437 : : }
438 : :
439 : :
440 : : /*
441 : : * Prepare targetlist SRF function call for execution.
442 : : *
443 : : * This is used by nodeProjectSet.c.
444 : : */
445 : : SetExprState *
446 : 10385 : ExecInitFunctionResultSet(Expr *expr,
447 : : ExprContext *econtext, PlanState *parent)
448 : : {
449 : 10385 : SetExprState *state = makeNode(SetExprState);
450 : :
451 : 10385 : state->funcReturnsSet = true;
452 : 10385 : state->expr = expr;
453 : 10385 : state->func.fn_oid = InvalidOid;
454 : :
455 : : /*
456 : : * Initialize metadata. The expression node could be either a FuncExpr or
457 : : * an OpExpr.
458 : : */
459 [ + + ]: 10385 : if (IsA(expr, FuncExpr))
460 : : {
461 : 10381 : FuncExpr *func = (FuncExpr *) expr;
462 : :
463 : 10381 : state->args = ExecInitExprList(func->args, parent);
3304 tgl@sss.pgh.pa.us 464 : 10381 : init_sexpr(func->funcid, func->inputcollid, expr, state, parent,
465 : : econtext->ecxt_per_query_memory, true, true);
466 : : }
3339 andres@anarazel.de 467 [ + - ]: 4 : else if (IsA(expr, OpExpr))
468 : : {
469 : 4 : OpExpr *op = (OpExpr *) expr;
470 : :
471 : 4 : state->args = ExecInitExprList(op->args, parent);
3304 tgl@sss.pgh.pa.us 472 : 4 : init_sexpr(op->opfuncid, op->inputcollid, expr, state, parent,
473 : : econtext->ecxt_per_query_memory, true, true);
474 : : }
475 : : else
3339 andres@anarazel.de 476 [ # # ]:UBC 0 : elog(ERROR, "unrecognized node type: %d",
477 : : (int) nodeTag(expr));
478 : :
479 : : /* shouldn't get here unless the selected function returns set */
3339 andres@anarazel.de 480 [ - + ]:CBC 10384 : Assert(state->func.fn_retset);
481 : :
482 : 10384 : return state;
483 : : }
484 : :
485 : : /*
486 : : * ExecMakeFunctionResultSet
487 : : *
488 : : * Evaluate the arguments to a set-returning function and then call the
489 : : * function itself. The argument expressions may not contain set-returning
490 : : * functions (the planner is supposed to have separated evaluation for those).
491 : : *
492 : : * This should be called in a short-lived (per-tuple) context, argContext
493 : : * needs to live until all rows have been returned (i.e. *isDone set to
494 : : * ExprEndResult or ExprSingleResult).
495 : : *
496 : : * This is used by nodeProjectSet.c.
497 : : */
498 : : Datum
499 : 3504285 : ExecMakeFunctionResultSet(SetExprState *fcache,
500 : : ExprContext *econtext,
501 : : MemoryContext argContext,
502 : : bool *isNull,
503 : : ExprDoneCond *isDone)
504 : : {
505 : : List *arguments;
506 : : Datum result;
507 : : FunctionCallInfo fcinfo;
508 : : PgStat_FunctionCallUsage fcusage;
509 : : ReturnSetInfo rsinfo;
510 : : bool callit;
511 : : int i;
512 : :
513 : 3513166 : restart:
514 : :
515 : : /* Guard against stack overflow due to overly complex expressions */
516 : 3513166 : check_stack_depth();
517 : :
518 : : /*
519 : : * If a previous call of the function returned a set result in the form of
520 : : * a tuplestore, continue reading rows from the tuplestore until it's
521 : : * empty.
522 : : */
523 [ + + ]: 3513166 : if (fcache->funcResultStore)
524 : : {
3131 525 : 53030 : TupleTableSlot *slot = fcache->funcResultSlot;
526 : : MemoryContext oldContext;
527 : : bool foundTup;
528 : :
529 : : /*
530 : : * Have to make sure tuple in slot lives long enough, otherwise
531 : : * clearing the slot could end up trying to free something already
532 : : * freed.
533 : : */
534 : 53030 : oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
535 : 53030 : foundTup = tuplestore_gettupleslot(fcache->funcResultStore, true, false,
536 : : fcache->funcResultSlot);
537 : 53030 : MemoryContextSwitchTo(oldContext);
538 : :
539 [ + + ]: 53030 : if (foundTup)
540 : : {
3339 541 : 44173 : *isDone = ExprMultipleResult;
542 [ + + ]: 44173 : if (fcache->funcReturnsTuple)
543 : : {
544 : : /* We must return the whole tuple as a Datum. */
545 : 40268 : *isNull = false;
2728 546 : 40268 : return ExecFetchSlotHeapTupleDatum(fcache->funcResultSlot);
547 : : }
548 : : else
549 : : {
550 : : /* Extract the first column and return it as a scalar. */
3339 551 : 3905 : return slot_getattr(fcache->funcResultSlot, 1, isNull);
552 : : }
553 : : }
554 : : /* Exhausted the tuplestore, so clean up */
555 : 8857 : tuplestore_end(fcache->funcResultStore);
556 : 8857 : fcache->funcResultStore = NULL;
557 : 8857 : *isDone = ExprEndResult;
558 : 8857 : *isNull = true;
559 : 8857 : return (Datum) 0;
560 : : }
561 : :
562 : : /*
563 : : * arguments is a list of expressions to evaluate before passing to the
564 : : * function manager. We skip the evaluation if it was already done in the
565 : : * previous call (ie, we are continuing the evaluation of a set-valued
566 : : * function). Otherwise, collect the current argument values into fcinfo.
567 : : *
568 : : * The arguments have to live in a context that lives at least until all
569 : : * rows from this SRF have been returned, otherwise ValuePerCall SRFs
570 : : * would reference freed memory after the first returned row.
571 : : */
2656 572 : 3460136 : fcinfo = fcache->fcinfo;
3339 573 : 3460136 : arguments = fcache->args;
574 [ + + ]: 3460136 : if (!fcache->setArgsValid)
575 : : {
3131 576 : 195878 : MemoryContext oldContext = MemoryContextSwitchTo(argContext);
577 : :
3339 578 : 195878 : ExecEvalFuncArgs(fcinfo, arguments, econtext);
3131 579 : 195878 : MemoryContextSwitchTo(oldContext);
580 : : }
581 : : else
582 : : {
583 : : /* Reset flag (we may set it again below) */
3339 584 : 3264258 : fcache->setArgsValid = false;
585 : : }
586 : :
587 : : /*
588 : : * Now call the function, passing the evaluated parameter values.
589 : : */
590 : :
591 : : /* Prepare a resultinfo node for communication. */
592 : 3460136 : fcinfo->resultinfo = (Node *) &rsinfo;
593 : 3460136 : rsinfo.type = T_ReturnSetInfo;
594 : 3460136 : rsinfo.econtext = econtext;
595 : 3460136 : rsinfo.expectedDesc = fcache->funcResultDesc;
596 : 3460136 : rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize);
597 : : /* note we do not set SFRM_Materialize_Random or _Preferred */
598 : 3460136 : rsinfo.returnMode = SFRM_ValuePerCall;
599 : : /* isDone is filled below */
600 : 3460136 : rsinfo.setResult = NULL;
601 : 3460136 : rsinfo.setDesc = NULL;
602 : :
603 : : /*
604 : : * If function is strict, and there are any NULL arguments, skip calling
605 : : * the function.
606 : : */
607 : 3460136 : callit = true;
608 [ + + ]: 3460136 : if (fcache->func.fn_strict)
609 : : {
610 [ + + ]: 9633667 : for (i = 0; i < fcinfo->nargs; i++)
611 : : {
2656 612 [ + + ]: 6221768 : if (fcinfo->args[i].isnull)
613 : : {
3339 614 : 45157 : callit = false;
615 : 45157 : break;
616 : : }
617 : : }
618 : : }
619 : :
620 [ + + ]: 3460136 : if (callit)
621 : : {
622 : 3414979 : pgstat_init_function_usage(fcinfo, &fcusage);
623 : :
624 : 3414979 : fcinfo->isnull = false;
625 : 3414979 : rsinfo.isDone = ExprSingleResult;
626 : 3414979 : result = FunctionCallInvoke(fcinfo);
627 : 3413845 : *isNull = fcinfo->isnull;
628 : 3413845 : *isDone = rsinfo.isDone;
629 : :
630 : 3413845 : pgstat_end_function_usage(&fcusage,
631 : 3413845 : rsinfo.isDone != ExprMultipleResult);
632 : : }
633 : : else
634 : : {
635 : : /* for a strict SRF, result for NULL is an empty set */
636 : 45157 : result = (Datum) 0;
637 : 45157 : *isNull = true;
638 : 45157 : *isDone = ExprEndResult;
639 : : }
640 : :
641 : : /* Which protocol does function want to use? */
642 [ + + ]: 3459002 : if (rsinfo.returnMode == SFRM_ValuePerCall)
643 : : {
644 [ + + ]: 3450106 : if (*isDone != ExprEndResult)
645 : : {
646 : : /*
647 : : * Save the current argument values to re-use on the next call.
648 : : */
649 [ + + ]: 3264305 : if (*isDone == ExprMultipleResult)
650 : : {
651 : 3264302 : fcache->setArgsValid = true;
652 : : /* Register cleanup callback if we didn't already */
653 [ + + ]: 3264302 : if (!fcache->shutdown_reg)
654 : : {
655 : 12662 : RegisterExprContextCallback(econtext,
656 : : ShutdownSetExpr,
657 : : PointerGetDatum(fcache));
658 : 12662 : fcache->shutdown_reg = true;
659 : : }
660 : : }
661 : : }
662 : : }
663 [ + - ]: 8896 : else if (rsinfo.returnMode == SFRM_Materialize)
664 : : {
665 : : /* check we're on the same page as the function author */
666 [ - + ]: 8896 : if (rsinfo.isDone != ExprSingleResult)
3339 andres@anarazel.de 667 [ # # ]:UBC 0 : ereport(ERROR,
668 : : (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
669 : : errmsg("table-function protocol for materialize mode was not followed")));
3339 andres@anarazel.de 670 [ + + ]:CBC 8896 : if (rsinfo.setResult != NULL)
671 : : {
672 : : /* prepare to return values from the tuplestore */
673 : 8881 : ExecPrepareTuplestoreResult(fcache, econtext,
674 : : rsinfo.setResult,
675 : : rsinfo.setDesc);
676 : : /* loop back to top to start returning from tuplestore */
677 : 8881 : goto restart;
678 : : }
679 : : /* if setResult was left null, treat it as empty set */
680 : 15 : *isDone = ExprEndResult;
681 : 15 : *isNull = true;
682 : 15 : result = (Datum) 0;
683 : : }
684 : : else
3339 andres@anarazel.de 685 [ # # ]:UBC 0 : ereport(ERROR,
686 : : (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
687 : : errmsg("unrecognized table-function returnMode: %d",
688 : : (int) rsinfo.returnMode)));
689 : :
3339 andres@anarazel.de 690 :CBC 3450121 : return result;
691 : : }
692 : :
693 : :
694 : : /*
695 : : * init_sexpr - initialize a SetExprState node during first use
696 : : */
697 : : static void
3304 tgl@sss.pgh.pa.us 698 : 54911 : init_sexpr(Oid foid, Oid input_collation, Expr *node,
699 : : SetExprState *sexpr, PlanState *parent,
700 : : MemoryContext sexprCxt, bool allowSRF, bool needDescForSRF)
701 : : {
702 : : AclResult aclresult;
2656 andres@anarazel.de 703 : 54911 : size_t numargs = list_length(sexpr->args);
704 : :
705 : : /* Check permission to call function */
1269 peter@eisentraut.org 706 : 54911 : aclresult = object_aclcheck(ProcedureRelationId, foid, GetUserId(), ACL_EXECUTE);
3339 andres@anarazel.de 707 [ + + ]: 54911 : if (aclresult != ACLCHECK_OK)
3076 peter_e@gmx.net 708 : 5 : aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(foid));
3339 andres@anarazel.de 709 [ - + ]: 54906 : InvokeFunctionExecuteHook(foid);
710 : :
711 : : /*
712 : : * Safety check on nargs. Under normal circumstances this should never
713 : : * fail, as parser should check sooner. But possibly it might fail if
714 : : * server has been compiled with FUNC_MAX_ARGS smaller than some functions
715 : : * declared in pg_proc?
716 : : */
717 [ - + ]: 54906 : if (list_length(sexpr->args) > FUNC_MAX_ARGS)
3339 andres@anarazel.de 718 [ # # ]:UBC 0 : ereport(ERROR,
719 : : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
720 : : errmsg_plural("cannot pass more than %d argument to a function",
721 : : "cannot pass more than %d arguments to a function",
722 : : FUNC_MAX_ARGS,
723 : : FUNC_MAX_ARGS)));
724 : :
725 : : /* Set up the primary fmgr lookup information */
3339 andres@anarazel.de 726 :CBC 54906 : fmgr_info_cxt(foid, &(sexpr->func), sexprCxt);
727 : 54906 : fmgr_info_set_expr((Node *) sexpr->expr, &(sexpr->func));
728 : :
729 : : /* Initialize the function call parameter struct as well */
2656 730 : 54906 : sexpr->fcinfo =
731 : 54906 : (FunctionCallInfo) palloc(SizeForFunctionCallInfo(numargs));
732 : 54906 : InitFunctionCallInfoData(*sexpr->fcinfo, &(sexpr->func),
733 : : numargs,
734 : : input_collation, NULL, NULL);
735 : :
736 : : /* If function returns set, check if that's allowed by caller */
3339 737 [ + + - + ]: 54906 : if (sexpr->func.fn_retset && !allowSRF)
3339 andres@anarazel.de 738 [ # # # # ]:UBC 0 : ereport(ERROR,
739 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
740 : : errmsg("set-valued function called in context that cannot accept a set"),
741 : : parent ? executor_errposition(parent->state,
742 : : exprLocation((Node *) node)) : 0));
743 : :
744 : : /* Otherwise, caller should have marked the sexpr correctly */
3339 andres@anarazel.de 745 [ - + ]:CBC 54906 : Assert(sexpr->func.fn_retset == sexpr->funcReturnsSet);
746 : :
747 : : /* If function returns set, prepare expected tuple descriptor */
748 [ + + + + ]: 54906 : if (sexpr->func.fn_retset && needDescForSRF)
749 : 10384 : {
750 : : TypeFuncClass functypclass;
751 : : Oid funcrettype;
752 : : TupleDesc tupdesc;
753 : : MemoryContext oldcontext;
754 : :
755 : 10384 : functypclass = get_expr_result_type(sexpr->func.fn_expr,
756 : : &funcrettype,
757 : : &tupdesc);
758 : :
759 : : /* Must save tupdesc in sexpr's context */
760 : 10384 : oldcontext = MemoryContextSwitchTo(sexprCxt);
761 : :
3113 tgl@sss.pgh.pa.us 762 [ + + + + ]: 10384 : if (functypclass == TYPEFUNC_COMPOSITE ||
763 : : functypclass == TYPEFUNC_COMPOSITE_DOMAIN)
764 : : {
765 : : /* Composite data type, e.g. a table's row type */
3339 andres@anarazel.de 766 [ - + ]: 787 : Assert(tupdesc);
767 : : /* Must copy it out of typcache for safety */
768 : 787 : sexpr->funcResultDesc = CreateTupleDescCopy(tupdesc);
769 : 787 : sexpr->funcReturnsTuple = true;
770 : : }
771 [ + + ]: 9597 : else if (functypclass == TYPEFUNC_SCALAR)
772 : : {
773 : : /* Base data type, i.e. scalar */
2723 774 : 9542 : tupdesc = CreateTemplateTupleDesc(1);
3339 775 : 9542 : TupleDescInitEntry(tupdesc,
776 : : (AttrNumber) 1,
777 : : NULL,
778 : : funcrettype,
779 : : -1,
780 : : 0);
50 drowley@postgresql.o 781 :GNC 9542 : TupleDescFinalize(tupdesc);
3339 andres@anarazel.de 782 :CBC 9542 : sexpr->funcResultDesc = tupdesc;
783 : 9542 : sexpr->funcReturnsTuple = false;
784 : : }
785 [ + - ]: 55 : else if (functypclass == TYPEFUNC_RECORD)
786 : : {
787 : : /* This will work if function doesn't need an expectedDesc */
788 : 55 : sexpr->funcResultDesc = NULL;
789 : 55 : sexpr->funcReturnsTuple = true;
790 : : }
791 : : else
792 : : {
793 : : /* Else, we will fail if function needs an expectedDesc */
3339 andres@anarazel.de 794 :UBC 0 : sexpr->funcResultDesc = NULL;
795 : : }
796 : :
3339 andres@anarazel.de 797 :CBC 10384 : MemoryContextSwitchTo(oldcontext);
798 : : }
799 : : else
800 : 44522 : sexpr->funcResultDesc = NULL;
801 : :
802 : : /* Initialize additional state */
803 : 54906 : sexpr->funcResultStore = NULL;
804 : 54906 : sexpr->funcResultSlot = NULL;
805 : 54906 : sexpr->shutdown_reg = false;
806 : 54906 : }
807 : :
808 : : /*
809 : : * callback function in case a SetExprState needs to be shut down before it
810 : : * has been run to completion
811 : : */
812 : : static void
813 : 13450 : ShutdownSetExpr(Datum arg)
814 : : {
815 : 13450 : SetExprState *sexpr = castNode(SetExprState, DatumGetPointer(arg));
816 : :
817 : : /* If we have a slot, make sure it's let go of any tuplestore pointer */
818 [ + + ]: 13450 : if (sexpr->funcResultSlot)
819 : 793 : ExecClearTuple(sexpr->funcResultSlot);
820 : :
821 : : /* Release any open tuplestore */
822 [ + + ]: 13450 : if (sexpr->funcResultStore)
823 : 24 : tuplestore_end(sexpr->funcResultStore);
824 : 13450 : sexpr->funcResultStore = NULL;
825 : :
826 : : /* Clear any active set-argument state */
827 : 13450 : sexpr->setArgsValid = false;
828 : :
829 : : /* execUtils will deregister the callback... */
830 : 13450 : sexpr->shutdown_reg = false;
831 : 13450 : }
832 : :
833 : : /*
834 : : * Evaluate arguments for a function.
835 : : */
836 : : static void
837 : 297602 : ExecEvalFuncArgs(FunctionCallInfo fcinfo,
838 : : List *argList,
839 : : ExprContext *econtext)
840 : : {
841 : : int i;
842 : : ListCell *arg;
843 : :
844 : 297602 : i = 0;
845 [ + + + + : 739122 : foreach(arg, argList)
+ + ]
846 : : {
847 : 441528 : ExprState *argstate = (ExprState *) lfirst(arg);
848 : :
2656 849 : 441528 : fcinfo->args[i].value = ExecEvalExpr(argstate,
850 : : econtext,
851 : : &fcinfo->args[i].isnull);
3339 852 : 441520 : i++;
853 : : }
854 : :
855 [ - + ]: 297594 : Assert(i == fcinfo->nargs);
856 : 297594 : }
857 : :
858 : : /*
859 : : * ExecPrepareTuplestoreResult
860 : : *
861 : : * Subroutine for ExecMakeFunctionResultSet: prepare to extract rows from a
862 : : * tuplestore function result. We must set up a funcResultSlot (unless
863 : : * already done in a previous call cycle) and verify that the function
864 : : * returned the expected tuple descriptor.
865 : : */
866 : : static void
867 : 8881 : ExecPrepareTuplestoreResult(SetExprState *sexpr,
868 : : ExprContext *econtext,
869 : : Tuplestorestate *resultStore,
870 : : TupleDesc resultDesc)
871 : : {
872 : 8881 : sexpr->funcResultStore = resultStore;
873 : :
874 [ + + ]: 8881 : if (sexpr->funcResultSlot == NULL)
875 : : {
876 : : /* Create a slot so we can read data out of the tuplestore */
877 : : TupleDesc slotDesc;
878 : : MemoryContext oldcontext;
879 : :
880 : 793 : oldcontext = MemoryContextSwitchTo(sexpr->func.fn_mcxt);
881 : :
882 : : /*
883 : : * If we were not able to determine the result rowtype from context,
884 : : * and the function didn't return a tupdesc, we have to fail.
885 : : */
886 [ + + ]: 793 : if (sexpr->funcResultDesc)
887 : 765 : slotDesc = sexpr->funcResultDesc;
888 [ + - ]: 28 : else if (resultDesc)
889 : : {
890 : : /* don't assume resultDesc is long-lived */
891 : 28 : slotDesc = CreateTupleDescCopy(resultDesc);
892 : : }
893 : : else
894 : : {
3339 andres@anarazel.de 895 [ # # ]:UBC 0 : ereport(ERROR,
896 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
897 : : errmsg("function returning setof record called in "
898 : : "context that cannot accept type record")));
899 : : slotDesc = NULL; /* keep compiler quiet */
900 : : }
901 : :
2728 andres@anarazel.de 902 :CBC 793 : sexpr->funcResultSlot = MakeSingleTupleTableSlot(slotDesc,
903 : : &TTSOpsMinimalTuple);
3339 904 : 793 : MemoryContextSwitchTo(oldcontext);
905 : : }
906 : :
907 : : /*
908 : : * If function provided a tupdesc, cross-check it. We only really need to
909 : : * do this for functions returning RECORD, but might as well do it always.
910 : : */
911 [ + - ]: 8881 : if (resultDesc)
912 : : {
913 [ + + ]: 8881 : if (sexpr->funcResultDesc)
914 : 8845 : tupledesc_match(sexpr->funcResultDesc, resultDesc);
915 : :
916 : : /*
917 : : * If it is a dynamically-allocated TupleDesc, free it: it is
918 : : * typically allocated in a per-query context, so we must avoid
919 : : * leaking it across multiple usages.
920 : : */
921 [ + - ]: 8881 : if (resultDesc->tdrefcount == -1)
922 : 8881 : FreeTupleDesc(resultDesc);
923 : : }
924 : :
925 : : /* Register cleanup callback if we didn't already */
926 [ + + ]: 8881 : if (!sexpr->shutdown_reg)
927 : : {
928 : 793 : RegisterExprContextCallback(econtext,
929 : : ShutdownSetExpr,
930 : : PointerGetDatum(sexpr));
931 : 793 : sexpr->shutdown_reg = true;
932 : : }
933 : 8881 : }
934 : :
935 : : /*
936 : : * Check that function result tuple type (src_tupdesc) matches or can
937 : : * be considered to match what the query expects (dst_tupdesc). If
938 : : * they don't match, ereport.
939 : : *
940 : : * We really only care about number of attributes and data type.
941 : : * Also, we can ignore type mismatch on columns that are dropped in the
942 : : * destination type, so long as the physical storage matches. This is
943 : : * helpful in some cases involving out-of-date cached plans.
944 : : */
945 : : static void
946 : 78855 : tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
947 : : {
948 : : int i;
949 : :
950 [ + + ]: 78855 : if (dst_tupdesc->natts != src_tupdesc->natts)
951 [ + - ]: 32 : ereport(ERROR,
952 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
953 : : errmsg("function return row and query-specified return row do not match"),
954 : : errdetail_plural("Returned row contains %d attribute, but query expects %d.",
955 : : "Returned row contains %d attributes, but query expects %d.",
956 : : src_tupdesc->natts,
957 : : src_tupdesc->natts, dst_tupdesc->natts)));
958 : :
959 [ + + ]: 347405 : for (i = 0; i < dst_tupdesc->natts; i++)
960 : : {
3180 961 : 268606 : Form_pg_attribute dattr = TupleDescAttr(dst_tupdesc, i);
962 : 268606 : Form_pg_attribute sattr = TupleDescAttr(src_tupdesc, i);
963 : :
3339 964 [ + + ]: 268606 : if (IsBinaryCoercible(sattr->atttypid, dattr->atttypid))
965 : 268582 : continue; /* no worries */
966 [ + - ]: 24 : if (!dattr->attisdropped)
967 [ + - ]: 24 : ereport(ERROR,
968 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
969 : : errmsg("function return row and query-specified return row do not match"),
970 : : errdetail("Returned type %s at ordinal position %d, but query expects %s.",
971 : : format_type_be(sattr->atttypid),
972 : : i + 1,
973 : : format_type_be(dattr->atttypid))));
974 : :
3339 andres@anarazel.de 975 [ # # ]:UBC 0 : if (dattr->attlen != sattr->attlen ||
976 [ # # ]: 0 : dattr->attalign != sattr->attalign)
977 [ # # ]: 0 : ereport(ERROR,
978 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
979 : : errmsg("function return row and query-specified return row do not match"),
980 : : errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.",
981 : : i + 1)));
982 : : }
3339 andres@anarazel.de 983 :CBC 78799 : }
|