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