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