-
Notifications
You must be signed in to change notification settings - Fork 2
/
QueryParser.java
193 lines (171 loc) · 4.69 KB
/
QueryParser.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
/**
*
*/
package sjdb;
import java.io.*;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
/**
* This class parses a canonical query provided on stdin
*
* The canonical query is of the form:
*
* SELECT <attribute name>,<attribute name>,...,<attribute name>
* FROM <relation name>,<relation name>,...,<relation name>
* WHERE <predicate>,<predicate>,...,<predicate>
*
* where <predicate> is of one of the following two forms:
*
* <attribute name>="<value>"
* <attribute name>=<attribute name>
*
* The WHERE line (corresponding to the select operators) is optional and
* may be omitted; the other lines are required.
*
* To form the canonical query, a left-deep tree of cartesian
* products over scans over the named relations is built, following by a series
* of select with the given predicates, and then a single project
* with the given attributes.
*
* Note that the author of this class was extremely lazy, and so the parsing
* is unforgiving and may be sensitive to extraneous whitespace. In particular,
* values in predicates that contain spaces (or for that matter commas) will
* break the parsing of the WHERE clause.
*
* @author nmg
*/
public class QueryParser {
private BufferedReader reader;
private Catalogue catalogue;
/**
* Create a new QueryParser. This class is intended to be used once only;
* repeated calls to parse() may cause unexpected behaviour.
*
* @param catalogue
* @param input
* @throws Exception
*/
public QueryParser(Catalogue catalogue, Reader input) throws Exception {
this.catalogue = catalogue;
this.reader = new BufferedReader(input);
}
/**
* Read a query from the input (via the BufferedReader) and parse it
* to create a canonical query plan.
*
* @return
* @throws Exception
*/
public Operator parse() throws Exception {
Operator product, select, project;
String projectLine = this.reader.readLine();
String productLine = this.reader.readLine();
String selectLine = this.reader.readLine();
product = parseProduct(productLine);
if (selectLine != null && selectLine.startsWith("WHERE")) {
select = parseSelect(selectLine, product);
project = parseProject(projectLine, select);
} else {
project = parseProject(projectLine, product);
}
return project;
}
/**
* Parse a "FROM ..." line
* @param line
* @return
*/
public Operator parseProduct(String line) {
String[] rels = line.split("FROM\\s+");
String[] reln = rels[1].split("\\s*,\\s*");
return buildProduct(reln);
}
/**
* Build a left-deep cartesian product tree from the relations
* with the given names
* @param names
* @return
*/
private Operator buildProduct(String[] names) {
Operator left = buildScan(names[0].trim());
Operator right;
Operator accum;
if (names.length>1) {
for (int i = 1; i < names.length; i++) {
right = buildScan(names[i].trim());
accum = new Product(left, right);
left = accum;
}
}
return left;
}
/**
* Build a scan operator that reads the relation with the given
* name
* @param name
* @return
*/
private Operator buildScan(String name) {
Operator op = null;
try {
op = new Scan(this.catalogue.getRelation(name));
} catch (Exception e) {
System.err.println(e.toString());
}
return op;
}
/**
* Parse a "WHERE ..." line.
* @param line
* @param op
* @return
*/
private Operator parseSelect(String line, Operator op) {
String[] prds = line.split("WHERE\\s+");
String[] pred = prds[1].split("\\s*,\\s*");
Operator ret = op;
for (int i=0; i<pred.length; i++) {
ret = buildSelect(pred[i].trim(), ret);
}
return ret;
}
/**
* Build a chain of select operators.
* @param pred
* @param op
* @return
*/
private Operator buildSelect(String pred, Operator op) {
Pattern p = Pattern.compile("(\\w+)=\"(\\w+)\"");
Matcher m = p.matcher(pred);
Predicate ret;
if (m.matches()) {
ret = new Predicate(new Attribute(m.group(1)), m.group(2));
} else {
String[] atts = pred.split("=");
ret = new Predicate(new Attribute(atts[0]), new Attribute(atts[1]));
}
return new Select(op, ret);
}
/**
* Parse a "SELECT ..." line and build the corresponding project operator.
* @param line
* @param op
* @return
*/
private Operator parseProject(String line, Operator op) {
String[] atts = line.split("SELECT\\s+");
if (atts[1].trim().equals("*")) {
return op;
} else {
String[] attr = atts[1].split("\\s*,\\s*");
ArrayList<Attribute> attributes = new ArrayList<Attribute>();
for (int i=0; i<attr.length; i++) {
attributes.add(new Attribute(attr[i].trim()));
}
return new Project(op, attributes);
}
}
}