1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.hadoop.hbase.hbql.statement.args;
22
23 import org.apache.expreval.client.InternalErrorException;
24 import org.apache.expreval.client.NullColumnValueException;
25 import org.apache.expreval.client.ResultMissingColumnException;
26 import org.apache.expreval.expr.TypeSupport;
27 import org.apache.expreval.expr.node.GenericValue;
28 import org.apache.hadoop.hbase.client.Get;
29 import org.apache.hadoop.hbase.client.Scan;
30 import org.apache.hadoop.hbase.hbql.client.HBqlException;
31 import org.apache.hadoop.hbase.hbql.io.IO;
32 import org.apache.hadoop.hbase.hbql.mapping.ColumnAttrib;
33 import org.apache.hadoop.hbase.hbql.statement.select.GetRequest;
34 import org.apache.hadoop.hbase.hbql.statement.select.IndexRequest;
35 import org.apache.hadoop.hbase.hbql.statement.select.RowRequest;
36 import org.apache.hadoop.hbase.hbql.statement.select.ScanRequest;
37 import org.apache.hadoop.hbase.hbql.util.Lists;
38
39 import java.util.Arrays;
40 import java.util.Collection;
41 import java.util.List;
42 import java.util.Set;
43
44 public class KeyRange extends SelectStatementArgs {
45
46 private enum RangeType {
47 SINGLE, RANGE, FIRST, LAST, ALL
48 }
49
50 private final RangeType rangeType;
51
52 private KeyRange() {
53 super(ArgType.NOARGSKEY);
54 this.rangeType = RangeType.ALL;
55 }
56
57 private KeyRange(final RangeType rangeType, final GenericValue arg0) {
58 super(ArgType.SINGLEKEY, arg0);
59 this.rangeType = rangeType;
60 }
61
62 private KeyRange(final GenericValue arg0, final GenericValue arg1) {
63 super(ArgType.KEYRANGE, arg0, arg1);
64 this.rangeType = RangeType.RANGE;
65 }
66
67 public static KeyRange newRange(final GenericValue arg0, final GenericValue arg1) {
68 return new KeyRange(arg0, arg1);
69 }
70
71 public static KeyRange newSingleKey(final GenericValue arg0) {
72 return new KeyRange(RangeType.SINGLE, arg0);
73 }
74
75 public static KeyRange newFirstRange(final GenericValue arg0) {
76 return new KeyRange(RangeType.FIRST, arg0);
77 }
78
79 public static KeyRange newLastRange(final GenericValue arg0) {
80 return new KeyRange(RangeType.LAST, arg0);
81 }
82
83 public static KeyRange newAllRange() {
84 return new KeyRange();
85 }
86
87 private RangeType getKeyRangeType() {
88 return this.rangeType;
89 }
90
91 public boolean isSingleKey() {
92 return this.getKeyRangeType() == RangeType.SINGLE;
93 }
94
95 public void validate() throws HBqlException {
96 this.validateTypes(this.allowColumns(), this.isSingleKey());
97 }
98
99
100 private Object getFirstArg(final boolean allowCollections) throws HBqlException {
101 return this.evaluateConstant(0, allowCollections);
102 }
103
104 private String getSecondArg() throws HBqlException {
105 return (String)this.evaluateConstant(1, false);
106 }
107
108 public List<RowRequest> getRowRequestList(final WithArgs withArgs,
109 final ColumnAttrib keyAttrib,
110 final Set<ColumnAttrib> columnAttribs) throws HBqlException {
111
112 return this.isSingleKey() ? this.getGetRequest(withArgs, keyAttrib, columnAttribs)
113 : this.getScanRequest(withArgs, keyAttrib, columnAttribs);
114 }
115
116 private List<RowRequest> getGetRequest(final WithArgs withArgs,
117 final ColumnAttrib keyAttrib,
118 final Set<ColumnAttrib> columnAttribs) throws HBqlException {
119
120 final List<RowRequest> rowRequestList = Lists.newArrayList();
121
122
123 final Object objval = this.getFirstArg(true);
124 if (TypeSupport.isACollection(objval)) {
125 for (final GenericValue val : (Collection<GenericValue>)objval) {
126 try {
127 final String rangeValue = (String)val.getValue(null, null);
128 final RowRequest rowRequest = this.newGet(withArgs, columnAttribs, keyAttrib, rangeValue);
129 rowRequestList.add(rowRequest);
130 }
131 catch (ResultMissingColumnException e) {
132 throw new InternalErrorException("Missing column: " + val.asString());
133 }
134 catch (NullColumnValueException e) {
135 throw new InternalErrorException("Null value: " + e.getMessage());
136 }
137 }
138 }
139 else {
140 final String rangeValue = (String)objval;
141 final RowRequest rowRequest = this.newGet(withArgs, columnAttribs, keyAttrib, rangeValue);
142 rowRequestList.add(rowRequest);
143 }
144
145 return rowRequestList;
146 }
147
148 private RowRequest newGet(final WithArgs withArgs,
149 final Set<ColumnAttrib> columnAttribs,
150 final ColumnAttrib keyAttrib,
151 final String rangeValue) throws HBqlException {
152
153 this.verifyRangeValueWidth(keyAttrib, rangeValue);
154
155 final byte[] lowerBytes = IO.getSerialization().getStringAsBytes(rangeValue);
156
157 if (withArgs.hasAnIndex()) {
158 final byte[] upperBytes = Arrays.copyOf(lowerBytes, lowerBytes.length);
159
160 upperBytes[lowerBytes.length - 1]++;
161 return new IndexRequest(lowerBytes, upperBytes, columnAttribs);
162 }
163 else {
164
165 if (withArgs.getServerExpressionTree() == null) {
166 final Get get = new Get(lowerBytes);
167 withArgs.setGetArgs(get, columnAttribs);
168 return new GetRequest(get);
169 }
170 else {
171
172 final Scan scan = new Scan();
173
174 scan.setStartRow(lowerBytes);
175
176 final byte[] upperBytes = Arrays.copyOf(lowerBytes, lowerBytes.length);
177
178 upperBytes[lowerBytes.length - 1]++;
179 scan.setStopRow(upperBytes);
180
181 withArgs.setScanArgs(scan, columnAttribs);
182 return new ScanRequest(scan);
183 }
184 }
185 }
186
187 private List<RowRequest> getScanRequest(final WithArgs withArgs,
188 final ColumnAttrib keyAttrib,
189 final Set<ColumnAttrib> columnAttribs) throws HBqlException {
190
191 final Scan scan = new Scan();
192 this.setStartStopRows(scan, keyAttrib);
193 withArgs.setScanArgs(scan, columnAttribs);
194 final RowRequest rowRequest = withArgs.hasAnIndex()
195 ? new IndexRequest(scan.getStartRow(), scan.getStopRow(), columnAttribs)
196 : new ScanRequest(scan);
197
198 return Lists.newArrayList(rowRequest);
199 }
200
201 private void setStartStopRows(final Scan scan, final ColumnAttrib keyAttrib) throws HBqlException {
202
203 switch (this.getKeyRangeType()) {
204 case ALL: {
205
206 break;
207 }
208 case FIRST: {
209 final String rangeValue = (String)this.getFirstArg(false);
210 this.verifyRangeValueWidth(keyAttrib, rangeValue);
211 final byte[] upperBytes = IO.getSerialization().getStringAsBytes(rangeValue);
212 scan.setStopRow(upperBytes);
213 break;
214 }
215 case LAST: {
216 final String rangeValue = (String)this.getFirstArg(false);
217 this.verifyRangeValueWidth(keyAttrib, rangeValue);
218 final byte[] lowerBytes = IO.getSerialization().getStringAsBytes(rangeValue);
219 scan.setStartRow(lowerBytes);
220 break;
221 }
222 case RANGE: {
223 final String firstRangeValue = (String)this.getFirstArg(false);
224 final String secondRangeValue = this.getSecondArg();
225 this.verifyRangeValueWidth(keyAttrib, firstRangeValue, secondRangeValue);
226 final byte[] lowerBytes = IO.getSerialization().getStringAsBytes(firstRangeValue);
227 final byte[] upperBytes = IO.getSerialization().getStringAsBytes(secondRangeValue);
228 scan.setStartRow(lowerBytes);
229 scan.setStopRow(upperBytes);
230 break;
231 }
232 default:
233 throw new InternalErrorException("Invalid range type: " + this.getKeyRangeType().name());
234 }
235 }
236
237 private void verifyRangeValueWidth(final ColumnAttrib keyAttrib, final String... rangeValues) throws HBqlException {
238
239 final ColumnWidth columnWidth = keyAttrib.getColumnDefinition().getColumnWidth();
240 if (columnWidth.isWidthSpecified()) {
241 for (final String rangeValue : rangeValues) {
242 final int width = columnWidth.getWidth();
243 if (width > 0 && rangeValue.length() != width)
244 throw new HBqlException("Invalid key range length in " + this.asString()
245 + " expecting width " + width + " but found " + rangeValue.length()
246 + " with key \"" + rangeValue + "\"");
247 }
248 }
249 }
250
251 public String asString() {
252
253 final StringBuilder sbuf = new StringBuilder();
254
255 switch (this.getKeyRangeType()) {
256 case ALL: {
257 sbuf.append("KEYS ALL");
258 break;
259 }
260 case SINGLE: {
261 sbuf.append("KEY " + this.getGenericValue(0).asString());
262 break;
263 }
264 case FIRST: {
265 sbuf.append("KEYS FIRST TO " + this.getGenericValue(0).asString());
266 break;
267 }
268 case LAST: {
269 sbuf.append("KEYS " + this.getGenericValue(0).asString() + "' TO LAST");
270 break;
271 }
272 case RANGE: {
273 sbuf.append("KEYS " + this.getGenericValue(0).asString());
274 sbuf.append(" TO ");
275 sbuf.append(this.getGenericValue(1).asString());
276 break;
277 }
278 default:
279 throw new RuntimeException("Invalid range type: " + this.getKeyRangeType().name());
280 }
281
282 return sbuf.toString();
283 }
284 }