nodeMaterial.c 8.92 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * nodeMaterial.c
4
 *	  Routines to handle materialization nodes.
5
 *
6
 * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
Bruce Momjian's avatar
Add:  
Bruce Momjian committed
7
 * Portions Copyright (c) 1994, Regents of the University of California
8 9 10
 *
 *
 * IDENTIFICATION
11
 *	  $PostgreSQL: pgsql/src/backend/executor/nodeMaterial.c,v 1.55 2006/06/27 02:51:39 tgl Exp $
12 13 14 15 16
 *
 *-------------------------------------------------------------------------
 */
/*
 * INTERFACE ROUTINES
17 18
 *		ExecMaterial			- materialize the result of a subplan
 *		ExecInitMaterial		- initialize node and subnodes
19
 *		ExecEndMaterial			- shutdown node and subnodes
20 21
 *
 */
22 23
#include "postgres.h"

24
#include "access/heapam.h"
25 26
#include "executor/executor.h"
#include "executor/nodeMaterial.h"
27 28
#include "miscadmin.h"
#include "utils/tuplestore.h"
29 30

/* ----------------------------------------------------------------
31
 *		ExecMaterial
32
 *
33 34 35 36
 *		As long as we are at the end of the data collected in the tuplestore,
 *		we collect one new row from the subplan on each call, and stash it
 *		aside in the tuplestore before returning it.  The tuplestore is
 *		only read if we are asked to scan backwards, rescan, or mark/restore.
37 38 39
 *
 * ----------------------------------------------------------------
 */
40
TupleTableSlot *				/* result tuple from subplan */
41
ExecMaterial(MaterialState *node)
42
{
43 44
	EState	   *estate;
	ScanDirection dir;
45
	bool		forward;
46
	Tuplestorestate *tuplestorestate;
47
	bool		eof_tuplestore;
48 49
	TupleTableSlot *slot;

50 51
	/*
	 * get state info from node
52
	 */
53
	estate = node->ss.ps.state;
54
	dir = estate->es_direction;
55
	forward = ScanDirectionIsForward(dir);
56
	tuplestorestate = (Tuplestorestate *) node->tuplestorestate;
57

58
	/*
59
	 * If first time through, and we need a tuplestore, initialize it.
60
	 */
61
	if (tuplestorestate == NULL && node->randomAccess)
62
	{
63
		tuplestorestate = tuplestore_begin_heap(true, false, work_mem);
64

65
		node->tuplestorestate = (void *) tuplestorestate;
66
	}
67

68
	/*
69 70
	 * If we are not at the end of the tuplestore, or are going backwards, try
	 * to fetch a tuple from tuplestore.
71
	 */
72 73
	eof_tuplestore = (tuplestorestate == NULL) ||
		tuplestore_ateof(tuplestorestate);
74

75 76 77
	if (!forward && eof_tuplestore)
	{
		if (!node->eof_underlying)
78
		{
79 80
			/*
			 * When reversing direction at tuplestore EOF, the first
81
			 * gettupleslot call will fetch the last-added tuple; but we want
82 83
			 * to return the one before that, if possible. So do an extra
			 * fetch.
84
			 */
85
			if (!tuplestore_advance(tuplestorestate, forward))
Bruce Momjian's avatar
Bruce Momjian committed
86
				return NULL;	/* the tuplestore must be empty */
87 88 89
		}
		eof_tuplestore = false;
	}
90

91 92 93 94
	/*
	 * If we can fetch another tuple from the tuplestore, return it.
	 */
	slot = node->ss.ps.ps_ResultTupleSlot;
95 96
	if (!eof_tuplestore)
	{
97 98 99
		if (tuplestore_gettupleslot(tuplestorestate, forward, slot))
			return slot;
		if (forward)
100 101
			eof_tuplestore = true;
	}
Bruce Momjian's avatar
Bruce Momjian committed
102

103 104 105
	/*
	 * If necessary, try to fetch another row from the subplan.
	 *
106 107 108 109
	 * Note: the eof_underlying state variable exists to short-circuit further
	 * subplan calls.  It's not optional, unfortunately, because some plan
	 * node types are not robust about being called again when they've already
	 * returned NULL.
110 111 112 113 114
	 */
	if (eof_tuplestore && !node->eof_underlying)
	{
		PlanState  *outerNode;
		TupleTableSlot *outerslot;
115

116
		/*
117 118
		 * We can only get here with forward==true, so no need to worry about
		 * which direction the subplan will go.
119
		 */
120 121 122 123 124 125 126
		outerNode = outerPlanState(node);
		outerslot = ExecProcNode(outerNode);
		if (TupIsNull(outerslot))
		{
			node->eof_underlying = true;
			return NULL;
		}
Bruce Momjian's avatar
Bruce Momjian committed
127

128
		/*
129
		 * Append returned tuple to tuplestore.  NOTE: because the
130 131
		 * tuplestore is certainly in EOF state, its read position will move
		 * forward over the added tuple.  This is what we want.
132
		 */
133
		if (tuplestorestate)
134 135 136 137 138 139 140
			tuplestore_puttupleslot(tuplestorestate, outerslot);

		/*
		 * And return a copy of the tuple.  (XXX couldn't we just return
		 * the outerslot?)
		 */
		return ExecCopySlot(slot, outerslot);
141
	}
142

143
	/*
144
	 * Nothing left ...
145
	 */
146
	return ExecClearTuple(slot);
147 148 149 150 151 152
}

