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