HBASE-20565 ColumnRangeFilter combined with ColumnPaginationFilter can produce 
incorrect result


Project: http://git-wip-us.apache.org/repos/asf/hbase/repo
Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/a888af3d
Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/a888af3d
Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/a888af3d

Branch: refs/heads/HBASE-20749
Commit: a888af3dc9320a5dc3c2e41e46e31522c7a0e356
Parents: b631727
Author: huzheng <open...@gmail.com>
Authored: Tue Jul 17 20:37:39 2018 +0800
Committer: huzheng <open...@gmail.com>
Committed: Tue Jul 24 10:29:24 2018 +0800

----------------------------------------------------------------------
 .../hadoop/hbase/filter/ColumnRangeFilter.java  | 25 +++---
 .../hadoop/hbase/filter/FilterListWithAND.java  | 14 +++-
 .../hbase/filter/TestColumnRangeFilter.java     | 84 ++++++++++++++------
 .../hadoop/hbase/filter/TestFilterList.java     |  8 +-
 4 files changed, 88 insertions(+), 43 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hbase/blob/a888af3d/hbase-client/src/main/java/org/apache/hadoop/hbase/filter/ColumnRangeFilter.java
----------------------------------------------------------------------
diff --git 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/filter/ColumnRangeFilter.java
 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/filter/ColumnRangeFilter.java
index cd103b8..f981ed7 100644
--- 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/filter/ColumnRangeFilter.java
+++ 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/filter/ColumnRangeFilter.java
@@ -208,20 +208,23 @@ public class ColumnRangeFilter extends FilterBase {
   }
 
   /**
-   * @param other
-   * @return true if and only if the fields of the filter that are serialized
-   * are equal to the corresponding fields in other.  Used for testing.
+   * @param o filter to serialize.
+   * @return true if and only if the fields of the filter that are serialized 
are equal to the
+   *         corresponding fields in other. Used for testing.
    */
   @Override
   boolean areSerializedFieldsEqual(Filter o) {
-   if (o == this) return true;
-   if (!(o instanceof ColumnRangeFilter)) return false;
-
-   ColumnRangeFilter other = (ColumnRangeFilter)o;
-   return Bytes.equals(this.getMinColumn(),other.getMinColumn())
-     && this.getMinColumnInclusive() == other.getMinColumnInclusive()
-     && Bytes.equals(this.getMaxColumn(), other.getMaxColumn())
-     && this.getMaxColumnInclusive() == other.getMaxColumnInclusive();
+    if (o == this) {
+      return true;
+    }
+    if (!(o instanceof ColumnRangeFilter)) {
+      return false;
+    }
+    ColumnRangeFilter other = (ColumnRangeFilter) o;
+    return Bytes.equals(this.getMinColumn(), other.getMinColumn())
+        && this.getMinColumnInclusive() == other.getMinColumnInclusive()
+        && Bytes.equals(this.getMaxColumn(), other.getMaxColumn())
+        && this.getMaxColumnInclusive() == other.getMaxColumnInclusive();
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/hbase/blob/a888af3d/hbase-client/src/main/java/org/apache/hadoop/hbase/filter/FilterListWithAND.java
----------------------------------------------------------------------
diff --git 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/filter/FilterListWithAND.java
 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/filter/FilterListWithAND.java
index 9f2ca21..ebff3a5 100644
--- 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/filter/FilterListWithAND.java
+++ 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/filter/FilterListWithAND.java
@@ -154,6 +154,11 @@ public class FilterListWithAND extends FilterListBase {
         "Received code is not valid. rc: " + rc + ", localRC: " + localRC);
   }
 
