nodeFunctionscan.c 10 KB
Newer Older
1 2 3 4 5
/*-------------------------------------------------------------------------
 *
 * nodeFunctionscan.c
 *	  Support routines for scanning RangeFunctions (functions in rangetable).
 *
Bruce Momjian's avatar
Bruce Momjian committed
6
 * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
7 8 9 10
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
11
 *	  $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.27 2004/09/22 17:41:51 tgl Exp $
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
 *
 *-------------------------------------------------------------------------
 */
/*
 * INTERFACE ROUTINES
 *		ExecFunctionScan		scans a function.
 *		ExecFunctionNext		retrieve next tuple in sequential order.
 *		ExecInitFunctionScan	creates and initializes a functionscan node.
 *		ExecEndFunctionScan		releases any storage allocated.
 *		ExecFunctionReScan		rescans the function
 */
#include "postgres.h"

#include "access/heapam.h"
#include "catalog/pg_type.h"
#include "executor/execdebug.h"
#include "executor/execdefs.h"
#include "executor/execdesc.h"
#include "executor/nodeFunctionscan.h"
#include "parser/parsetree.h"
#include "parser/parse_expr.h"
#include "parser/parse_type.h"
#include "utils/lsyscache.h"
35
#include "utils/typcache.h"
36

37

38
static TupleTableSlot *FunctionNext(FunctionScanState *node);
39
static bool tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc);
40 41 42 43 44 45 46 47 48 49 50 51

/* ----------------------------------------------------------------
 *						Scan Support
 * ----------------------------------------------------------------
 */
/* ----------------------------------------------------------------
 *		FunctionNext
 *
 *		This is a workhorse for ExecFunctionScan
 * ----------------------------------------------------------------
 */
static TupleTableSlot *
52
FunctionNext(FunctionScanState *node)
53
{
Bruce Momjian's avatar
Bruce Momjian committed
54 55 56 57 58 59
	TupleTableSlot *slot;
	EState	   *estate;
	ScanDirection direction;
	Tuplestorestate *tuplestorestate;
	bool		should_free;
	HeapTuple	heapTuple;
60 61 62 63

	/*
	 * get information from the estate and scan state
	 */
64
	estate = node->ss.ps.state;
65 66
	direction = estate->es_direction;

67
	tuplestorestate = node->tuplestorestate;
68 69

	/*
70
	 * If first time through, read all tuples from function and put them
Bruce Momjian's avatar
Bruce Momjian committed
71 72
	 * in a tuplestore. Subsequent calls just fetch tuples from
	 * tuplestore.
73 74 75
	 */
	if (tuplestorestate == NULL)
	{
76
		ExprContext *econtext = node->ss.ps.ps_ExprContext;
Bruce Momjian's avatar
Bruce Momjian committed
77
		TupleDesc	funcTupdesc;
78

79 80
		node->tuplestorestate = tuplestorestate =
			ExecMakeTableFunctionResult(node->funcexpr,
81
										econtext,
82
										node->tupdesc,
83
										&funcTupdesc);
84 85

		/*
Bruce Momjian's avatar
Bruce Momjian committed
86 87 88
		 * If function provided a tupdesc, cross-check it.	We only really
		 * need to do this for functions returning RECORD, but might as
		 * well do it always.
89
		 */
90
		if (funcTupdesc && !tupledesc_match(node->tupdesc, funcTupdesc))
91 92
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
93
					 errmsg("query-specified return row and actual function return row do not match")));
94 95 96 97 98
	}

	/*
	 * Get the next tuple from tuplestore. Return NULL if no more tuples.
	 */
99 100 101
	heapTuple = tuplestore_getheaptuple(tuplestorestate,
										ScanDirectionIsForward(direction),
										&should_free);
102
	slot = node->ss.ss_ScanTupleSlot;
103 104 105 106 107 108
	return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
}

/* ----------------------------------------------------------------
 *		ExecFunctionScan(node)
 *
109
 *		Scans the function sequentially and returns the next qualifying
110 111
 *		tuple.
 *		It calls the ExecScan() routine and passes it the access method
112
 *		which retrieves tuples sequentially.
113 114 115 116
 *
 */

TupleTableSlot *
117
ExecFunctionScan(FunctionScanState *node)
118 119 120 121
{
	/*
	 * use FunctionNext as access method
	 */
122
	return ExecScan(&node->ss, (ExecScanAccessMtd) FunctionNext);
123 124 125 126 127 128
}

