1 /*-------------------------------------------------------------------------
2 *
3 * nodeTableFuncscan.c
4 * Support routines for scanning RangeTableFunc (XMLTABLE like functions).
5 *
6 * Portions Copyright (c) 1996-2017, 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
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 *
TableFuncNext(TableFuncScanState * node)54 TableFuncNext(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 */
80 static bool
TableFuncRecheck(TableFuncScanState * node,TupleTableSlot * slot)81 TableFuncRecheck(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 */
96 static TupleTableSlot *
ExecTableFuncScan(PlanState * pstate)97 ExecTableFuncScan(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 */
110 TableFuncScanState *
ExecInitTableFuncScan(TableFuncScan * node,EState * estate,int eflags)111 ExecInitTableFuncScan(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 child expressions
144 */
145 scanstate->ss.ps.qual =
146 ExecInitQual(node->scan.plan.qual, &scanstate->ss.ps);
147
148 /*
149 * tuple table initialization
150 */
151 ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
152 ExecInitScanTupleSlot(estate, &scanstate->ss);
153
154 /*
155 * initialize source tuple type
156 */
157 tupdesc = BuildDescFromLists(tf->colnames,
158 tf->coltypes,
159 tf->coltypmods,
160 tf->colcollations);
161
162 ExecAssignScanType(&scanstate->ss, tupdesc);
163
164 /*
165 * Initialize result tuple type and projection info.
166 */
167 ExecAssignResultTypeFromTL(&scanstate->ss.ps);
168 ExecAssignScanProjectionInfo(&scanstate->ss);
169
170 /* Only XMLTABLE is supported currently */
171 scanstate->routine = &XmlTableRoutine;
172
173 scanstate->perTableCxt =
174 AllocSetContextCreate(CurrentMemoryContext,
175 "TableFunc per value context",
176 ALLOCSET_DEFAULT_SIZES);
177 scanstate->opaque = NULL; /* initialized at runtime */
178
179 scanstate->ns_names = tf->ns_names;
180
181 scanstate->ns_uris =
182 ExecInitExprList(tf->ns_uris, (PlanState *) scanstate);
183 scanstate->docexpr =
184 ExecInitExpr((Expr *) tf->docexpr, (PlanState *) scanstate);
185 scanstate->rowexpr =
186 ExecInitExpr((Expr *) tf->rowexpr, (PlanState *) scanstate);
187 scanstate->colexprs =
188 ExecInitExprList(tf->colexprs, (PlanState *) scanstate);
189 scanstate->coldefexprs =
190 ExecInitExprList(tf->coldefexprs, (PlanState *) scanstate);
191
192 scanstate->notnulls = tf->notnulls;
193
194 /* these are allocated now and initialized later */
195 scanstate->in_functions = palloc(sizeof(FmgrInfo) * tupdesc->natts);
196 scanstate->typioparams = palloc(sizeof(Oid) * tupdesc->natts);
197
198 /*
199 * Fill in the necessary fmgr infos.
200 */
201 for (i = 0; i < tupdesc->natts; i++)
202 {
203 Oid in_funcid;
204
205 getTypeInputInfo(tupdesc->attrs[i]->atttypid,
206 &in_funcid, &scanstate->typioparams[i]);
207 fmgr_info(in_funcid, &scanstate->in_functions[i]);
208 }
209
210 return scanstate;
211 }
212
213 /* ----------------------------------------------------------------
214 * ExecEndTableFuncscan
215 *
216 * frees any storage allocated through C routines.
217 * ----------------------------------------------------------------
218 */
219 void
ExecEndTableFuncScan(TableFuncScanState * node)220 ExecEndTableFuncScan(TableFuncScanState *node)
221 {
222 /*
223 * Free the exprcontext
224 */
225 ExecFreeExprContext(&node->ss.ps);
226
227 /*
228 * clean out the tuple table
229 */
230 ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
231 ExecClearTuple(node->ss.ss_ScanTupleSlot);
232
233 /*
234 * Release tuplestore resources
235 */
236 if (node->tupstore != NULL)
237 tuplestore_end(node->tupstore);
238 node->tupstore = NULL;
239 }
240
241 /* ----------------------------------------------------------------
242 * ExecReScanTableFuncscan
243 *
244 * Rescans the relation.
245 * ----------------------------------------------------------------
246 */
247 void
ExecReScanTableFuncScan(TableFuncScanState * node)248 ExecReScanTableFuncScan(TableFuncScanState *node)
249 {
250 Bitmapset *chgparam = node->ss.ps.chgParam;
251
252 ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
253 ExecScanReScan(&node->ss);
254
255 /*
256 * Recompute when parameters are changed.
257 */
258 if (chgparam)
259 {
260 if (node->tupstore != NULL)
261 {
262 tuplestore_end(node->tupstore);
263 node->tupstore = NULL;
264 }
265 }
266
267 if (node->tupstore != NULL)
268 tuplestore_rescan(node->tupstore);
269 }
270
271 /* ----------------------------------------------------------------
272 * tfuncFetchRows
273 *
274 * Read rows from a TableFunc producer
275 * ----------------------------------------------------------------
276 */
277 static void
tfuncFetchRows(TableFuncScanState * tstate,ExprContext * econtext)278 tfuncFetchRows(TableFuncScanState *tstate, ExprContext *econtext)
279 {
280 const TableFuncRoutine *routine = tstate->routine;
281 MemoryContext oldcxt;
282 Datum value;
283 bool isnull;
284
285 Assert(tstate->opaque == NULL);
286
287 /* build tuplestore for the result */
288 oldcxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
289 tstate->tupstore = tuplestore_begin_heap(false, false, work_mem);
290
291 /*
292 * Each call to fetch a new set of rows - of which there may be very many
293 * if XMLTABLE is being used in a lateral join - will allocate a possibly
294 * substantial amount of memory, so we cannot use the per-query context
295 * here. perTableCxt now serves the same function as "argcontext" does in
296 * FunctionScan - a place to store per-one-call (i.e. one result table)
297 * lifetime data (as opposed to per-query or per-result-tuple).
298 */
299 MemoryContextSwitchTo(tstate->perTableCxt);
300
301 PG_TRY();
302 {
303 routine->InitOpaque(tstate,
304 tstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor->natts);
305
306 /*
307 * If evaluating the document expression returns NULL, the table
308 * expression is empty and we return immediately.
309 */
310 value = ExecEvalExpr(tstate->docexpr, econtext, &isnull);
311
312 if (!isnull)
313 {
314 /* otherwise, pass the document value to the table builder */
315 tfuncInitialize(tstate, econtext, value);
316
317 /* initialize ordinality counter */
318 tstate->ordinal = 1;
319
320 /* Load all rows into the tuplestore, and we're done */
321 tfuncLoadRows(tstate, econtext);
322 }
323 }
324 PG_CATCH();
325 {
326 if (tstate->opaque != NULL)
327 routine->DestroyOpaque(tstate);
328 PG_RE_THROW();
329 }
330 PG_END_TRY();
331
332 /* clean up and return to original memory context */
333
334 if (tstate->opaque != NULL)
335 {
336 routine->DestroyOpaque(tstate);
337 tstate->opaque = NULL;
338 }
339
340 MemoryContextSwitchTo(oldcxt);
341 MemoryContextReset(tstate->perTableCxt);
342
343 return;
344 }
345
346 /*
347 * Fill in namespace declarations, the row filter, and column filters in a
348 * table expression builder context.
349 */
350 static void
tfuncInitialize(TableFuncScanState * tstate,ExprContext * econtext,Datum doc)351 tfuncInitialize(TableFuncScanState *tstate, ExprContext *econtext, Datum doc)
352 {
353 const TableFuncRoutine *routine = tstate->routine;
354 TupleDesc tupdesc;
355 ListCell *lc1,
356 *lc2;
357 bool isnull;
358 int colno;
359 Datum value;
360 int ordinalitycol =
361 ((TableFuncScan *) (tstate->ss.ps.plan))->tablefunc->ordinalitycol;
362
363 /*
364 * Install the document as a possibly-toasted Datum into the tablefunc
365 * context.
366 */
367 routine->SetDocument(tstate, doc);
368
369 /* Evaluate namespace specifications */
370 forboth(lc1, tstate->ns_uris, lc2, tstate->ns_names)
371 {
372 ExprState *expr = (ExprState *) lfirst(lc1);
373 Value *ns_node = (Value *) lfirst(lc2);
374 char *ns_uri;
375 char *ns_name;
376
377 value = ExecEvalExpr((ExprState *) expr, econtext, &isnull);
378 if (isnull)
379 ereport(ERROR,
380 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
381 errmsg("namespace URI must not be null")));
382 ns_uri = TextDatumGetCString(value);
383
384 /* DEFAULT is passed down to SetNamespace as NULL */
385 ns_name = ns_node ? strVal(ns_node) : NULL;
386
387 routine->SetNamespace(tstate, ns_name, ns_uri);
388 }
389
390 /* Install the row filter expression into the table builder context */
391 value = ExecEvalExpr(tstate->rowexpr, econtext, &isnull);
392 if (isnull)
393 ereport(ERROR,
394 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
395 errmsg("row filter expression must not be null")));
396
397 routine->SetRowFilter(tstate, TextDatumGetCString(value));
398
399 /*
400 * Install the column filter expressions into the table builder context.
401 * If an expression is given, use that; otherwise the column name itself
402 * is the column filter.
403 */
404 colno = 0;
405 tupdesc = tstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
406 foreach(lc1, tstate->colexprs)
407 {
408 char *colfilter;
409
410 if (colno != ordinalitycol)
411 {
412 ExprState *colexpr = lfirst(lc1);
413
414 if (colexpr != NULL)
415 {
416 value = ExecEvalExpr(colexpr, econtext, &isnull);
417 if (isnull)
418 ereport(ERROR,
419 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
420 errmsg("column filter expression must not be null"),
421 errdetail("Filter for column \"%s\" is null.",
422 NameStr(tupdesc->attrs[colno]->attname))));
423 colfilter = TextDatumGetCString(value);
424 }
425 else
426 colfilter = NameStr(tupdesc->attrs[colno]->attname);
427
428 routine->SetColumnFilter(tstate, colfilter, colno);
429 }
430
431 colno++;
432 }
433 }
434
435 /*
436 * Load all the rows from the TableFunc table builder into a tuplestore.
437 */
438 static void
tfuncLoadRows(TableFuncScanState * tstate,ExprContext * econtext)439 tfuncLoadRows(TableFuncScanState *tstate, ExprContext *econtext)
440 {
441 const TableFuncRoutine *routine = tstate->routine;
442 TupleTableSlot *slot = tstate->ss.ss_ScanTupleSlot;
443 TupleDesc tupdesc = slot->tts_tupleDescriptor;
444 Datum *values = slot->tts_values;
445 bool *nulls = slot->tts_isnull;
446 int natts = tupdesc->natts;
447 MemoryContext oldcxt;
448 int ordinalitycol;
449
450 ordinalitycol =
451 ((TableFuncScan *) (tstate->ss.ps.plan))->tablefunc->ordinalitycol;
452
453 /*
454 * We need a short-lived memory context that we can clean up each time
455 * around the loop, to avoid wasting space. Our default per-tuple context
456 * is fine for the job, since we won't have used it for anything yet in
457 * this tuple cycle.
458 */
459 oldcxt = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
460
461 /*
462 * Keep requesting rows from the table builder until there aren't any.
463 */
464 while (routine->FetchRow(tstate))
465 {
466 ListCell *cell = list_head(tstate->coldefexprs);
467 int colno;
468
469 CHECK_FOR_INTERRUPTS();
470
471 ExecClearTuple(tstate->ss.ss_ScanTupleSlot);
472
473 /*
474 * Obtain the value of each column for this row, installing them into
475 * the slot; then add the tuple to the tuplestore.
476 */
477 for (colno = 0; colno < natts; 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 tupdesc->attrs[colno]->atttypid,
492 tupdesc->attrs[colno]->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(tupdesc->attrs[colno]->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