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
package edu.caltech.nanodb.plannodes;
import java.util.ArrayList;
import java.util.List;
import edu.caltech.nanodb.expressions.OrderByExpression;
import edu.caltech.nanodb.expressions.TupleLiteral;
import edu.caltech.nanodb.relations.Tuple;
/**
* <p>
* This plan-node materializes the results of a child plan-node in memory.
* The tuples of the child plan-node are fetched on-demand (not all at once
* at the start of plan-node execution), and are cached within this plan-node.
* If the child plan produces disk-backed tuples, this plan-node caches a copy
* of the tuple so that the disk-backed tuple can be unpinned.
* </p>
* <p>
* Note that a more typical implementation of a "materialize" node would have
* a maximum in-memory footprint, and would store tuples to a temporary disk
* file when this maximum memory size is reached. However, the memory
* allocation for in-memory tuples is not managed by the Buffer Manager, so it
* really isn't possible for the materialize node to provide this kind of
* functionality.
* </p>
*/
public class MaterializeNode extends PlanNode {
/**
* This collection holds the tuples that have been generated by the child
* plan so far.
*/
private ArrayList<Tuple> tuples;
/**
* This field stores the index of the "current tuple" as the materialized
* results are traversed.
*/
private int currentTupleIndex;
/** This field stores the index of the tuple when a position is marked. */
private int markedTupleIndex;
/**
* When this flag is true, the child plan-node has finished generating
* all of its results.
*/
private boolean childNodeFinished;
public MaterializeNode(PlanNode leftChild) {
super(leftChild);
}
@Override
public List<OrderByExpression> resultsOrderedBy() {
return leftChild.resultsOrderedBy();
}
@Override
public boolean supportsMarking() {
return true;
}
@Override
public void prepare() {
leftChild.prepare();
schema = leftChild.getSchema();
stats = leftChild.getStats();
cost = leftChild.getCost();
tuples = new ArrayList<>();
currentTupleIndex = -1;
markedTupleIndex = -1;
childNodeFinished = false;
}
@Override
public Tuple getNextTuple() {
Tuple tup = null;
assert currentTupleIndex >= -1;
assert currentTupleIndex <= tuples.size();
if (currentTupleIndex + 1 < tuples.size()) {
// Moving forward the "current tuple" index will stay within the
// tuples we have so far.
currentTupleIndex++;
tup = tuples.get(currentTupleIndex);
}
else {
// Moving forward the "current tuple" index will move beyond the
// tuples we have so far. Need to see if we have consumed all
// child tuples yet.
if (!childNodeFinished) {
// Try to fetch another tuple from the child plan-node, if
// there is one.
tup = leftChild.getNextTuple();
if (tup != null) {
if (tup.isDiskBacked()) {
// Make an in-memory version of the tuple we can cache.
Tuple copy = new TupleLiteral(tup);
tup.unpin();
tup = copy;
}
tuples.add(tup);
currentTupleIndex++;
}
else {
// The child has no more tuples.
childNodeFinished = true;
}
assert currentTupleIndex <= tuples.size();
}
}
return tup;
}
@Override
public void markCurrentPosition() {
markedTupleIndex = currentTupleIndex;
}
@Override
public void resetToLastMark() {
currentTupleIndex = markedTupleIndex;
}
@Override
public void cleanUp() {
leftChild.cleanUp();
tuples = null;
currentTupleIndex = -1;
}
@Override
public String toString() {
return "Materialize";
}
@Override
public boolean equals(Object obj) {
if (obj instanceof MaterializeNode) {
MaterializeNode other = (MaterializeNode) obj;
return leftChild.equals(other.leftChild);
}
return false;
}
@Override
public int hashCode() {
return leftChild.hashCode();
}
}