/* ----------------------------------------------------------------
 *		ExecInitFunctionScan
 * ----------------------------------------------------------------
 */
129 130
FunctionScanState *
ExecInitFunctionScan(FunctionScan *node, EState *estate)
131
{
Bruce Momjian's avatar
Bruce Momjian committed
132 133 134 135 136
	FunctionScanState *scanstate;
	RangeTblEntry *rte;
	Oid			funcrettype;
	char		functyptype;
	TupleDesc	tupdesc = NULL;
137 138 139 140

	/*
	 * FunctionScan should not have any children.
	 */
141 142
	Assert(outerPlan(node) == NULL);
	Assert(innerPlan(node) == NULL);
143 144 145 146 147

	/*
	 * create new ScanState for node
	 */
	scanstate = makeNode(FunctionScanState);
148 149
	scanstate->ss.ps.plan = (Plan *) node;
	scanstate->ss.ps.state = estate;
150 151 152 153 154 155

	/*
	 * Miscellaneous initialization
	 *
	 * create expression context for node
	 */
156
	ExecAssignExprContext(estate, &scanstate->ss.ps);
157 158 159 160 161 162

#define FUNCTIONSCAN_NSLOTS 2

	/*
	 * tuple table initialization
	 */
163 164 165 166 167 168 169
	ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
	ExecInitScanTupleSlot(estate, &scanstate->ss);

	/*
	 * initialize child expressions
	 */
	scanstate->ss.ps.targetlist = (List *)
170
		ExecInitExpr((Expr *) node->scan.plan.targetlist,
171 172
					 (PlanState *) scanstate);
	scanstate->ss.ps.qual = (List *)
173
		ExecInitExpr((Expr *) node->scan.plan.qual,
174
					 (PlanState *) scanstate);
175 176 177 178 179 180 181

	/*
	 * get info about function
	 */
	rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
	Assert(rte->rtekind == RTE_FUNCTION);
	funcrettype = exprType(rte->funcexpr);
182 183 184

	/*
	 * Now determine if the function returns a simple or composite type,
185
	 * and build an appropriate tupdesc.
186
	 */
187
	functyptype = get_typtype(funcrettype);
188

189
	if (functyptype == 'c')
190
	{
191 192
		/* Composite data type, e.g. a table's row type */
		tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(funcrettype, -1));
193
	}
194
	else if (functyptype == 'b' || functyptype == 'd')
195
	{
196
		/* Must be a base data type, i.e. scalar */
197
		char	   *attname = strVal(linitial(rte->eref->colnames));
198

199
		tupdesc = CreateTemplateTupleDesc(1, false);
200 201 202 203 204
		TupleDescInitEntry(tupdesc,
						   (AttrNumber) 1,
						   attname,
						   funcrettype,
						   -1,
205
						   0);
206
	}
207
	else if (funcrettype == RECORDOID)
208
	{
209
		/* Must be a pseudo type, i.e. record */
210
		tupdesc = BuildDescForRelation(rte->coldeflist);
211 212
	}
	else
213 214 215 216
	{
		/* crummy error message, but parser should have caught this */
		elog(ERROR, "function in FROM has unsupported return type");
	}
217

218 219 220 221 222 223 224 225
	/*
	 * For RECORD results, make sure a typmod has been assigned.  (The
	 * function should do this for itself, but let's cover things in case
	 * it doesn't.)
	 */
	if (tupdesc->tdtypeid == RECORDOID && tupdesc->tdtypmod < 0)
		assign_record_type_typmod(tupdesc);

226
	scanstate->tupdesc = tupdesc;
227
	ExecSetSlotDescriptor(scanstate->ss.ss_ScanTupleSlot,
228 229 230 231 232 233
						  tupdesc, false);

	/*
	 * Other node-specific setup
	 */
	scanstate->tuplestorestate = NULL;
234 235
	scanstate->funcexpr = ExecInitExpr((Expr *) rte->funcexpr,
									   (PlanState *) scanstate);
236

237
	scanstate->ss.ps.ps_TupFromTlist = false;
238 239

	/*
240
	 * Initialize result tuple type and projection info.
241
	 */
242 243
	ExecAssignResultTypeFromTL(&scanstate->ss.ps);
	ExecAssignProjectionInfo(&scanstate->ss.ps);