/* ----------------------------------------------------------------
 *		ExecInitMaterial
 * ----------------------------------------------------------------
 */
153
MaterialState *
154
ExecInitMaterial(Material *node, EState *estate, int eflags)
155
{
156 157
	MaterialState *matstate;
	Plan	   *outerPlan;
158

159
	/*
160
	 * create state structure
161
	 */
162
	matstate = makeNode(MaterialState);
163 164 165
	matstate->ss.ps.plan = (Plan *) node;
	matstate->ss.ps.state = estate;

166 167 168 169 170 171 172 173 174 175
	/*
	 * We must have random access to the subplan output to do backward scan
	 * or mark/restore.  We also prefer to materialize the subplan output
	 * if we might be called on to rewind and replay it many times.
	 * However, if none of these cases apply, we can skip storing the data.
	 */
	matstate->randomAccess = (eflags & (EXEC_FLAG_REWIND |
										EXEC_FLAG_BACKWARD |
										EXEC_FLAG_MARK)) != 0;

176
	matstate->eof_underlying = false;
177
	matstate->tuplestorestate = NULL;
178

179 180
	/*
	 * Miscellaneous initialization
181
	 *
182 183
	 * Materialization nodes don't need ExprContexts because they never call
	 * ExecQual or ExecProject.
184
	 */
185

186
#define MATERIAL_NSLOTS 2
187 188

	/*
189
	 * tuple table initialization
190
	 *
191
	 * material nodes only return tuples from their materialized relation.
192
	 */
193 194
	ExecInitResultTupleSlot(estate, &matstate->ss.ps);
	ExecInitScanTupleSlot(estate, &matstate->ss);
195

196
	/*
197 198 199 200
	 * initialize child nodes
	 *
	 * We shield the child node from the need to support REWIND, BACKWARD,
	 * or MARK/RESTORE.
201
	 */
202 203
	eflags &= ~(EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK);

204
	outerPlan = outerPlan(node);
205
	outerPlanState(matstate) = ExecInitNode(outerPlan, estate, eflags);
206

207
	/*
208 209
	 * initialize tuple type.  no need to initialize projection info because
	 * this node doesn't do projections.
210
	 */
211
	ExecAssignResultTypeFromTL(&matstate->ss.ps);
212 213
	ExecAssignScanTypeFromOuterPlan(&matstate->ss);
	matstate->ss.ps.ps_ProjInfo = NULL;
214

215
	return matstate;
216 217 218
}

