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 | |
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 | 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 |
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 * |
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 * |
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 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 | */ |
214 | void |
215 | ExecEndTableFuncScan(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 | */ |
243 | void |
244 | ExecReScanTableFuncScan(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 | */ |
274 | static void |
275 | tfuncFetchRows(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 | */ |
347 | static void |
348 | tfuncInitialize(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 | */ |
436 | static void |
437 | tfuncLoadRows(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 | |