+  private boolean isIncludeRelatedReturnCode(ReturnCode rc) {
+    return isInReturnCodes(rc, ReturnCode.INCLUDE, 
ReturnCode.INCLUDE_AND_NEXT_COL,
+      ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW);
+  }
+
   @Override
   public ReturnCode filterCell(Cell c) throws IOException {
     if (isEmpty()) {
@@ -168,11 +173,16 @@ public class FilterListWithAND extends FilterListBase {
       }
       ReturnCode localRC;
       localRC = filter.filterCell(c);
-      rc = mergeReturnCode(rc, localRC);
-
       if (localRC == ReturnCode.SEEK_NEXT_USING_HINT) {
         seekHintFilters.add(filter);
       }
+      rc = mergeReturnCode(rc, localRC);
+      // Only when rc is INCLUDE* case, we should pass the cell to the 
following sub-filters.
+      // otherwise we may mess up the global state (such as offset, count..) 
in the following
+      // sub-filters. (HBASE-20565)
+      if (!isIncludeRelatedReturnCode(rc)) {
+        return rc;
+      }
     }
     if (!seekHintFilters.isEmpty()) {
       return ReturnCode.SEEK_NEXT_USING_HINT;

http://git-wip-us.apache.org/repos/asf/hbase/blob/a888af3d/hbase-server/src/test/java/org/apache/hadoop/hbase/filter/TestColumnRangeFilter.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/filter/TestColumnRangeFilter.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/filter/TestColumnRangeFilter.java
index 98bc57b..c69466e 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/filter/TestColumnRangeFilter.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/filter/TestColumnRangeFilter.java
@@ -19,6 +19,7 @@ package org.apache.hadoop.hbase.filter;
 
 import static org.junit.Assert.*;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -39,6 +40,7 @@ import org.apache.hadoop.hbase.client.Result;
 import org.apache.hadoop.hbase.client.ResultScanner;
 import org.apache.hadoop.hbase.client.Scan;
 import org.apache.hadoop.hbase.client.Table;
+import org.apache.hadoop.hbase.filter.FilterList.Operator;
 import org.apache.hadoop.hbase.testclassification.FilterTests;
 import org.apache.hadoop.hbase.testclassification.MediumTests;
 import org.apache.hadoop.hbase.util.Bytes;
@@ -226,38 +228,68 @@ public class TestColumnRangeFilter {
     Scan scan = new Scan();
     scan.setMaxVersions();
     for (StringRange s : rangeMap.keySet()) {
-      filter = new ColumnRangeFilter(s.getStart() == null ? null
-          : Bytes.toBytes(s.getStart()), s.isStartInclusive(),
-          s.getEnd() == null ? null : Bytes.toBytes(s.getEnd()),
+      filter = new ColumnRangeFilter(s.getStart() == null ? null : 
Bytes.toBytes(s.getStart()),
+          s.isStartInclusive(), s.getEnd() == null ? null : 
Bytes.toBytes(s.getEnd()),
           s.isEndInclusive());
-      scan.setFilter(filter);
-      ResultScanner scanner = ht.getScanner(scan);
-      List<Cell> results = new ArrayList<>();
-      LOG.info("scan column range: " + s.toString());
-      long timeBeforeScan = System.currentTimeMillis();
+      assertEquals(rangeMap.get(s).size(), cellsCount(ht, filter));
+    }
+    ht.close();
+  }
+
+  @Test
+  public void TestColumnRangeFilterWithColumnPaginationFilter() throws 
Exception {
+    String family = "Family";
+    String table = "TestColumnRangeFilterWithColumnPaginationFilter";
+    try (Table ht =
+        TEST_UTIL.createTable(TableName.valueOf(table), Bytes.toBytes(family), 
Integer.MAX_VALUE)) {
+      // one row.
+      String row = "row";
+      // One version
+      long timestamp = 100;
+      // 10 columns
+      int[] columns = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+      String valueString = "ValueString";
+
+      Put p = new Put(Bytes.toBytes(row));
+      p.setDurability(Durability.SKIP_WAL);
+      for (int column : columns) {
+        KeyValue kv =
+            KeyValueTestUtil.create(row, family, Integer.toString(column), 
timestamp, valueString);
+        p.add(kv);
+      }
+      ht.put(p);
+
+      TEST_UTIL.flush();
+
+      // Column range from 1 to 9.
+      StringRange stringRange = new StringRange("1", true, "9", false);
+      ColumnRangeFilter filter1 = new 
ColumnRangeFilter(Bytes.toBytes(stringRange.getStart()),
+          stringRange.isStartInclusive(), Bytes.toBytes(stringRange.getEnd()),
+          stringRange.isEndInclusive());
+
+      ColumnPaginationFilter filter2 = new ColumnPaginationFilter(5, 0);
+      ColumnPaginationFilter filter3 = new ColumnPaginationFilter(5, 1);
+      ColumnPaginationFilter filter4 = new ColumnPaginationFilter(5, 2);
+      ColumnPaginationFilter filter5 = new ColumnPaginationFilter(5, 6);
+      ColumnPaginationFilter filter6 = new ColumnPaginationFilter(5, 9);
+      assertEquals(5, cellsCount(ht, new FilterList(Operator.MUST_PASS_ALL, 
filter1, filter2)));
+      assertEquals(5, cellsCount(ht, new FilterList(Operator.MUST_PASS_ALL, 
filter1, filter3)));
+      assertEquals(5, cellsCount(ht, new FilterList(Operator.MUST_PASS_ALL, 
filter1, filter4)));
+      assertEquals(2, cellsCount(ht, new FilterList(Operator.MUST_PASS_ALL, 
filter1, filter5)));
+      assertEquals(0, cellsCount(ht, new FilterList(Operator.MUST_PASS_ALL, 
filter1, filter6)));
+    }
+  }
 
+  private int cellsCount(Table table, Filter filter) throws IOException {
+    Scan scan = new Scan().setFilter(filter).readAllVersions();
+    try (ResultScanner scanner = table.getScanner(scan)) {
+      List<Cell> results = new ArrayList<>();
       Result result;
       while ((result = scanner.next()) != null) {
-        for (Cell kv : result.listCells()) {
-          results.add(kv);
-        }
-      }
-      long scanTime = System.currentTimeMillis() - timeBeforeScan;
-      scanner.close();
-      LOG.info("scan time = " + scanTime + "ms");
-      LOG.info("found " + results.size() + " results");
-      LOG.info("Expecting " + rangeMap.get(s).size() + " results");
-
-      /*
-      for (KeyValue kv : results) {
-        LOG.info("found row " + Bytes.toString(kv.getRow()) + ", column "
-            + Bytes.toString(kv.getQualifier()));
+        result.listCells().forEach(results::add);
       }
-      */
-
-      assertEquals(rangeMap.get(s).size(), results.size());
+      return results.size();
     }
-    ht.close();
   }
 
   List<String> generateRandomWords(int numberOfWords, int maxLengthOfWords) {

http://git-wip-us.apache.org/repos/asf/hbase/blob/a888af3d/hbase-server/src/test/java/org/apache/hadoop/hbase/filter/TestFilterList.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/filter/TestFilterList.java 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/filter/TestFilterList.java
index b2fe9d0..5f9515a 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/filter/TestFilterList.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/filter/TestFilterList.java
@@ -525,7 +525,7 @@ public class TestFilterList {
     filterList = new FilterList(Operator.MUST_PASS_ALL,
         Arrays.asList(new Filter [] { filterMinHint, filterMaxHint } ));
     filterList.filterCell(null);
-    assertEquals(0, comparator.compare(filterList.getNextCellHint(null), 
maxKeyValue));
+    assertEquals(0, comparator.compare(filterList.getNextCellHint(null), 
minKeyValue));
 
     filterList = new FilterList(Operator.MUST_PASS_ALL,
         Arrays.asList(new Filter [] { filterMaxHint, filterMinHint } ));
@@ -536,7 +536,7 @@ public class TestFilterList {
     filterList = new FilterList(Operator.MUST_PASS_ALL,
         Arrays.asList(new Filter[] { filterNoHint, filterMinHint, 
filterMaxHint }));
     filterList.filterCell(null);
-    assertEquals(0, comparator.compare(filterList.getNextCellHint(null), 
maxKeyValue));
+    assertEquals(0, comparator.compare(filterList.getNextCellHint(null), 
minKeyValue));
     filterList = new FilterList(Operator.MUST_PASS_ALL,
         Arrays.asList(new Filter[] { filterNoHint, filterMaxHint }));
     filterList.filterCell(null);
@@ -744,10 +744,10 @@ public class TestFilterList {
     assertEquals(ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW, 
filterList.filterCell(kv1));
 
     filterList = new FilterList(Operator.MUST_PASS_ALL, filter4, filter5, 
filter6);
-    assertEquals(ReturnCode.SEEK_NEXT_USING_HINT, filterList.filterCell(kv1));
+    assertEquals(ReturnCode.NEXT_COL, filterList.filterCell(kv1));
 
     filterList = new FilterList(Operator.MUST_PASS_ALL, filter4, filter6);
-    assertEquals(ReturnCode.SEEK_NEXT_USING_HINT, filterList.filterCell(kv1));
+    assertEquals(ReturnCode.NEXT_COL, filterList.filterCell(kv1));
 
     filterList = new FilterList(Operator.MUST_PASS_ALL, filter3, filter1);
     assertEquals(ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW, 
filterList.filterCell(kv1));

Reply via email to