int
219
ExecCountSlotsMaterial(Material *node)
220
{
221
	return ExecCountSlotsNode(outerPlan((Plan *) node)) +
222 223
		ExecCountSlotsNode(innerPlan((Plan *) node)) +
		MATERIAL_NSLOTS;
224 225 226
}

/* ----------------------------------------------------------------
227
 *		ExecEndMaterial
228 229 230
 * ----------------------------------------------------------------
 */
void
231
ExecEndMaterial(MaterialState *node)
232
{
233
	/*
234
	 * clean out the tuple table
235
	 */
236
	ExecClearTuple(node->ss.ss_ScanTupleSlot);
237

238 239
	/*
	 * Release tuplestore resources
240
	 */
241 242 243
	if (node->tuplestorestate != NULL)
		tuplestore_end((Tuplestorestate *) node->tuplestorestate);
	node->tuplestorestate = NULL;
244 245 246 247 248

	/*
	 * shut down the subplan
	 */
	ExecEndNode(outerPlanState(node));
249 250
}

Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
251
/* ----------------------------------------------------------------
252
 *		ExecMaterialMarkPos
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
253
 *
254
 *		Calls tuplestore to save the current position in the stored file.
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
255 256 257
 * ----------------------------------------------------------------
 */
void
258
ExecMaterialMarkPos(MaterialState *node)
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
259
{
260 261
	Assert(node->randomAccess);

262 263
	/*
	 * if we haven't materialized yet, just return.
264
	 */
265
	if (!node->tuplestorestate)
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
266
		return;
267

268
	tuplestore_markpos((Tuplestorestate *) node->tuplestorestate);
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
269 270
}

271
/* ----------------------------------------------------------------
272 273 274
 *		ExecMaterialRestrPos
 *
 *		Calls tuplestore to restore the last saved file position.
275 276
 * ----------------------------------------------------------------
 */
277
void
278
ExecMaterialRestrPos(MaterialState *node)
279
{
280 281
	Assert(node->randomAccess);

282 283
	/*
	 * if we haven't materialized yet, just return.
284
	 */
285
	if (!node->tuplestorestate)
286
		return;
287

288 289
	/*
	 * restore the scan to the previously marked position
290
	 */
291
	tuplestore_restorepos((Tuplestorestate *) node->tuplestorestate);
292 293 294
}

/* ----------------------------------------------------------------
295 296 297
 *		ExecMaterialReScan
 *
 *		Rescans the materialized relation.
298 299 300
 * ----------------------------------------------------------------
 */
void
301
ExecMaterialReScan(MaterialState *node, ExprContext *exprCtxt)
302
{
303
	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
304

305
	if (node->randomAccess)
306
	{
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
		/*
		 * If we haven't materialized yet, just return. If outerplan' chgParam
		 * is not NULL then it will be re-scanned by ExecProcNode, else - no
		 * reason to re-scan it at all.
		 */
		if (!node->tuplestorestate)
			return;

		/*
		 * If subnode is to be rescanned then we forget previous stored
		 * results; we have to re-read the subplan and re-store.
		 *
		 * Otherwise we can just rewind and rescan the stored output. The
		 * state of the subnode does not change.
		 */
		if (((PlanState *) node)->lefttree->chgParam != NULL)
		{
			tuplestore_end((Tuplestorestate *) node->tuplestorestate);
			node->tuplestorestate = NULL;
			node->eof_underlying = false;
		}
		else
			tuplestore_rescan((Tuplestorestate *) node->tuplestorestate);
330 331
	}
	else
332 333 334 335 336 337 338 339 340 341 342
	{
		/* In this case we are just passing on the subquery's output */

		/*
		 * if chgParam of subnode is not null then plan will be re-scanned by
		 * first ExecProcNode.
		 */
		if (((PlanState *) node)->lefttree->chgParam == NULL)
			ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
		node->eof_underlying = false;
	}
343
}