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
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
package edu.caltech.nanodb.plannodes;
import java.util.List;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import edu.caltech.nanodb.expressions.Expression;
import edu.caltech.nanodb.expressions.OrderByExpression;
import edu.caltech.nanodb.indexes.IndexInfo;
import edu.caltech.nanodb.queryeval.PlanCost;
import edu.caltech.nanodb.queryeval.SelectivityEstimator;
import edu.caltech.nanodb.queryeval.TableStats;
import edu.caltech.nanodb.relations.TableInfo;
import edu.caltech.nanodb.storage.FilePointer;
import edu.caltech.nanodb.storage.TupleFile;
/**
* <p>
* A select plan-node that scans a tuple file, checking the optional predicate
* against each tuple in the file. Note that there are no optimizations used
* if the tuple file is a sequential tuple file or a hashed tuple file.
* </p>
* <p>
* This plan node can also be used with indexes, when a "file-scan" is to be
* performed over all of the index's tuples, in whatever order the index will
* produce the tuples. If the planner wishes to take advantage of an index's
* ability to look up tuples based on various values, the {@link IndexScanNode}
* should be used instead.
* </p>
*/
public class FileScanNode extends SelectNode {
/** A logging object for reporting anything interesting that happens. */
private static Logger logger = LogManager.getLogger(FileScanNode.class);
/**
* The table-info for the table being scanned, or {@code null} if the node
* is performing a scan over an index.
*/
private TableInfo tableInfo;
/**
* The index-info for the index being scanned, or {@code null} if the node
* is performing a scan over a table.
*/
private IndexInfo indexInfo;
/** The table to select from if this node is a leaf. */
private TupleFile tupleFile;
/**
* This field allows the file-scan node to mark a particular tuple in the
* tuple-stream and then rewind to that point in the tuple-stream.
*/
private FilePointer markedTuple;
private boolean jumpToMarkedTuple;
/**
* Construct a file scan node that traverses a table file.
*
* @param tableInfo the information about the table being scanned
* @param predicate an optional predicate for selection, or {@code null}
* if all rows in the table should be included in the output
*/
public FileScanNode(TableInfo tableInfo, Expression predicate) {
super(predicate);
if (tableInfo == null)
throw new IllegalArgumentException("tableInfo cannot be null");
this.tableInfo = tableInfo;
tupleFile = tableInfo.getTupleFile();
}
/**
* Construct a file scan node that traverses an index file.
*
* @param indexInfo the information about the index being scanned
* @param predicate an optional predicate for selection, or {@code null}
* if all rows in the index should be included in the output
*/
public FileScanNode(IndexInfo indexInfo, Expression predicate) {
super(predicate);
if (indexInfo == null)
throw new IllegalArgumentException("indexInfo cannot be null");
this.indexInfo = indexInfo;
tupleFile = indexInfo.getTupleFile();
}
/**
* Returns true if the passed-in object is a <tt>FileScanNode</tt> with
* the same predicate and table.
*
* @param obj the object to check for equality
*
* @return true if the passed-in object is equal to this object; false
* otherwise
*/
@Override
public boolean equals(Object obj) {
if (obj instanceof FileScanNode) {
FileScanNode other = (FileScanNode) obj;
// We don't include the table-info or the index-info since each
// table or index is in its own tuple file.
return tupleFile.equals(other.tupleFile) &&
predicate.equals(other.predicate);
}
return false;
}
/**
* Computes the hashcode of a PlanNode. This method is used to see if two
* plan nodes CAN be equal.
**/
public int hashCode() {
int hash = 7;
hash = 31 * hash + (predicate != null ? predicate.hashCode() : 0);
// We don't include the table-info or the index-info since each table
// or index is in its own tuple file.
hash = 31 * hash + tupleFile.hashCode();
return hash;
}
/**
* Creates a copy of this simple filter node node and its subtree. This
* method is used by {@link PlanNode#duplicate} to copy a plan tree.
*/
@Override
protected PlanNode clone() throws CloneNotSupportedException {
FileScanNode node = (FileScanNode) super.clone();
// TODO: Should we clone these?
node.tableInfo = tableInfo;
node.indexInfo = indexInfo;
// The tuple file doesn't need to be copied since it's immutable.
node.tupleFile = tupleFile;
return node;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("FileScan[");
if (tableInfo != null) {
buf.append("table: ").append(tableInfo.getTableName());
}
else if (indexInfo != null) {
buf.append("index: ").append(indexInfo.getTableName());
buf.append('.').append(indexInfo.getIndexName());
}
else {
throw new IllegalStateException("Both tableInfo and indexInfo " +
"are null!");
}
if (predicate != null)
buf.append(", pred: ").append(predicate.toString());
buf.append("]");
return buf.toString();
}
/**
* Currently we will always say that the file-scan node produces unsorted
* results. In actuality, a file scan's results will be sorted if the table
* file uses a sequential format, but currently we don't have any sequential
* file formats.
*/
public List<OrderByExpression> resultsOrderedBy() {
return null;
}
/** This node supports marking. */
public boolean supportsMarking() {
return true;
}
/** This node has no children so of course it doesn't require marking. */
public boolean requiresLeftMarking() {
return false;
}
/** This node has no children so of course it doesn't require marking. */
public boolean requiresRightMarking() {
return false;
}
protected void prepareSchema() {
// Grab the schema from the table.
schema = tupleFile.getSchema();
}
// Inherit javadocs from base class.
public void prepare() {
// Grab the schema and statistics from the table file.
schema = tupleFile.getSchema();
TableStats tableStats = tupleFile.getStats();
// TODO: Compute the cost of the plan node!
cost = null;
// TODO: Update the statistics based on the predicate.
stats = tableStats.getAllColumnStats();
}
public void initialize() {
super.initialize();
// Reset our marking state.
markedTuple = null;
jumpToMarkedTuple = false;
}
public void cleanUp() {
// Nothing to do!
}
/**
* Advances the current tuple forward for a file scan. Grabs the first
* tuple if current is null. Otherwise gets the next tuple.
*/
protected void advanceCurrentTuple() {
if (jumpToMarkedTuple) {
logger.debug("Resuming at previously marked tuple.");
currentTuple = tupleFile.getTuple(markedTuple);
jumpToMarkedTuple = false;
return;
}
if (currentTuple == null) // Get the first tuple.
currentTuple = tupleFile.getFirstTuple();
else // Get the next tuple.
currentTuple = tupleFile.getNextTuple(currentTuple);
}
public void markCurrentPosition() {
if (currentTuple == null)
throw new IllegalStateException("There is no current tuple!");
logger.debug("Marking current position in tuple-stream.");
markedTuple = currentTuple.getExternalReference();
}
public void resetToLastMark() {
if (markedTuple == null)
throw new IllegalStateException("There is no last-marked tuple!");
logger.debug("Resetting to previously marked position in tuple-stream.");
jumpToMarkedTuple = true;
}
}