244

245
	return scanstate;
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
}

int
ExecCountSlotsFunctionScan(FunctionScan *node)
{
	return ExecCountSlotsNode(outerPlan(node)) +
		ExecCountSlotsNode(innerPlan(node)) +
		FUNCTIONSCAN_NSLOTS;
}

/* ----------------------------------------------------------------
 *		ExecEndFunctionScan
 *
 *		frees any storage allocated through C routines.
 * ----------------------------------------------------------------
 */
void
263
ExecEndFunctionScan(FunctionScanState *node)
264 265
{
	/*
266
	 * Free the exprcontext
267
	 */
268
	ExecFreeExprContext(&node->ss.ps);
269 270 271 272

	/*
	 * clean out the tuple table
	 */
273 274
	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
	ExecClearTuple(node->ss.ss_ScanTupleSlot);
275 276 277 278

	/*
	 * Release tuplestore resources
	 */
279 280 281
	if (node->tuplestorestate != NULL)
		tuplestore_end(node->tuplestorestate);
	node->tuplestorestate = NULL;
282 283 284 285 286 287 288 289 290
}

/* ----------------------------------------------------------------
 *		ExecFunctionMarkPos
 *
 *		Calls tuplestore to save the current position in the stored file.
 * ----------------------------------------------------------------
 */
void
291
ExecFunctionMarkPos(FunctionScanState *node)
292 293 294 295
{
	/*
	 * if we haven't materialized yet, just return.
	 */
296
	if (!node->tuplestorestate)
297 298
		return;

299
	tuplestore_markpos(node->tuplestorestate);
300 301 302 303 304 305 306 307 308
}

/* ----------------------------------------------------------------
 *		ExecFunctionRestrPos
 *
 *		Calls tuplestore to restore the last saved file position.
 * ----------------------------------------------------------------
 */
void
309
ExecFunctionRestrPos(FunctionScanState *node)
310 311 312 313
{
	/*
	 * if we haven't materialized yet, just return.
	 */
314
	if (!node->tuplestorestate)
315 316
		return;

317
	tuplestore_restorepos(node->tuplestorestate);
318 319 320 321 322 323 324 325 326
}

/* ----------------------------------------------------------------
 *		ExecFunctionReScan
 *
 *		Rescans the relation.
 * ----------------------------------------------------------------
 */
void
327
ExecFunctionReScan(FunctionScanState *node, ExprContext *exprCtxt)
328
{
329
	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
330 331 332 333

	/*
	 * If we haven't materialized yet, just return.
	 */
334
	if (!node->tuplestorestate)
335 336 337 338 339 340 341 342
		return;

	/*
	 * Here we have a choice whether to drop the tuplestore (and recompute
	 * the function outputs) or just rescan it.  This should depend on
	 * whether the function expression contains parameters and/or is
	 * marked volatile.  FIXME soon.
	 */
343
	if (node->ss.ps.chgParam != NULL)
344
	{
345 346
		tuplestore_end(node->tuplestorestate);
		node->tuplestorestate = NULL;
347 348
	}
	else
349
		tuplestore_rescan(node->tuplestorestate);
350 351
}

352 353 354 355 356 357 358 359 360
/*
 * Check that function result tuple type (src_tupdesc) matches or can be
 * considered to match what the query expects (dst_tupdesc).
 *
 * We really only care about number of attributes and data type.
 * Also, we can ignore type mismatch on columns that are dropped in the
 * destination type, so long as the physical storage matches.  This is
 * helpful in some cases involving out-of-date cached plans.
 */
361
static bool
362
tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
363 364 365
{
	int			i;

366 367
	if (dst_tupdesc->natts != src_tupdesc->natts)
		return false;
368

369
	for (i = 0; i < dst_tupdesc->natts; i++)
370
	{
371 372 373 374 375 376 377 378 379 380
		Form_pg_attribute dattr = dst_tupdesc->attrs[i];
		Form_pg_attribute sattr = src_tupdesc->attrs[i];

		if (dattr->atttypid == sattr->atttypid)
			continue;			/* no worries */
		if (!dattr->attisdropped)
			return false;
		if (dattr->attlen != sattr->attlen ||
			dattr->attalign != sattr->attalign)
			return false;
381 382
	}

383
	return true;
384
}