summaryrefslogtreecommitdiff
path: root/src/backend/executor/execFlatten.c
blob: 729232d9d42acee7f68b4a401cc6a4869e4d7cce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
/*-------------------------------------------------------------------------
 *
 * execFlatten.c--
 *	  This file handles the nodes associated with flattening sets in the
 *	  target list of queries containing functions returning sets.
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /cvsroot/pgsql/src/backend/executor/Attic/execFlatten.c,v 1.7 1998/09/01 04:28:14 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */

/*
 * ExecEvalIter() -
 *	 Iterate through the all return tuples/base types from a function one
 *	 at time (i.e. one per ExecEvalIter call).	Not really needed for
 *	 postquel functions, but for reasons of orthogonality, these nodes
 *	 exist above pq functions as well as c functions.
 *
 * ExecEvalFjoin() -
 *	 Given N Iter nodes return a vector of all combinations of results
 *	 one at a time (i.e. one result vector per ExecEvalFjoin call).  This
 *	 node does the actual flattening work.
 */
#include "postgres.h"
#include "nodes/primnodes.h"
#include "nodes/relation.h"
#include "nodes/execnodes.h"
#include "executor/executor.h"
#include "executor/execFlatten.h"

#ifdef SETS_FIXED
static bool FjoinBumpOuterNodes(TargetEntry *tlist, ExprContext *econtext,
					DatumPtr results, char *nulls);

#endif

Datum
ExecEvalIter(Iter *iterNode,
			 ExprContext *econtext,
			 bool *resultIsNull,
			 bool *iterIsDone)
{
	Node	   *expression;

	expression = iterNode->iterexpr;

	/*
	 * Really Iter nodes are only needed for C functions, postquel
	 * function by their nature return 1 result at a time.	For now we are
	 * only worrying about postquel functions, c functions will come
	 * later.
	 */
	return ExecEvalExpr(expression, econtext, resultIsNull, iterIsDone);
}

void
ExecEvalFjoin(TargetEntry *tlist,
			  ExprContext *econtext,
			  bool *isNullVect,
			  bool *fj_isDone)
{

#ifdef SETS_FIXED
	bool		isDone;
	int			curNode;
	List	   *tlistP;

	Fjoin	   *fjNode = tlist->fjoin;
	DatumPtr	resVect = fjNode->fj_results;
	BoolPtr		alwaysDone = fjNode->fj_alwaysDone;

	if (fj_isDone)
		*fj_isDone = false;

	/*
	 * For the next tuple produced by the plan, we need to re-initialize
	 * the Fjoin node.
	 */
	if (!fjNode->fj_initialized)
	{

		/*
		 * Initialize all of the Outer nodes
		 */
		curNode = 1;
		foreach(tlistP, lnext(tlist))
		{
			TargetEntry *tle = lfirst(tlistP);

			resVect[curNode] = ExecEvalIter((Iter *) tle->expr,
											econtext,
											&isNullVect[curNode],
											&isDone);
			if (isDone)
				isNullVect[curNode] = alwaysDone[curNode] = true;
			else
				alwaysDone[curNode] = false;

			curNode++;
		}

		/*
		 * Initialize the inner node
		 */
		resVect[0] = ExecEvalIter((Iter *) fjNode->fj_innerNode->expr,
								  econtext,
								  &isNullVect[0],
								  &isDone);
		if (isDone)
			isNullVect[0] = alwaysDone[0] = true;
		else
			alwaysDone[0] = false;

		/*
		 * Mark the Fjoin as initialized now.
		 */
		fjNode->fj_initialized = TRUE;

		/*
		 * If the inner node is always done, then we are done for now
		 */
		if (isDone)
			return;
	}
	else
	{

		/*
		 * If we're already initialized, all we need to do is get the next
		 * inner result and pair it up with the existing outer node result
		 * vector.	Watch out for the degenerate case, where the inner
		 * node never returns results.
		 */

		/*
		 * Fill in nulls for every function that is always done.
		 */
		for (curNode = fjNode->fj_nNodes - 1; curNode >= 0; curNode--)
			isNullVect[curNode] = alwaysDone[curNode];

		if (alwaysDone[0] == true)
		{
			*fj_isDone = FjoinBumpOuterNodes(tlist,
											 econtext,
											 resVect,
											 isNullVect);
			return;
		}
		else
			resVect[0] = ExecEvalIter((Iter *) fjNode->fj_innerNode->expr,
									  econtext,
									  &isNullVect[0],
									  &isDone);
	}

	/*
	 * if the inner node is done
	 */
	if (isDone)
	{
		*fj_isDone = FjoinBumpOuterNodes(tlist,
										 econtext,
										 resVect,
										 isNullVect);
		if (*fj_isDone)
			return;

		resVect[0] = ExecEvalIter((Iter *) fjNode->fj_innerNode->expr,
								  econtext,
								  &isNullVect[0],
								  &isDone);

	}
#endif
	return;
}

#ifdef SETS_FIXED
static bool
FjoinBumpOuterNodes(TargetEntry *tlist,
					ExprContext *econtext,
					DatumPtr results,
					char *nulls)
{
	bool		funcIsDone = true;
	Fjoin	   *fjNode = tlist->fjoin;
	char	   *alwaysDone = fjNode->fj_alwaysDone;
	List	   *outerList = lnext(tlist);
	List	   *trailers = lnext(tlist);
	int			trailNode = 1;
	int			curNode = 1;

	/*
	 * Run through list of functions until we get to one that isn't yet
	 * done returning values.  Watch out for funcs that are always done.
	 */
	while ((funcIsDone == true) && (outerList != NIL))
	{
		TargetEntry *tle = lfirst(outerList);

		if (alwaysDone[curNode] == true)
			nulls[curNode] = 'n';
		else
			results[curNode] = ExecEvalIter((Iter) tle->expr,
											econtext,
											&nulls[curNode],
											&funcIsDone);
		curNode++;
		outerList = lnext(outerList);
	}

	/*
	 * If every function is done, then we are done flattening. Mark the
	 * Fjoin node unitialized, it is time to get the next tuple from the
	 * plan and redo all of the flattening.
	 */
	if (funcIsDone)
	{
		set_fj_initialized(fjNode, false);
		return true;
	}

	/*
	 * We found a function that wasn't done.  Now re-run every function
	 * before it.  As usual watch out for functions that are always done.
	 */
	trailNode = 1;
	while (trailNode != curNode - 1)
	{
		TargetEntry *tle = lfirst(trailers);

		if (alwaysDone[trailNode] != true)
			results[trailNode] = ExecEvalIter((Iter) tle->expr,
											  econtext,
											  &nulls[trailNode],
											  &funcIsDone);
		trailNode++;
		trailers = lnext(trailers);
	}
	return false;
}

#endif