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