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