Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * nodeTableFuncscan.c
4 : * Support routines for scanning RangeTableFunc (XMLTABLE like functions).
5 : *
6 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/executor/nodeTableFuncscan.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : /*
16 : * INTERFACE ROUTINES
17 : * ExecTableFuncScan scans a function.
18 : * ExecFunctionNext retrieve next tuple in sequential order.
19 : * ExecInitTableFuncScan creates and initializes a TableFuncscan node.
20 : * ExecEndTableFuncScan releases any storage allocated.
21 : * ExecReScanTableFuncScan rescans the function
22 : */
23 : #include "postgres.h"
24 :
25 : #include "executor/executor.h"
26 : #include "executor/nodeTableFuncscan.h"
27 : #include "executor/tablefunc.h"
28 : #include "miscadmin.h"
29 : #include "nodes/execnodes.h"
30 : #include "utils/builtins.h"
31 : #include "utils/jsonpath.h"
32 : #include "utils/lsyscache.h"
33 : #include "utils/memutils.h"
34 : #include "utils/xml.h"
35 :
36 : static TupleTableSlot *TableFuncNext(TableFuncScanState *node);
37 : static bool TableFuncRecheck(TableFuncScanState *node, TupleTableSlot *slot);
38 :
39 : static void tfuncFetchRows(TableFuncScanState *tstate, ExprContext *econtext);
40 : static void tfuncInitialize(TableFuncScanState *tstate, ExprContext *econtext, Datum doc);
41 : static void tfuncLoadRows(TableFuncScanState *tstate, ExprContext *econtext);
42 :
43 : /* ----------------------------------------------------------------
44 : * Scan Support
45 : * ----------------------------------------------------------------
46 : */
47 : /* ----------------------------------------------------------------
48 : * TableFuncNext
49 : *
50 : * This is a workhorse for ExecTableFuncScan
51 : * ----------------------------------------------------------------
52 : */
53 : static TupleTableSlot *
54 24184 : TableFuncNext(TableFuncScanState *node)
55 : {
56 : TupleTableSlot *scanslot;
57 :
58 24184 : scanslot = node->ss.ss_ScanTupleSlot;
59 :
60 : /*
61 : * If first time through, read all tuples from function and put them in a
62 : * tuplestore. Subsequent calls just fetch tuples from tuplestore.
63 : */
64 24184 : if (node->tupstore == NULL)
65 794 : tfuncFetchRows(node, node->ss.ps.ps_ExprContext);
66 :
67 : /*
68 : * Get the next tuple from tuplestore.
69 : */
70 24070 : (void) tuplestore_gettupleslot(node->tupstore,
71 : true,
72 : false,
73 : scanslot);
74 24070 : return scanslot;
75 : }
76 :
77 : /*
78 : * TableFuncRecheck -- access method routine to recheck a tuple in EvalPlanQual
79 : */
80 : static bool
81 0 : TableFuncRecheck(TableFuncScanState *node, TupleTableSlot *slot)
82 : {
83 : /* nothing to check */
84 0 : return true;
85 : }
86 :
87 : /* ----------------------------------------------------------------
88 : * ExecTableFuncScan(node)
89 : *
90 : * Scans the function sequentially and returns the next qualifying
91 : * tuple.
92 : * We call the ExecScan() routine and pass it the appropriate
93 : * access method functions.
94 : * ----------------------------------------------------------------
95 : */
96 : static TupleTableSlot *
97 24130 : ExecTableFuncScan(PlanState *pstate)
98 : {
99 24130 : TableFuncScanState *node = castNode(TableFuncScanState, pstate);
100 :
101 24130 : return ExecScan(&node->ss,
102 : (ExecScanAccessMtd) TableFuncNext,
103 : (ExecScanRecheckMtd) TableFuncRecheck);
104 : }
105 :
106 : /* ----------------------------------------------------------------
107 : * ExecInitTableFuncScan
108 : * ----------------------------------------------------------------
109 : */
110 : TableFuncScanState *
111 626 : ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags)
112 : {
113 : TableFuncScanState *scanstate;
114 626 : TableFunc *tf = node->tablefunc;
115 : TupleDesc tupdesc;
116 : int i;
117 :
118 : /* check for unsupported flags */
119 : Assert(!(eflags & EXEC_FLAG_MARK));
120 :
121 : /*
122 : * TableFuncscan should not have any children.
123 : */
124 : Assert(outerPlan(node) == NULL);
125 : Assert(innerPlan(node) == NULL);
126 :
127 : /*
128 : * create new ScanState for node
129 : */
130 626 : scanstate = makeNode(TableFuncScanState);
131 626 : scanstate->ss.ps.plan = (Plan *) node;
132 626 : scanstate->ss.ps.state = estate;
133 626 : scanstate->ss.ps.ExecProcNode = ExecTableFuncScan;
134 :
135 : /*
136 : * Miscellaneous initialization
137 : *
138 : * create expression context for node
139 : */
140 626 : ExecAssignExprContext(estate, &scanstate->ss.ps);
141 :
142 : /*
143 : * initialize source tuple type
144 : */
145 626 : tupdesc = BuildDescFromLists(tf->colnames,
146 626 : tf->coltypes,
147 626 : tf->coltypmods,
148 626 : tf->colcollations);
149 : /* and the corresponding scan slot */
150 626 : ExecInitScanTupleSlot(estate, &scanstate->ss, tupdesc,
151 : &TTSOpsMinimalTuple);
152 :
153 : /*
154 : * Initialize result type and projection.
155 : */
156 626 : ExecInitResultTypeTL(&scanstate->ss.ps);
157 626 : ExecAssignScanProjectionInfo(&scanstate->ss);
158 :
159 : /*
160 : * initialize child expressions
161 : */
162 626 : scanstate->ss.ps.qual =
163 626 : ExecInitQual(node->scan.plan.qual, &scanstate->ss.ps);
164 :
165 : /* Only XMLTABLE and JSON_TABLE are supported currently */
166 626 : scanstate->routine =
167 626 : tf->functype == TFT_XMLTABLE ? &XmlTableRoutine : &JsonbTableRoutine;
168 :
169 626 : scanstate->perTableCxt =
170 626 : AllocSetContextCreate(CurrentMemoryContext,
171 : "TableFunc per value context",
172 : ALLOCSET_DEFAULT_SIZES);
173 626 : scanstate->opaque = NULL; /* initialized at runtime */
174 :
175 626 : scanstate->ns_names = tf->ns_names;
176 :
177 626 : scanstate->ns_uris =
178 626 : ExecInitExprList(tf->ns_uris, (PlanState *) scanstate);
179 626 : scanstate->docexpr =
180 626 : ExecInitExpr((Expr *) tf->docexpr, (PlanState *) scanstate);
181 626 : scanstate->rowexpr =
182 626 : ExecInitExpr((Expr *) tf->rowexpr, (PlanState *) scanstate);
183 626 : scanstate->colexprs =
184 626 : ExecInitExprList(tf->colexprs, (PlanState *) scanstate);
185 626 : scanstate->coldefexprs =
186 626 : ExecInitExprList(tf->coldefexprs, (PlanState *) scanstate);
187 626 : scanstate->colvalexprs =
188 626 : ExecInitExprList(tf->colvalexprs, (PlanState *) scanstate);
189 626 : scanstate->passingvalexprs =
190 626 : ExecInitExprList(tf->passingvalexprs, (PlanState *) scanstate);
191 :
192 626 : scanstate->notnulls = tf->notnulls;
193 :
194 : /* these are allocated now and initialized later */
195 626 : scanstate->in_functions = palloc(sizeof(FmgrInfo) * tupdesc->natts);
196 626 : scanstate->typioparams = palloc(sizeof(Oid) * tupdesc->natts);
197 :
198 : /*
199 : * Fill in the necessary fmgr infos.
200 : */
201 2290 : for (i = 0; i < tupdesc->natts; i++)
202 : {
203 : Oid in_funcid;
204 :
205 1664 : getTypeInputInfo(TupleDescAttr(tupdesc, i)->atttypid,
206 1664 : &in_funcid, &scanstate->typioparams[i]);
207 1664 : fmgr_info(in_funcid, &scanstate->in_functions[i]);
208 : }
209 :
210 626 : return scanstate;
211 : }
212 :
213 : /* ----------------------------------------------------------------
214 : * ExecEndTableFuncScan
215 : *
216 : * frees any storage allocated through C routines.
217 : * ----------------------------------------------------------------
218 : */
219 : void
220 512 : ExecEndTableFuncScan(TableFuncScanState *node)
221 : {
222 : /*
223 : * Release tuplestore resources
224 : */
225 512 : if (node->tupstore != NULL)
226 434 : tuplestore_end(node->tupstore);
227 512 : node->tupstore = NULL;
228 512 : }
229 :
230 : /* ----------------------------------------------------------------
231 : * ExecReScanTableFuncScan
232 : *
233 : * Rescans the relation.
234 : * ----------------------------------------------------------------
235 : */
236 : void
237 444 : ExecReScanTableFuncScan(TableFuncScanState *node)
238 : {
239 444 : Bitmapset *chgparam = node->ss.ps.chgParam;
240 :
241 444 : if (node->ss.ps.ps_ResultTupleSlot)
242 0 : ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
243 444 : ExecScanReScan(&node->ss);
244 :
245 : /*
246 : * Recompute when parameters are changed.
247 : */
248 444 : if (chgparam)
249 : {
250 444 : if (node->tupstore != NULL)
251 : {
252 246 : tuplestore_end(node->tupstore);
253 246 : node->tupstore = NULL;
254 : }
255 : }
256 :
257 444 : if (node->tupstore != NULL)
258 0 : tuplestore_rescan(node->tupstore);
259 444 : }
260 :
261 : /* ----------------------------------------------------------------
262 : * tfuncFetchRows
263 : *
264 : * Read rows from a TableFunc producer
265 : * ----------------------------------------------------------------
266 : */
267 : static void
268 794 : tfuncFetchRows(TableFuncScanState *tstate, ExprContext *econtext)
269 : {
270 794 : const TableFuncRoutine *routine = tstate->routine;
271 : MemoryContext oldcxt;
272 : Datum value;
273 : bool isnull;
274 :
275 : Assert(tstate->opaque == NULL);
276 :
277 : /* build tuplestore for the result */
278 794 : oldcxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
279 794 : tstate->tupstore = tuplestore_begin_heap(false, false, work_mem);
280 :
281 : /*
282 : * Each call to fetch a new set of rows - of which there may be very many
283 : * if XMLTABLE or JSON_TABLE is being used in a lateral join - will
284 : * allocate a possibly substantial amount of memory, so we cannot use the
285 : * per-query context here. perTableCxt now serves the same function as
286 : * "argcontext" does in FunctionScan - a place to store per-one-call (i.e.
287 : * one result table) lifetime data (as opposed to per-query or
288 : * per-result-tuple).
289 : */
290 794 : MemoryContextSwitchTo(tstate->perTableCxt);
291 :
292 794 : PG_TRY();
293 : {
294 794 : routine->InitOpaque(tstate,
295 794 : tstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor->natts);
296 :
297 : /*
298 : * If evaluating the document expression returns NULL, the table
299 : * expression is empty and we return immediately.
300 : */
301 794 : value = ExecEvalExpr(tstate->docexpr, econtext, &isnull);
302 :
303 794 : if (!isnull)
304 : {
305 : /* otherwise, pass the document value to the table builder */
306 788 : tfuncInitialize(tstate, econtext, value);
307 :
308 : /* initialize ordinality counter */
309 776 : tstate->ordinal = 1;
310 :
311 : /* Load all rows into the tuplestore, and we're done */
312 776 : tfuncLoadRows(tstate, econtext);
313 : }
314 : }
315 114 : PG_CATCH();
316 : {
317 114 : if (tstate->opaque != NULL)
318 114 : routine->DestroyOpaque(tstate);
319 114 : PG_RE_THROW();
320 : }
321 680 : PG_END_TRY();
322 :
323 : /* clean up and return to original memory context */
324 :
325 680 : if (tstate->opaque != NULL)
326 : {
327 680 : routine->DestroyOpaque(tstate);
328 680 : tstate->opaque = NULL;
329 : }
330 :
331 680 : MemoryContextSwitchTo(oldcxt);
332 680 : MemoryContextReset(tstate->perTableCxt);
333 680 : }
334 :
335 : /*
336 : * Fill in namespace declarations, the row filter, and column filters in a
337 : * table expression builder context.
338 : */
339 : static void
340 788 : tfuncInitialize(TableFuncScanState *tstate, ExprContext *econtext, Datum doc)
341 : {
342 788 : const TableFuncRoutine *routine = tstate->routine;
343 : TupleDesc tupdesc;
344 : ListCell *lc1,
345 : *lc2;
346 : bool isnull;
347 : int colno;
348 : Datum value;
349 788 : int ordinalitycol =
350 788 : ((TableFuncScan *) (tstate->ss.ps.plan))->tablefunc->ordinalitycol;
351 :
352 : /*
353 : * Install the document as a possibly-toasted Datum into the tablefunc
354 : * context.
355 : */
356 788 : routine->SetDocument(tstate, doc);
357 :
358 : /* Evaluate namespace specifications */
359 794 : forboth(lc1, tstate->ns_uris, lc2, tstate->ns_names)
360 : {
361 18 : ExprState *expr = (ExprState *) lfirst(lc1);
362 18 : String *ns_node = lfirst_node(String, lc2);
363 : char *ns_uri;
364 : char *ns_name;
365 :
366 18 : value = ExecEvalExpr((ExprState *) expr, econtext, &isnull);
367 18 : if (isnull)
368 0 : ereport(ERROR,
369 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
370 : errmsg("namespace URI must not be null")));
371 18 : ns_uri = TextDatumGetCString(value);
372 :
373 : /* DEFAULT is passed down to SetNamespace as NULL */
374 18 : ns_name = ns_node ? strVal(ns_node) : NULL;
375 :
376 18 : routine->SetNamespace(tstate, ns_name, ns_uri);
377 : }
378 :
379 : /*
380 : * Install the row filter expression, if any, into the table builder
381 : * context.
382 : */
383 776 : if (routine->SetRowFilter)
384 : {
385 258 : value = ExecEvalExpr(tstate->rowexpr, econtext, &isnull);
386 258 : if (isnull)
387 0 : ereport(ERROR,
388 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
389 : errmsg("row filter expression must not be null")));
390 :
391 258 : routine->SetRowFilter(tstate, TextDatumGetCString(value));
392 : }
393 :
394 : /*
395 : * Install the column filter expressions into the table builder context.
396 : * If an expression is given, use that; otherwise the column name itself
397 : * is the column filter.
398 : */
399 776 : colno = 0;
400 776 : tupdesc = tstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
401 1622 : foreach(lc1, tstate->colexprs)
402 : {
403 : char *colfilter;
404 846 : Form_pg_attribute att = TupleDescAttr(tupdesc, colno);
405 :
406 846 : if (colno != ordinalitycol)
407 : {
408 774 : ExprState *colexpr = lfirst(lc1);
409 :
410 774 : if (colexpr != NULL)
411 : {
412 606 : value = ExecEvalExpr(colexpr, econtext, &isnull);
413 606 : if (isnull)
414 0 : ereport(ERROR,
415 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
416 : errmsg("column filter expression must not be null"),
417 : errdetail("Filter for column \"%s\" is null.",
418 : NameStr(att->attname))));
419 606 : colfilter = TextDatumGetCString(value);
420 : }
421 : else
422 168 : colfilter = NameStr(att->attname);
423 :
424 774 : routine->SetColumnFilter(tstate, colfilter, colno);
425 : }
426 :
427 846 : colno++;
428 : }
429 776 : }
430 :
431 : /*
432 : * Load all the rows from the TableFunc table builder into a tuplestore.
433 : */
434 : static void
435 776 : tfuncLoadRows(TableFuncScanState *tstate, ExprContext *econtext)
436 : {
437 776 : const TableFuncRoutine *routine = tstate->routine;
438 776 : TupleTableSlot *slot = tstate->ss.ss_ScanTupleSlot;
439 776 : TupleDesc tupdesc = slot->tts_tupleDescriptor;
440 776 : Datum *values = slot->tts_values;
441 776 : bool *nulls = slot->tts_isnull;
442 776 : int natts = tupdesc->natts;
443 : MemoryContext oldcxt;
444 : int ordinalitycol;
445 :
446 776 : ordinalitycol =
447 776 : ((TableFuncScan *) (tstate->ss.ps.plan))->tablefunc->ordinalitycol;
448 :
449 : /*
450 : * We need a short-lived memory context that we can clean up each time
451 : * around the loop, to avoid wasting space. Our default per-tuple context
452 : * is fine for the job, since we won't have used it for anything yet in
453 : * this tuple cycle.
454 : */
455 776 : oldcxt = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
456 :
457 : /*
458 : * Keep requesting rows from the table builder until there aren't any.
459 : */
460 24166 : while (routine->FetchRow(tstate))
461 : {
462 23492 : ListCell *cell = list_head(tstate->coldefexprs);
463 : int colno;
464 :
465 23492 : CHECK_FOR_INTERRUPTS();
466 :
467 23492 : ExecClearTuple(tstate->ss.ss_ScanTupleSlot);
468 :
469 : /*
470 : * Obtain the value of each column for this row, installing them into
471 : * the slot; then add the tuple to the tuplestore.
472 : */
473 161470 : for (colno = 0; colno < natts; colno++)
474 : {
475 138080 : Form_pg_attribute att = TupleDescAttr(tupdesc, colno);
476 :
477 138080 : if (colno == ordinalitycol)
478 : {
479 : /* Fast path for ordinality column */
480 270 : values[colno] = Int32GetDatum(tstate->ordinal++);
481 270 : nulls[colno] = false;
482 : }
483 : else
484 : {
485 : bool isnull;
486 :
487 137810 : values[colno] = routine->GetValue(tstate,
488 : colno,
489 : att->atttypid,
490 : att->atttypmod,
491 : &isnull);
492 :
493 : /* No value? Evaluate and apply the default, if any */
494 137714 : if (isnull && cell != NULL)
495 : {
496 22266 : ExprState *coldefexpr = (ExprState *) lfirst(cell);
497 :
498 22266 : if (coldefexpr != NULL)
499 222 : values[colno] = ExecEvalExpr(coldefexpr, econtext,
500 : &isnull);
501 : }
502 :
503 : /* Verify a possible NOT NULL constraint */
504 137714 : if (isnull && bms_is_member(colno, tstate->notnulls))
505 6 : ereport(ERROR,
506 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
507 : errmsg("null is not allowed in column \"%s\"",
508 : NameStr(att->attname))));
509 :
510 137708 : nulls[colno] = isnull;
511 : }
512 :
513 : /* advance list of default expressions */
514 137978 : if (cell != NULL)
515 133380 : cell = lnext(tstate->coldefexprs, cell);
516 : }
517 :
518 23390 : tuplestore_putvalues(tstate->tupstore, tupdesc, values, nulls);
519 :
520 23390 : MemoryContextReset(econtext->ecxt_per_tuple_memory);
521 : }
522 :
523 674 : MemoryContextSwitchTo(oldcxt);
524 674 : }
|