1 /**
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20 package org.apache.hadoop.hbase.regionserver;
21
22 import java.io.IOException;
23 import java.util.NavigableSet;
24
25 import org.apache.hadoop.hbase.KeyValue.Type;
26 import org.apache.hadoop.hbase.classification.InterfaceAudience;
27 import org.apache.hadoop.hbase.Cell;
28 import org.apache.hadoop.hbase.CellUtil;
29 import org.apache.hadoop.hbase.HConstants;
30 import org.apache.hadoop.hbase.KeepDeletedCells;
31 import org.apache.hadoop.hbase.KeyValue;
32 import org.apache.hadoop.hbase.KeyValueUtil;
33 import org.apache.hadoop.hbase.client.Scan;
34 import org.apache.hadoop.hbase.filter.Filter;
35 import org.apache.hadoop.hbase.filter.Filter.ReturnCode;
36 import org.apache.hadoop.hbase.io.TimeRange;
37 import org.apache.hadoop.hbase.regionserver.DeleteTracker.DeleteResult;
38 import org.apache.hadoop.hbase.util.Bytes;
39 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
40
41 import com.google.common.base.Preconditions;
42
43 /**
44 * A query matcher that is specifically designed for the scan case.
45 */
46 @InterfaceAudience.Private
47 public class ScanQueryMatcher {
48 // Optimization so we can skip lots of compares when we decide to skip
49 // to the next row.
50 private boolean stickyNextRow;
51 private final byte[] stopRow;
52
53 private final TimeRange tr;
54
55 private final Filter filter;
56
57 /** Keeps track of deletes */
58 private final DeleteTracker deletes;
59
60 /*
61 * The following three booleans define how we deal with deletes.
62 * There are three different aspects:
63 * 1. Whether to keep delete markers. This is used in compactions.
64 * Minor compactions always keep delete markers.
65 * 2. Whether to keep deleted rows. This is also used in compactions,
66 * if the store is set to keep deleted rows. This implies keeping
67 * the delete markers as well.
68 * In this case deleted rows are subject to the normal max version
69 * and TTL/min version rules just like "normal" rows.
70 * 3. Whether a scan can do time travel queries even before deleted
71 * marker to reach deleted rows.
72 */
73 /** whether to retain delete markers */
74 private boolean retainDeletesInOutput;
75
76 /** whether to return deleted rows */
77 private final KeepDeletedCells keepDeletedCells;
78 /** whether time range queries can see rows "behind" a delete */
79 private final boolean seePastDeleteMarkers;
80
81
82 /** Keeps track of columns and versions */
83 private final ColumnTracker columns;
84
85 /** Key to seek to in memstore and StoreFiles */
86 private final Cell startKey;
87
88 /** Row comparator for the region this query is for */
89 private final KeyValue.KVComparator rowComparator;
90
91 /* row is not private for tests */
92 /** Row the query is on */
93 byte [] row;
94 int rowOffset;
95 short rowLength;
96
97 /**
98 * Oldest put in any of the involved store files
99 * Used to decide whether it is ok to delete
100 * family delete marker of this store keeps
101 * deleted KVs.
102 */
103 private final long earliestPutTs;
104 private final long ttl;
105
106 /** The oldest timestamp we are interested in, based on TTL */
107 private final long oldestUnexpiredTS;
108 private final long now;
109
110 /** readPoint over which the KVs are unconditionally included */
111 protected long maxReadPointToTrackVersions;
112
113 private byte[] dropDeletesFromRow = null, dropDeletesToRow = null;
114
115 /**
116 * This variable shows whether there is an null column in the query. There
117 * always exists a null column in the wildcard column query.
118 * There maybe exists a null column in the explicit column query based on the
119 * first column.
120 * */
121 private boolean hasNullColumn = true;
122
123 private RegionCoprocessorHost regionCoprocessorHost= null;
124
125 // By default, when hbase.hstore.time.to.purge.deletes is 0ms, a delete
126 // marker is always removed during a major compaction. If set to non-zero
127 // value then major compaction will try to keep a delete marker around for
128 // the given number of milliseconds. We want to keep the delete markers
129 // around a bit longer because old puts might appear out-of-order. For
130 // example, during log replication between two clusters.
131 //
132 // If the delete marker has lived longer than its column-family's TTL then
133 // the delete marker will be removed even if time.to.purge.deletes has not
134 // passed. This is because all the Puts that this delete marker can influence
135 // would have also expired. (Removing of delete markers on col family TTL will
136 // not happen if min-versions is set to non-zero)
137 //
138 // But, if time.to.purge.deletes has not expired then a delete
139 // marker will not be removed just because there are no Puts that it is
140 // currently influencing. This is because Puts, that this delete can
141 // influence. may appear out of order.
142 private final long timeToPurgeDeletes;
143
144 private final boolean isUserScan;
145
146 private final boolean isReversed;
147
148 /**
149 * True if we are doing a 'Get' Scan. Every Get is actually a one-row Scan.
150 */
151 private final boolean get;
152
153 /**
154 * Construct a QueryMatcher for a scan
155 * @param scanInfo The store's immutable scan info
156 * @param scanType Type of the scan
157 * @param earliestPutTs Earliest put seen in any of the store files.
158 * @param oldestUnexpiredTS the oldest timestamp we are interested in, based on TTL
159 */
160 public ScanQueryMatcher(Scan scan, ScanInfo scanInfo, NavigableSet<byte[]> columns,
161 ScanType scanType, long readPointToUse, long earliestPutTs, long oldestUnexpiredTS,
162 long now, RegionCoprocessorHost regionCoprocessorHost) throws IOException {
163 TimeRange timeRange = scan.getColumnFamilyTimeRange().get(scanInfo.getFamily());
164 if (timeRange == null) {
165 this.tr = scan.getTimeRange();
166 } else {
167 this.tr = timeRange;
168 }
169 this.get = scan.isGetScan();
170 this.rowComparator = scanInfo.getComparator();
171 this.regionCoprocessorHost = regionCoprocessorHost;
172 this.deletes = instantiateDeleteTracker();
173 this.stopRow = scan.getStopRow();
174 this.startKey = KeyValueUtil.createFirstDeleteFamilyOnRow(scan.getStartRow(),
175 scanInfo.getFamily());
176 this.filter = scan.getFilter();
177 this.earliestPutTs = earliestPutTs;
178 this.oldestUnexpiredTS = oldestUnexpiredTS;
179 this.now = now;
180
181 this.maxReadPointToTrackVersions = readPointToUse;
182 this.timeToPurgeDeletes = scanInfo.getTimeToPurgeDeletes();
183 this.ttl = oldestUnexpiredTS;
184
185 /* how to deal with deletes */
186 this.isUserScan = scanType == ScanType.USER_SCAN;
187 // keep deleted cells: if compaction or raw scan
188 this.keepDeletedCells = scan.isRaw() ? KeepDeletedCells.TRUE :
189 isUserScan ? KeepDeletedCells.FALSE : scanInfo.getKeepDeletedCells();
190 // retain deletes: if minor compaction or raw scanisDone
191 this.retainDeletesInOutput = scanType == ScanType.COMPACT_RETAIN_DELETES || scan.isRaw();
192 // seePastDeleteMarker: user initiated scans
193 this.seePastDeleteMarkers =
194 scanInfo.getKeepDeletedCells() != KeepDeletedCells.FALSE && isUserScan;
195
196 int maxVersions =
197 scan.isRaw() ? scan.getMaxVersions() : Math.min(scan.getMaxVersions(),
198 scanInfo.getMaxVersions());
199
200 // Single branch to deal with two types of reads (columns vs all in family)
201 if (columns == null || columns.size() == 0) {
202 // there is always a null column in the wildcard column query.
203 hasNullColumn = true;
204
205 // use a specialized scan for wildcard column tracker.
206 this.columns = new ScanWildcardColumnTracker(
207 scanInfo.getMinVersions(), maxVersions, oldestUnexpiredTS);
208 } else {
209 // whether there is null column in the explicit column query
210 hasNullColumn = (columns.first().length == 0);
211
212 // We can share the ExplicitColumnTracker, diff is we reset
213 // between rows, not between storefiles.
214 this.columns = new ExplicitColumnTracker(columns, scanInfo.getMinVersions(), maxVersions,
215 oldestUnexpiredTS);
216 }
217 this.isReversed = scan.isReversed();
218 }
219
220 private DeleteTracker instantiateDeleteTracker() throws IOException {
221 DeleteTracker tracker = new ScanDeleteTracker();
222 if (regionCoprocessorHost != null) {
223 tracker = regionCoprocessorHost.postInstantiateDeleteTracker(tracker);
224 }
225 return tracker;
226 }
227
228 /**
229 * Construct a QueryMatcher for a scan that drop deletes from a limited range of rows.
230 * @param scan
231 * @param scanInfo The store's immutable scan info
232 * @param columns
233 * @param earliestPutTs Earliest put seen in any of the store files.
234 * @param oldestUnexpiredTS the oldest timestamp we are interested in, based on TTL
235 * @param now the current server time
236 * @param dropDeletesFromRow The inclusive left bound of the range; can be EMPTY_START_ROW.
237 * @param dropDeletesToRow The exclusive right bound of the range; can be EMPTY_END_ROW.
238 * @param regionCoprocessorHost
239 * @throws IOException
240 */
241 public ScanQueryMatcher(Scan scan, ScanInfo scanInfo, NavigableSet<byte[]> columns,
242 long readPointToUse, long earliestPutTs, long oldestUnexpiredTS, long now,
243 byte[] dropDeletesFromRow, byte[] dropDeletesToRow,
244 RegionCoprocessorHost regionCoprocessorHost) throws IOException {
245 this(scan, scanInfo, columns, ScanType.COMPACT_RETAIN_DELETES, readPointToUse, earliestPutTs,
246 oldestUnexpiredTS, now, regionCoprocessorHost);
247 Preconditions.checkArgument((dropDeletesFromRow != null) && (dropDeletesToRow != null));
248 this.dropDeletesFromRow = dropDeletesFromRow;
249 this.dropDeletesToRow = dropDeletesToRow;
250 }
251
252 /*
253 * Constructor for tests
254 */
255 ScanQueryMatcher(Scan scan, ScanInfo scanInfo,
256 NavigableSet<byte[]> columns, long oldestUnexpiredTS, long now) throws IOException {
257 this(scan, scanInfo, columns, ScanType.USER_SCAN,
258 Long.MAX_VALUE, /* max Readpoint to track versions */
259 HConstants.LATEST_TIMESTAMP, oldestUnexpiredTS, now, null);
260 }
261
262 /**
263 *
264 * @return whether there is an null column in the query
265 */
266 public boolean hasNullColumnInQuery() {
267 return hasNullColumn;
268 }
269
270 /**
271 * Determines if the caller should do one of several things:
272 * - seek/skip to the next row (MatchCode.SEEK_NEXT_ROW)
273 * - seek/skip to the next column (MatchCode.SEEK_NEXT_COL)
274 * - include the current KeyValue (MatchCode.INCLUDE)
275 * - ignore the current KeyValue (MatchCode.SKIP)
276 * - got to the next row (MatchCode.DONE)
277 *
278 * @param cell KeyValue to check
279 * @return The match code instance.
280 * @throws IOException in case there is an internal consistency problem
281 * caused by a data corruption.
282 */
283 public MatchCode match(Cell cell) throws IOException {
284 if (filter != null && filter.filterAllRemaining()) {
285 return MatchCode.DONE_SCAN;
286 }
287 if (row != null) {
288 int ret = this.rowComparator.compareRows(row, this.rowOffset, this.rowLength,
289 cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
290 if (!this.isReversed) {
291 if (ret <= -1) {
292 return MatchCode.DONE;
293 } else if (ret >= 1) {
294 // could optimize this, if necessary?
295 // Could also be called SEEK_TO_CURRENT_ROW, but this
296 // should be rare/never happens.
297 return MatchCode.SEEK_NEXT_ROW;
298 }
299 } else {
300 if (ret <= -1) {
301 return MatchCode.SEEK_NEXT_ROW;
302 } else if (ret >= 1) {
303 return MatchCode.DONE;
304 }
305 }
306 } else {
307 return MatchCode.DONE;
308 }
309
310 // optimize case.
311 if (this.stickyNextRow)
312 return MatchCode.SEEK_NEXT_ROW;
313
314 if (this.columns.done()) {
315 stickyNextRow = true;
316 return MatchCode.SEEK_NEXT_ROW;
317 }
318
319 int qualifierOffset = cell.getQualifierOffset();
320 int qualifierLength = cell.getQualifierLength();
321
322 long timestamp = cell.getTimestamp();
323 // check for early out based on timestamp alone
324 if (columns.isDone(timestamp)) {
325 return columns.getNextRowOrNextColumn(cell.getQualifierArray(), qualifierOffset,
326 qualifierLength);
327 }
328 // check if the cell is expired by cell TTL
329 if (HStore.isCellTTLExpired(cell, this.oldestUnexpiredTS, this.now)) {
330 return MatchCode.SKIP;
331 }
332
333 /*
334 * The delete logic is pretty complicated now.
335 * This is corroborated by the following:
336 * 1. The store might be instructed to keep deleted rows around.
337 * 2. A scan can optionally see past a delete marker now.
338 * 3. If deleted rows are kept, we have to find out when we can
339 * remove the delete markers.
340 * 4. Family delete markers are always first (regardless of their TS)
341 * 5. Delete markers should not be counted as version
342 * 6. Delete markers affect puts of the *same* TS
343 * 7. Delete marker need to be version counted together with puts
344 * they affect
345 */
346 byte typeByte = cell.getTypeByte();
347 long mvccVersion = cell.getMvccVersion();
348 if (CellUtil.isDelete(cell)) {
349 if (keepDeletedCells == KeepDeletedCells.FALSE
350 || (keepDeletedCells == KeepDeletedCells.TTL && timestamp < ttl)) {
351 // first ignore delete markers if the scanner can do so, and the
352 // range does not include the marker
353 //
354 // during flushes and compactions also ignore delete markers newer
355 // than the readpoint of any open scanner, this prevents deleted
356 // rows that could still be seen by a scanner from being collected
357 boolean includeDeleteMarker = seePastDeleteMarkers ?
358 tr.withinTimeRange(timestamp) :
359 tr.withinOrAfterTimeRange(timestamp);
360 if (includeDeleteMarker
361 && mvccVersion <= maxReadPointToTrackVersions) {
362 this.deletes.add(cell);
363 }
364 // Can't early out now, because DelFam come before any other keys
365 }
366
367 if ((!isUserScan)
368 && timeToPurgeDeletes > 0
369 && (EnvironmentEdgeManager.currentTime() - timestamp)
370 <= timeToPurgeDeletes) {
371 return MatchCode.INCLUDE;
372 } else if (retainDeletesInOutput || mvccVersion > maxReadPointToTrackVersions) {
373 // always include or it is not time yet to check whether it is OK
374 // to purge deltes or not
375 if (!isUserScan) {
376 // if this is not a user scan (compaction), we can filter this deletemarker right here
377 // otherwise (i.e. a "raw" scan) we fall through to normal version and timerange checking
378 return MatchCode.INCLUDE;
379 }
380 } else if (keepDeletedCells == KeepDeletedCells.TRUE
381 || (keepDeletedCells == KeepDeletedCells.TTL && timestamp >= ttl)) {
382 if (timestamp < earliestPutTs) {
383 // keeping delete rows, but there are no puts older than
384 // this delete in the store files.
385 return columns.getNextRowOrNextColumn(cell.getQualifierArray(),
386 qualifierOffset, qualifierLength);
387 }
388 // else: fall through and do version counting on the
389 // delete markers
390 } else {
391 return MatchCode.SKIP;
392 }
393 // note the following next else if...
394 // delete marker are not subject to other delete markers
395 } else if (!this.deletes.isEmpty()) {
396 DeleteResult deleteResult = deletes.isDeleted(cell);
397 switch (deleteResult) {
398 case FAMILY_DELETED:
399 case COLUMN_DELETED:
400 return columns.getNextRowOrNextColumn(cell.getQualifierArray(),
401 qualifierOffset, qualifierLength);
402 case VERSION_DELETED:
403 case FAMILY_VERSION_DELETED:
404 return MatchCode.SKIP;
405 case NOT_DELETED:
406 break;
407 default:
408 throw new RuntimeException("UNEXPECTED");
409 }
410 }
411
412 // NOTE: Cryptic stuff!
413 // if the timestamp is HConstants.OLDEST_TIMESTAMP, then this is a fake cell made to prime a
414 // Scanner; See KeyValueUTil#createLastOnRow. This Cell should never end up returning out of
415 // here a matchcode of INCLUDE else we will return to the client a fake Cell. If we call
416 // TimeRange, it will return 0 because it doesn't deal in OLDEST_TIMESTAMP and we will fall
417 // into the later code where we could return a matchcode of INCLUDE. See HBASE-16074 "ITBLL
418 // fails, reports lost big or tiny families" for a horror story. Check here for
419 // OLDEST_TIMESTAMP. TimeRange#compare is about more generic timestamps, between 0L and
420 // Long.MAX_LONG. It doesn't do OLDEST_TIMESTAMP weird handling.
421 int timestampComparison = timestamp == HConstants.OLDEST_TIMESTAMP? -1: tr.compare(timestamp);
422 if (timestampComparison >= 1) {
423 return MatchCode.SKIP;
424 } else if (timestampComparison <= -1) {
425 return columns.getNextRowOrNextColumn(cell.getQualifierArray(), qualifierOffset,
426 qualifierLength);
427 }
428
429 // STEP 1: Check if the column is part of the requested columns
430 MatchCode colChecker = columns.checkColumn(cell.getQualifierArray(),
431 qualifierOffset, qualifierLength, typeByte);
432 if (colChecker == MatchCode.INCLUDE) {
433 ReturnCode filterResponse = ReturnCode.SKIP;
434 // STEP 2: Yes, the column is part of the requested columns. Check if filter is present
435 if (filter != null) {
436 // STEP 3: Filter the key value and return if it filters out
437 filterResponse = filter.filterKeyValue(cell);
438 switch (filterResponse) {
439 case SKIP:
440 return MatchCode.SKIP;
441 case NEXT_COL:
442 return columns.getNextRowOrNextColumn(cell.getQualifierArray(),
443 qualifierOffset, qualifierLength);
444 case NEXT_ROW:
445 stickyNextRow = true;
446 return MatchCode.SEEK_NEXT_ROW;
447 case SEEK_NEXT_USING_HINT:
448 return MatchCode.SEEK_NEXT_USING_HINT;
449 default:
450 //It means it is either include or include and seek next
451 break;
452 }
453 }
454 /*
455 * STEP 4: Reaching this step means the column is part of the requested columns and either
456 * the filter is null or the filter has returned INCLUDE or INCLUDE_AND_NEXT_COL response.
457 * Now check the number of versions needed. This method call returns SKIP, INCLUDE,
458 * INCLUDE_AND_SEEK_NEXT_ROW, INCLUDE_AND_SEEK_NEXT_COL.
459 *
460 * FilterResponse ColumnChecker Desired behavior
461 * INCLUDE SKIP row has already been included, SKIP.
462 * INCLUDE INCLUDE INCLUDE
463 * INCLUDE INCLUDE_AND_SEEK_NEXT_COL INCLUDE_AND_SEEK_NEXT_COL
464 * INCLUDE INCLUDE_AND_SEEK_NEXT_ROW INCLUDE_AND_SEEK_NEXT_ROW
465 * INCLUDE_AND_SEEK_NEXT_COL SKIP row has already been included, SKIP.
466 * INCLUDE_AND_SEEK_NEXT_COL INCLUDE INCLUDE_AND_SEEK_NEXT_COL
467 * INCLUDE_AND_SEEK_NEXT_COL INCLUDE_AND_SEEK_NEXT_COL INCLUDE_AND_SEEK_NEXT_COL
468 * INCLUDE_AND_SEEK_NEXT_COL INCLUDE_AND_SEEK_NEXT_ROW INCLUDE_AND_SEEK_NEXT_ROW
469 *
470 * In all the above scenarios, we return the column checker return value except for
471 * FilterResponse (INCLUDE_AND_SEEK_NEXT_COL) and ColumnChecker(INCLUDE)
472 */
473 colChecker =
474 columns.checkVersions(cell.getQualifierArray(), qualifierOffset,
475 qualifierLength, timestamp, typeByte,
476 mvccVersion > maxReadPointToTrackVersions);
477 //Optimize with stickyNextRow
478 stickyNextRow = colChecker == MatchCode.INCLUDE_AND_SEEK_NEXT_ROW ? true : stickyNextRow;
479 return (filterResponse == ReturnCode.INCLUDE_AND_NEXT_COL &&
480 colChecker == MatchCode.INCLUDE) ? MatchCode.INCLUDE_AND_SEEK_NEXT_COL
481 : colChecker;
482 }
483 stickyNextRow = (colChecker == MatchCode.SEEK_NEXT_ROW) ? true
484 : stickyNextRow;
485 return colChecker;
486 }
487
488 /** Handle partial-drop-deletes. As we match keys in order, when we have a range from which
489 * we can drop deletes, we can set retainDeletesInOutput to false for the duration of this
490 * range only, and maintain consistency. */
491 private void checkPartialDropDeleteRange(byte [] row, int offset, short length) {
492 // If partial-drop-deletes are used, initially, dropDeletesFromRow and dropDeletesToRow
493 // are both set, and the matcher is set to retain deletes. We assume ordered keys. When
494 // dropDeletesFromRow is leq current kv, we start dropping deletes and reset
495 // dropDeletesFromRow; thus the 2nd "if" starts to apply.
496 if ((dropDeletesFromRow != null)
497 && ((dropDeletesFromRow == HConstants.EMPTY_START_ROW)
498 || (Bytes.compareTo(row, offset, length,
499 dropDeletesFromRow, 0, dropDeletesFromRow.length) >= 0))) {
500 retainDeletesInOutput = false;
501 dropDeletesFromRow = null;
502 }
503 // If dropDeletesFromRow is null and dropDeletesToRow is set, we are inside the partial-
504 // drop-deletes range. When dropDeletesToRow is leq current kv, we stop dropping deletes,
505 // and reset dropDeletesToRow so that we don't do any more compares.
506 if ((dropDeletesFromRow == null)
507 && (dropDeletesToRow != null) && (dropDeletesToRow != HConstants.EMPTY_END_ROW)
508 && (Bytes.compareTo(row, offset, length,
509 dropDeletesToRow, 0, dropDeletesToRow.length) >= 0)) {
510 retainDeletesInOutput = true;
511 dropDeletesToRow = null;
512 }
513 }
514
515 /**
516 * @return Returns false if we know there are no more rows to be scanned (We've reached the
517 * <code>stopRow</code> or we are scanning on row only because this Scan is for a Get, etc.
518 */
519 public boolean moreRowsMayExistAfter(Cell kv) {
520 // If a 'get' Scan -- we are doing a Get (every Get is a single-row Scan in implementation) --
521 // then we are looking at one row only, the one specified in the Get coordinate..so we know
522 // for sure that there are no more rows on this Scan
523 if (this.get) {
524 return false;
525 }
526 // If no stopRow, return that there may be more rows. The tests that follow depend on a
527 // non-empty, non-default stopRow so this little test below short-circuits out doing the
528 // following compares.
529 if (this.stopRow == null || this.stopRow == HConstants.EMPTY_BYTE_ARRAY) {
530 return true;
531 }
532 return this.isReversed?
533 rowComparator.compareRows(kv, stopRow, 0, stopRow.length) > 0:
534 Bytes.equals(stopRow, HConstants.EMPTY_END_ROW) ||
535 rowComparator.compareRows(kv, stopRow, 0, stopRow.length) < 0;
536 }
537
538 /**
539 * Set current row
540 * @param row
541 */
542 public void setRow(byte [] row, int offset, short length) {
543 checkPartialDropDeleteRange(row, offset, length);
544 this.row = row;
545 this.rowOffset = offset;
546 this.rowLength = length;
547 reset();
548 }
549
550 public void reset() {
551 this.deletes.reset();
552 this.columns.reset();
553
554 stickyNextRow = false;
555 }
556
557 /**
558 *
559 * @return the start key
560 */
561 public Cell getStartKey() {
562 return this.startKey;
563 }
564
565 /**
566 *
567 * @return the Filter
568 */
569 Filter getFilter() {
570 return this.filter;
571 }
572
573 public Cell getNextKeyHint(Cell kv) throws IOException {
574 if (filter == null) {
575 return null;
576 } else {
577 return filter.getNextCellHint(kv);
578 }
579 }
580
581 public Cell getKeyForNextColumn(Cell kv) {
582 ColumnCount nextColumn = columns.getColumnHint();
583 if (nextColumn == null) {
584 return KeyValueUtil.createLastOnRow(
585 kv.getRowArray(), kv.getRowOffset(), kv.getRowLength(),
586 kv.getFamilyArray(), kv.getFamilyOffset(), kv.getFamilyLength(),
587 kv.getQualifierArray(), kv.getQualifierOffset(), kv.getQualifierLength());
588 } else {
589 return KeyValueUtil.createFirstOnRow(
590 kv.getRowArray(), kv.getRowOffset(), kv.getRowLength(),
591 kv.getFamilyArray(), kv.getFamilyOffset(), kv.getFamilyLength(),
592 nextColumn.getBuffer(), nextColumn.getOffset(), nextColumn.getLength());
593 }
594 }
595
596 /**
597 * @param nextIndexed the key of the next entry in the block index (if any)
598 * @param kv The Cell we're using to calculate the seek key
599 * @return result of the compare between the indexed key and the key portion of the passed cell
600 */
601 public int compareKeyForNextRow(Cell nextIndexed, Cell kv) {
602 return rowComparator.compareKey(nextIndexed,
603 kv.getRowArray(), kv.getRowOffset(), kv.getRowLength(),
604 null, 0, 0,
605 null, 0, 0,
606 HConstants.OLDEST_TIMESTAMP, Type.Minimum.getCode());
607 }
608
609 /**
610 * @param nextIndexed the key of the next entry in the block index (if any)
611 * @param kv The Cell we're using to calculate the seek key
612 * @return result of the compare between the indexed key and the key portion of the passed cell
613 */
614 public int compareKeyForNextColumn(Cell nextIndexed, Cell kv) {
615 ColumnCount nextColumn = columns.getColumnHint();
616 if (nextColumn == null) {
617 return rowComparator.compareKey(nextIndexed,
618 kv.getRowArray(), kv.getRowOffset(), kv.getRowLength(),
619 kv.getFamilyArray(), kv.getFamilyOffset(), kv.getFamilyLength(),
620 kv.getQualifierArray(), kv.getQualifierOffset(), kv.getQualifierLength(),
621 HConstants.OLDEST_TIMESTAMP, Type.Minimum.getCode());
622 } else {
623 return rowComparator.compareKey(nextIndexed,
624 kv.getRowArray(), kv.getRowOffset(), kv.getRowLength(),
625 kv.getFamilyArray(), kv.getFamilyOffset(), kv.getFamilyLength(),
626 nextColumn.getBuffer(), nextColumn.getOffset(), nextColumn.getLength(),
627 HConstants.LATEST_TIMESTAMP, Type.Maximum.getCode());
628 }
629 }
630
631 //Used only for testing purposes
632 static MatchCode checkColumn(ColumnTracker columnTracker, byte[] bytes, int offset,
633 int length, long ttl, byte type, boolean ignoreCount) throws IOException {
634 MatchCode matchCode = columnTracker.checkColumn(bytes, offset, length, type);
635 if (matchCode == MatchCode.INCLUDE) {
636 return columnTracker.checkVersions(bytes, offset, length, ttl, type, ignoreCount);
637 }
638 return matchCode;
639 }
640
641 /**
642 * {@link #match} return codes. These instruct the scanner moving through
643 * memstores and StoreFiles what to do with the current KeyValue.
644 * <p>
645 * Additionally, this contains "early-out" language to tell the scanner to
646 * move on to the next File (memstore or Storefile), or to return immediately.
647 */
648 public static enum MatchCode {
649 /**
650 * Include KeyValue in the returned result
651 */
652 INCLUDE,
653
654 /**
655 * Do not include KeyValue in the returned result
656 */
657 SKIP,
658
659 /**
660 * Do not include, jump to next StoreFile or memstore (in time order)
661 */
662 NEXT,
663
664 /**
665 * Do not include, return current result
666 */
667 DONE,
668
669 /**
670 * These codes are used by the ScanQueryMatcher
671 */
672
673 /**
674 * Done with the row, seek there.
675 */
676 SEEK_NEXT_ROW,
677 /**
678 * Done with column, seek to next.
679 */
680 SEEK_NEXT_COL,
681
682 /**
683 * Done with scan, thanks to the row filter.
684 */
685 DONE_SCAN,
686
687 /*
688 * Seek to next key which is given as hint.
689 */
690 SEEK_NEXT_USING_HINT,
691
692 /**
693 * Include KeyValue and done with column, seek to next.
694 */
695 INCLUDE_AND_SEEK_NEXT_COL,
696
697 /**
698 * Include KeyValue and done with row, seek to next.
699 */
700 INCLUDE_AND_SEEK_NEXT_ROW,
701 }
702 }