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