View Javadoc

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     // We aren't sure whether any DeleteFamily cells exist, so we can't skip to next column.
583     // TODO: Current way disable us to seek to next column quickly. Is there any better solution?
584     // see HBASE-18471 for more details
585     // see TestFromClientSide3#testScanAfterDeletingSpecifiedRow
586     // see TestFromClientSide3#testScanAfterDeletingSpecifiedRowV2
587     if (kv.getQualifierLength() == 0) {
588       Cell nextKey = createNextOnRowCol(kv);
589       if (nextKey != kv) {
590         return nextKey;
591       }
592       // The cell is at the end of row/family/qualifier, so it is impossible to find any DeleteFamily cells.
593       // Let us seek to next column.
594     }
595     ColumnCount nextColumn = columns.getColumnHint();
596     if (nextColumn == null) {
597       return KeyValueUtil.createLastOnRow(
598           kv.getRowArray(), kv.getRowOffset(), kv.getRowLength(),
599           kv.getFamilyArray(), kv.getFamilyOffset(), kv.getFamilyLength(),
600           kv.getQualifierArray(), kv.getQualifierOffset(), kv.getQualifierLength());
601     } else {
602       return KeyValueUtil.createFirstOnRow(
603           kv.getRowArray(), kv.getRowOffset(), kv.getRowLength(),
604           kv.getFamilyArray(), kv.getFamilyOffset(), kv.getFamilyLength(),
605           nextColumn.getBuffer(), nextColumn.getOffset(), nextColumn.getLength());
606     }
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 compareKeyForNextRow(Cell nextIndexed, Cell kv) {
615     return rowComparator.compareKey(nextIndexed,
616       kv.getRowArray(), kv.getRowOffset(), kv.getRowLength(),
617       null, 0, 0,
618       null, 0, 0,
619       HConstants.OLDEST_TIMESTAMP, Type.Minimum.getCode());
620   }
621 
622   /**
623    * @param nextIndexed the key of the next entry in the block index (if any)
624    * @param kv The Cell we're using to calculate the seek key
625    * @return result of the compare between the indexed key and the key portion of the passed cell
626    */
627   public int compareKeyForNextColumn(Cell nextIndexed, Cell kv) {
628     ColumnCount nextColumn = columns.getColumnHint();
629     if (nextColumn == null) {
630       return rowComparator.compareKey(nextIndexed,
631         kv.getRowArray(), kv.getRowOffset(), kv.getRowLength(),
632         kv.getFamilyArray(), kv.getFamilyOffset(), kv.getFamilyLength(),
633         kv.getQualifierArray(), kv.getQualifierOffset(), kv.getQualifierLength(),
634         HConstants.OLDEST_TIMESTAMP, Type.Minimum.getCode());
635     } else {
636       return rowComparator.compareKey(nextIndexed,
637         kv.getRowArray(), kv.getRowOffset(), kv.getRowLength(),
638         kv.getFamilyArray(), kv.getFamilyOffset(), kv.getFamilyLength(),
639         nextColumn.getBuffer(), nextColumn.getOffset(), nextColumn.getLength(),
640         HConstants.LATEST_TIMESTAMP, Type.Maximum.getCode());
641     }
642   }
643 
644   //Used only for testing purposes
645   static MatchCode checkColumn(ColumnTracker columnTracker, byte[] bytes, int offset,
646       int length, long ttl, byte type, boolean ignoreCount) throws IOException {
647     MatchCode matchCode = columnTracker.checkColumn(bytes, offset, length, type);
648     if (matchCode == MatchCode.INCLUDE) {
649       return columnTracker.checkVersions(bytes, offset, length, ttl, type, ignoreCount);
650     }
651     return matchCode;
652   }
653 
654   /**
655    * {@link #match} return codes.  These instruct the scanner moving through
656    * memstores and StoreFiles what to do with the current KeyValue.
657    * <p>
658    * Additionally, this contains "early-out" language to tell the scanner to
659    * move on to the next File (memstore or Storefile), or to return immediately.
660    */
661   public static enum MatchCode {
662     /**
663      * Include KeyValue in the returned result
664      */
665     INCLUDE,
666 
667     /**
668      * Do not include KeyValue in the returned result
669      */
670     SKIP,
671 
672     /**
673      * Do not include, jump to next StoreFile or memstore (in time order)
674      */
675     NEXT,
676 
677     /**
678      * Do not include, return current result
679      */
680     DONE,
681 
682     /**
683      * These codes are used by the ScanQueryMatcher
684      */
685 
686     /**
687      * Done with the row, seek there.
688      */
689     SEEK_NEXT_ROW,
690     /**
691      * Done with column, seek to next.
692      */
693     SEEK_NEXT_COL,
694 
695     /**
696      * Done with scan, thanks to the row filter.
697      */
698     DONE_SCAN,
699 
700     /*
701      * Seek to next key which is given as hint.
702      */
703     SEEK_NEXT_USING_HINT,
704 
705     /**
706      * Include KeyValue and done with column, seek to next.
707      */
708     INCLUDE_AND_SEEK_NEXT_COL,
709 
710     /**
711      * Include KeyValue and done with row, seek to next.
712      */
713     INCLUDE_AND_SEEK_NEXT_ROW,
714   }
715 
716   /**
717    * @return An new cell is located following input cell. If both of type and timestamp are
718    *         minimum, the input cell will be returned directly.
719    */
720   private static Cell createNextOnRowCol(Cell cell) {
721     long ts = cell.getTimestamp();
722     byte type = cell.getTypeByte();
723     if (type != Type.Minimum.getCode()) {
724       type = KeyValue.Type.values()[KeyValue.Type.codeToType(type).ordinal() - 1].getCode();
725     } else if (ts != HConstants.OLDEST_TIMESTAMP) {
726       ts = ts - 1;
727       type = Type.Maximum.getCode();
728     } else {
729       return cell;
730     }
731     return createNextOnRowCol(cell, ts, type);
732   }
733 
734   private static Cell createNextOnRowCol(final Cell cell, final long ts, final byte type) {
735     return new Cell() {
736       @Override
737       public byte[] getRowArray() { return cell.getRowArray(); }
738 
739       @Override
740       public int getRowOffset() { return cell.getRowOffset(); }
741 
742       @Override
743       public short getRowLength() { return cell.getRowLength(); }
744 
745       @Override
746       public byte[] getFamilyArray() { return cell.getFamilyArray(); }
747 
748       @Override
749       public int getFamilyOffset() { return cell.getFamilyOffset(); }
750 
751       @Override
752       public byte getFamilyLength() { return cell.getFamilyLength(); }
753 
754       @Override
755       public byte[] getQualifierArray() { return cell.getQualifierArray(); }
756 
757       @Override
758       public int getQualifierOffset() { return cell.getQualifierOffset(); }
759 
760       @Override
761       public int getQualifierLength() { return cell.getQualifierLength(); }
762 
763       @Override
764       public long getTimestamp() { return ts; }
765 
766       @Override
767       public byte getTypeByte() {return type; }
768 
769       @Override
770       public long getMvccVersion() { return cell.getMvccVersion(); }
771 
772       @Override
773       public long getSequenceId() { return cell.getSequenceId(); }
774 
775       @Override
776       public byte[] getValueArray() { return cell.getValueArray(); }
777 
778       @Override
779       public int getValueOffset() { return cell.getValueOffset(); }
780 
781       @Override
782       public int getValueLength() { return cell.getValueLength(); }
783 
784       @Override
785       public byte[] getTagsArray() { return cell.getTagsArray(); }
786 
787       @Override
788       public int getTagsOffset() { return cell.getTagsOffset(); }
789 
790       @Override
791       public int getTagsLength() { return cell.getTagsLength(); }
792 
793       @Override
794       public byte[] getValue() { return cell.getValue(); }
795 
796       @Override
797       public byte[] getFamily() { return cell.getFamily(); }
798 
799       @Override
800       public byte[] getQualifier() { return cell.getQualifier(); }
801 
802       @Override
803       public byte[] getRow() {return cell.getRow(); }
804     };
805   }
806 }