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.SKIP;
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 MatchCode.SEEK_NEXT_COL;
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 }
198 }
199
200 MatchCode colChecker = columns.checkColumn(bytes, offset, qualLength);
201
202
203
204 if (colChecker == MatchCode.SKIP) {
205 return MatchCode.SEEK_NEXT_COL;
206 } else if (colChecker == MatchCode.NEXT || colChecker == MatchCode.DONE) {
207 stickyNextRow = true;
208 return MatchCode.SEEK_NEXT_ROW;
209 }
210
211 return MatchCode.INCLUDE;
212
213 }
214
215 public MatchCode getNextRowOrNextColumn(byte[] bytes, int offset,
216 int qualLength) {
217 if (columns instanceof ExplicitColumnTracker) {
218
219
220 ((ExplicitColumnTracker)columns).doneWithColumn(bytes, offset,
221 qualLength);
222 if (columns.getColumnHint() == null) {
223 return MatchCode.SEEK_NEXT_ROW;
224 } else {
225 return MatchCode.SEEK_NEXT_COL;
226 }
227 } else {
228 return MatchCode.SEEK_NEXT_COL;
229 }
230 }
231
232 public boolean moreRowsMayExistAfter(KeyValue kv) {
233 if (!Bytes.equals(stopRow , HConstants.EMPTY_END_ROW) &&
234 rowComparator.compareRows(kv.getBuffer(),kv.getRowOffset(),
235 kv.getRowLength(), stopRow, 0, stopRow.length) >= 0) {
236 return false;
237 } else {
238 return true;
239 }
240 }
241
242
243
244
245
246 public void setRow(byte [] row) {
247 this.row = row;
248 reset();
249 }
250
251 public void reset() {
252 this.deletes.reset();
253 this.columns.reset();
254
255 stickyNextRow = false;
256 }
257
258
259 protected boolean isDelete(byte type) {
260 return (type != KeyValue.Type.Put.getCode());
261 }
262
263 protected boolean isExpired(long timestamp) {
264 return (timestamp < oldestStamp);
265 }
266
267
268
269
270
271 public KeyValue getStartKey() {
272 return this.startKey;
273 }
274
275
276
277
278
279
280
281
282 public static enum MatchCode {
283
284
285
286 INCLUDE,
287
288
289
290
291 SKIP,
292
293
294
295
296 NEXT,
297
298
299
300
301 DONE,
302
303
304
305
306
307
308
309
310 SEEK_NEXT_ROW,
311
312
313
314 SEEK_NEXT_COL,
315
316
317
318
319 DONE_SCAN,
320 }
321 }