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.regionserver;
22
23 import org.apache.hadoop.hbase.HConstants;
24 import org.apache.hadoop.hbase.KeyValue;
25 import org.apache.hadoop.hbase.client.Scan;
26 import org.apache.hadoop.hbase.filter.Filter;
27 import org.apache.hadoop.hbase.filter.Filter.ReturnCode;
28 import org.apache.hadoop.hbase.io.TimeRange;
29 import org.apache.hadoop.hbase.util.Bytes;
30
31 import java.util.NavigableSet;
32
33
34
35
36 public class ScanQueryMatcher {
37
38
39 private boolean stickyNextRow;
40 private byte[] stopRow;
41
42 protected TimeRange tr;
43
44 protected Filter filter;
45
46
47 protected DeleteTracker deletes;
48
49
50 protected ColumnTracker columns;
51
52
53 protected KeyValue startKey;
54
55
56 protected long oldestStamp;
57
58
59 KeyValue.KeyComparator rowComparator;
60
61
62 protected byte [] row;
63
64
65
66
67
68
69
70
71
72 public ScanQueryMatcher(Scan scan, byte [] family,
73 NavigableSet<byte[]> columns, long ttl,
74 KeyValue.KeyComparator rowComparator, int maxVersions) {
75 this.tr = scan.getTimeRange();
76 this.oldestStamp = System.currentTimeMillis() - ttl;
77 this.rowComparator = rowComparator;
78 this.deletes = new ScanDeleteTracker();
79 this.stopRow = scan.getStopRow();
80 this.startKey = KeyValue.createFirstOnRow(scan.getStartRow());
81 this.filter = scan.getFilter();
82
83
84 if (columns == null || columns.size() == 0) {
85
86 this.columns = new ScanWildcardColumnTracker(maxVersions);
87 } else {
88
89
90 this.columns = new ExplicitColumnTracker(columns,maxVersions);
91 }
92 }
93
94
95
96
97
98
99
100
101
102
103
104
105 public MatchCode match(KeyValue kv) {
106 if (filter != null && filter.filterAllRemaining()) {
107 return MatchCode.DONE_SCAN;
108 }
109
110 byte [] bytes = kv.getBuffer();
111 int offset = kv.getOffset();
112 int initialOffset = offset;
113
114 int keyLength = Bytes.toInt(bytes, offset, Bytes.SIZEOF_INT);
115 offset += KeyValue.ROW_OFFSET;
116
117 short rowLength = Bytes.toShort(bytes, offset, Bytes.SIZEOF_SHORT);
118 offset += Bytes.SIZEOF_SHORT;
119
120 int ret = this.rowComparator.compareRows(row, 0, row.length,
121 bytes, offset, rowLength);
122 if (ret <= -1) {
123 return MatchCode.DONE;
124 } else if (ret >= 1) {
125
126
127
128 return MatchCode.SEEK_NEXT_ROW;
129 }
130
131
132 if (this.stickyNextRow)
133 return MatchCode.SEEK_NEXT_ROW;
134
135 if (this.columns.done()) {
136 stickyNextRow = true;
137 return MatchCode.SEEK_NEXT_ROW;
138 }
139
140
141 offset += rowLength;
142
143
144 byte familyLength = bytes [offset];
145 offset += familyLength + 1;
146
147 int qualLength = keyLength + KeyValue.ROW_OFFSET -
148 (offset - initialOffset) - KeyValue.TIMESTAMP_TYPE_SIZE;
149
150 long timestamp = kv.getTimestamp();
151 if (isExpired(timestamp)) {
152
153 return getNextRowOrNextColumn(bytes, offset, qualLength);
154 }
155
156 byte type = kv.getType();
157 if (isDelete(type)) {
158 if (tr.withinOrAfterTimeRange(timestamp)) {
159 this.deletes.add(bytes, offset, qualLength, timestamp, type);
160
161 }
162
163
164
165
166
167 return MatchCode.SKIP;
168 }
169
170 if (!this.deletes.isEmpty() &&
171 deletes.isDeleted(bytes, offset, qualLength, timestamp)) {
172 return MatchCode.SKIP;
173 }
174
175 int timestampComparison = tr.compare(timestamp);
176 if (timestampComparison >= 1) {
177 return MatchCode.SKIP;
178 } else if (timestampComparison <= -1) {
179 return getNextRowOrNextColumn(bytes, offset, qualLength);
180 }
181
182
183
184
185
186
187
188 if (filter != null) {
189 ReturnCode filterResponse = filter.filterKeyValue(kv);
190 if (filterResponse == ReturnCode.SKIP) {
191 return MatchCode.SKIP;
192 } else if (filterResponse == ReturnCode.NEXT_COL) {
193 return getNextRowOrNextColumn(bytes, offset, qualLength);
194 } else if (filterResponse == ReturnCode.NEXT_ROW) {
195 stickyNextRow = true;
196 return MatchCode.SEEK_NEXT_ROW;
197 } else if (filterResponse == ReturnCode.SEEK_NEXT_USING_HINT) {
198 return MatchCode.SEEK_NEXT_USING_HINT;
199 }
200 }
201
202 MatchCode colChecker = columns.checkColumn(bytes, offset, qualLength);
203
204
205
206 if (colChecker == MatchCode.SKIP) {
207 return MatchCode.SEEK_NEXT_COL;
208 } else if (colChecker == MatchCode.NEXT || colChecker == MatchCode.DONE) {
209 stickyNextRow = true;
210 return MatchCode.SEEK_NEXT_ROW;
211 }
212
213 return MatchCode.INCLUDE;
214
215 }
216
217 public MatchCode getNextRowOrNextColumn(byte[] bytes, int offset,
218 int qualLength) {
219 if (columns instanceof ExplicitColumnTracker) {
220
221
222 ((ExplicitColumnTracker)columns).doneWithColumn(bytes, offset,
223 qualLength);
224 if (columns.getColumnHint() == null) {
225 return MatchCode.SEEK_NEXT_ROW;
226 } else {
227 return MatchCode.SEEK_NEXT_COL;
228 }
229 } else {
230 return MatchCode.SEEK_NEXT_COL;
231 }
232 }
233
234 public boolean moreRowsMayExistAfter(KeyValue kv) {
235 if (!Bytes.equals(stopRow , HConstants.EMPTY_END_ROW) &&
236 rowComparator.compareRows(kv.getBuffer(),kv.getRowOffset(),
237 kv.getRowLength(), stopRow, 0, stopRow.length) >= 0) {
238 return false;
239 } else {
240 return true;
241 }
242 }
243
244
245
246
247
248 public void setRow(byte [] row) {
249 this.row = row;
250 reset();
251 }
252
253 public void reset() {
254 this.deletes.reset();
255 this.columns.reset();
256
257 stickyNextRow = false;
258 }
259
260
261 protected boolean isDelete(byte type) {
262 return (type != KeyValue.Type.Put.getCode());
263 }
264
265 protected boolean isExpired(long timestamp) {
266 return (timestamp < oldestStamp);
267 }
268
269
270
271
272
273 public KeyValue getStartKey() {
274 return this.startKey;
275 }
276
277 public KeyValue getNextKeyHint(KeyValue kv) {
278 if (filter == null) {
279 return null;
280 } else {
281 return filter.getNextKeyHint(kv);
282 }
283 }
284
285
286
287
288
289
290
291
292 public static enum MatchCode {
293
294
295
296 INCLUDE,
297
298
299
300
301 SKIP,
302
303
304
305
306 NEXT,
307
308
309
310
311 DONE,
312
313
314
315
316
317
318
319
320 SEEK_NEXT_ROW,
321
322
323
324 SEEK_NEXT_COL,
325
326
327
328
329 DONE_SCAN,
330
331
332
333
334 SEEK_NEXT_USING_HINT,
335 }
336 }