-
Notifications
You must be signed in to change notification settings - Fork 0
/
Query.java
312 lines (253 loc) · 12.9 KB
/
Query.java
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
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.bind.EntryBinding;
import java.io.*;
import java.util.Vector;
/*****************************************************************************************************
This class contains the actual query evaluation and planning logic.
We currently only support queries of the form:
select [distinct] <list of attributes>
from <list of tables>
where <list of predicates>
order by <list of attributes>;
The Select Clause can either contain "*" or a list of fully defined attributes.
No aliasing is allowed.
Only equality predicates are allowed.
Queries should not contain cycles or should not require Cartesian products.
***************************************************************************************************/
public class Query {
/* All the information about the query. */
Vector<BaseRelationSchema> query_relations;
Vector<String> query_relation_names;
Vector<Predicate> query_predicates;
Vector<TupleAttribute> select_attributes;
boolean distinct;
Vector<TupleAttribute> order_by_attributes;
/* The root operator. We will execute the query by doing a get_next() on the root. */
Operator root;
/* Execute the query. */
void executeQuery() {
/* Initialize. root will recursively call init on its children. */
root.init();
/* Print out the select attributes. */
System.out.println("-------------------------------------------------------------------------");
for(TupleAttribute attr : select_attributes)
System.out.print(attr + " ");
System.out.println();
System.out.println("-------------------------------------------------------------------------");
/* Print out the result tuples one by one. */
Tuple t = null;
while((t = root.get_next()) != null) {
System.out.println(t);
}
System.out.println("-------------------------------------------------------------------------");
/* Close. */
root.close();
}
Query(ParsedStatement ps) {
query_relation_names = ps.fromTables;
query_predicates = ps.wherePredicates;
select_attributes = ps.selectAttributes;
distinct = ps.distinct;
order_by_attributes = ps.orderByAttributes;
}
/** Is the argument relation contained in the From Clause ? **/
boolean checkRelationContainedInFromClause(String name) {
for(BaseRelationSchema rs : query_relations)
if(rs.getName().equals(name))
return true;
return false;
}
/** Check if a tuple attribute reference is valid.
Disambiguate if needed. Also set the RelationSchema for the TupleAttribute. **/
boolean analyzeTupleAttribute(TupleAttribute ta) {
if(ta.tableName == null) {
BaseRelationSchema found_in = null;
for(BaseRelationSchema rs : query_relations) {
// System.out.println("searching for " + ta.attributeName + " in " + rs);
if(rs.hasAttribute(ta.attributeName)) {
if(found_in == null) {
found_in = rs;
} else {
System.out.println("=========> Attribute " + ta.attributeName + " ambiguous");
return false;
}
}
}
if(found_in != null) {
ta.tableName = found_in.getName();
ta.setRelationSchema(found_in);
return true;
} else {
System.out.println("=========> Attribute " + ta.attributeName + " not found in any of the tables in the FROM clause");
return false;
}
} else {
if(! checkRelationContainedInFromClause(ta.tableName)) {
System.out.println("=========> Relation " + ta.tableName + " not in the From Clause");
return false;
}
ta.setRelationSchema(Globals.getRelationSchema(ta.tableName));
if(! ta.rs.hasAttribute(ta.attributeName)) {
System.out.println("=========> Attribute " + ta.attributeName + " not present in the relation " + ta.tableName);
return false;
}
return true;
}
}
/* Construct the query object. Analyze, check for errors etc. */
boolean analyze() {
/* Get the relation schemas, and make sure the relations exist. */
query_relations = new Vector<BaseRelationSchema>();
for(String name : query_relation_names) {
BaseRelationSchema rs;
if( (rs = Globals.getRelationSchema(name)) == null) {
System.out.println("=========> Relation " + name + " does not exist");
return false;
}
query_relations.add(rs);
}
/* Now check all the predicates. */
for(Predicate p : query_predicates) {
/* Check the validity of the attributes. */
if( (p.lhs() instanceof TupleAttribute) && (!analyzeTupleAttribute((TupleAttribute) p.lhs())) )
return false;
if( (p.rhs() instanceof TupleAttribute) && (!analyzeTupleAttribute((TupleAttribute) p.rhs())) )
return false;
}
/* Finally check the select list. */
if(select_attributes.size() != 0) {
for(TupleAttribute ta : select_attributes) {
/* Check the validity of the attribute. */
if(!analyzeTupleAttribute(ta))
return false;
}
} else {
for(BaseRelationSchema rs : query_relations) {
for(String attrName : rs.attributeNames) {
TupleAttribute ta = new TupleAttribute(rs.getName(), attrName);
ta.setRelationSchema(rs);
select_attributes.add(ta);
}
}
}
return true;
}
/****************************************************************************************************************
Query Planning: We use a simple algorithm to create a query plan.
1. We create a Scan Operator for each relation in the query.
2. We start with the first join predicate, and make it the lowest join operator in the query plan. Its children
are two scan operators.
3. We find a join predicate such that one of the relations is already incorporated in the query plan, and we
ada a join operator corresponding to that join predicate.
4. We repeat Step 3 until join operators corresponding to all join predicates have been constructed.
The code below can only handle well-specified query: for n relation query, there should be exactly n-1 join
predicates, and it should be possible to evaluate the query without using Cartesian products.
**************************************************************************************************************/
/* List of operators created. We will split them into Scan operators, and Join operators. */
Vector<ScanOperator> scan_operators;
Vector<JoinOperator> join_operators;
/* We will simply create a scan operator for every relation in the query. */
void construct_scan_operators() {
scan_operators = new Vector<ScanOperator>();
for(BaseRelationSchema rs : query_relations) {
/* There may be predicates involving just that relation. */
Vector<Predicate> v = new Vector<Predicate>();
for(Predicate p : query_predicates)
if(p.isScanPredicate(rs))
v.add(p);
scan_operators.add(new ScanOperator(rs, v));
}
}
/* Find the scan operator corresponding to a schema. */
ScanOperator findScanOperator(BaseRelationSchema rs) {
for(ScanOperator so : scan_operators) {
if(so.getRelationSchema() == rs)
return so;
}
assert false : "Bug: This shouldn't happen";
return null;
}
/* Check if the Scan corresponding to the RelationSchema is already present in the join_operators. */
boolean relationAlreadyContainedInAJoinOperator(BaseRelationSchema rs) {
ScanOperator so = findScanOperator(rs);
for(JoinOperator jo : join_operators)
if( (so == jo.getLeftOperator()) || (so == jo.getRightOperator()) )
return true;
return false;
}
/* Create a query plan, the operators etc. */
boolean plan() {
construct_scan_operators();
if(query_relations.size() == 1) {
/* It is a single table query. We are essentially done. */
assert scan_operators.size() == 1;
root = scan_operators.get(0);
} else {
/* Before creating the join operators, we will first choose an order in which to do the joins.
For now, we will use a simple ordering that results in a left-deep plan.
We will start with any join predicate, and create a join operator for that using the Scans.
We will then choose some join predicate that does not require a Cartesian product. */
/* Let's first create a Vector containing the join predicates. */
Vector<Predicate> join_predicates_remaining = new Vector<Predicate>();
for(Predicate p : query_predicates)
if(p.isJoinPredicate())
join_predicates_remaining.add(p);
if(join_predicates_remaining.size() != (scan_operators.size() - 1)) {
System.out.println("=========> The query is not well formed");
return false;
}
join_operators = new Vector<JoinOperator>();
/* We will choose the first join operator arbitrarily. */
int num_join_predicates = join_predicates_remaining.size();
for(int i = 0; i < num_join_predicates; i++) {
if(i == 0) {
/* The first join predicate is used to create the bottommost join operator, with two scans as children. */
Predicate jp = join_predicates_remaining.get(0);
join_predicates_remaining.remove(0);
ScanOperator left = findScanOperator(jp.leftRelationSchema());
ScanOperator right = findScanOperator(jp.rightRelationSchema());
join_operators.add(JoinOperator.createNewJoinOperator(left, right, jp));
} else {
/* Find the first join predicate whose left or right RelationSchema is already present. */
Predicate next_jp = null;
for(Predicate jp : join_predicates_remaining) {
boolean leftContained = relationAlreadyContainedInAJoinOperator(jp.leftRelationSchema());
boolean rightContained = relationAlreadyContainedInAJoinOperator(jp.rightRelationSchema());
if(leftContained || rightContained) {
if(leftContained && rightContained) {
System.out.println("=========> Query not well-formed: There is a cycle in the query");
return false;
}
/* We have found a valid join predicate. Break. */
next_jp = jp;
break;
}
}
if(next_jp == null) {
System.out.println("=========> Query not well-formed: I think the query requires a Cartesian Product.");
return false;
}
join_predicates_remaining.removeElement(next_jp);
/* One quirk here is that we may need to swap the two arguments of the join predicate to match the left and right. */
if(relationAlreadyContainedInAJoinOperator(next_jp.rightRelationSchema()))
next_jp.swap();
join_operators.add(JoinOperator.createNewJoinOperator(join_operators.lastElement(), findScanOperator(next_jp.rightRelationSchema()), next_jp));
}
}
/* Finally: we need to set the root to be the very last join operator. */
root = join_operators.lastElement();
}
root = new ProjectOperator(root, select_attributes, distinct, order_by_attributes);
return true;
}
void print() {
System.out.println("========================================= Query Plan ========================================");
root.print();
System.out.println("=============================================================================================");
}
}