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 *
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 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
ExecEndTableFuncScan(TableFuncScanState * node)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
ExecReScanTableFuncScan(TableFuncScanState * node)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
tfuncFetchRows(TableFuncScanState * tstate,ExprContext * econtext)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
tfuncInitialize(TableFuncScanState * tstate,ExprContext * econtext,Datum doc)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
tfuncLoadRows(TableFuncScanState * tstate,ExprContext * econtext)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