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