[java-client] IN-list predicate

Change-Id: Ia556196963153f9db64d67bc699f96cb920ecac6
Reviewed-on: http://gerrit.cloudera.org:8080/4530
Tested-by: Kudu Jenkins
Reviewed-by: Jean-Daniel Cryans <jdcry...@apache.org>


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

Branch: refs/heads/master
Commit: cb92799baf195d70b80535d11e54447e25b71c42
Parents: 9b50bd5
Author: Dan Burkert <d...@cloudera.com>
Authored: Fri Sep 23 13:50:58 2016 -0700
Committer: Dan Burkert <d...@cloudera.com>
Committed: Thu Sep 29 01:30:31 2016 +0000

----------------------------------------------------------------------
 .../org/apache/kudu/client/KuduPredicate.java   | 298 ++++++++++++++++---
 .../org/apache/kudu/client/PartitionPruner.java |  19 +-
 .../org/apache/kudu/client/TestKuduClient.java  |  17 ++
 .../apache/kudu/client/TestKuduPredicate.java   | 206 +++++++++++++
 .../apache/kudu/client/TestPartitionPruner.java | 192 +++++++-----
 5 files changed, 615 insertions(+), 117 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kudu/blob/cb92799b/java/kudu-client/src/main/java/org/apache/kudu/client/KuduPredicate.java
----------------------------------------------------------------------
diff --git 
a/java/kudu-client/src/main/java/org/apache/kudu/client/KuduPredicate.java 
b/java/kudu-client/src/main/java/org/apache/kudu/client/KuduPredicate.java
index 03a6004..a78d63d 100644
--- a/java/kudu-client/src/main/java/org/apache/kudu/client/KuduPredicate.java
+++ b/java/kudu-client/src/main/java/org/apache/kudu/client/KuduPredicate.java
@@ -18,6 +18,7 @@
 package org.apache.kudu.client;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Joiner;
 import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
 import com.google.common.primitives.UnsignedBytes;
@@ -30,7 +31,13 @@ import org.apache.kudu.Type;
 import org.apache.kudu.annotations.InterfaceAudience;
 import org.apache.kudu.annotations.InterfaceStability;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
 
 /**
  * A predicate which can be used to filter rows based on the value of a column.
@@ -52,6 +59,8 @@ public class KuduPredicate {
     RANGE,
     /** A predicate which filters all null rows. */
     IS_NOT_NULL,
+    /** A predicate which filters all rows not matching a list of values. */
+    IN_LIST,
   }
 
   /**
@@ -79,6 +88,9 @@ public class KuduPredicate {
   /** The exclusive upper bound value if this is a Range predicate. */
   private final byte[] upper;
 
+  /** IN-list values. */
+  private final byte[][] inListValues;
+
   /**
    * Creates a new {@code KuduPredicate} on a boolean column.
    * @param column the column schema
@@ -316,6 +328,80 @@ public class KuduPredicate {
   }
 
   /**
+   * Creates a new IN list predicate.
+   *
+   * The list must contain values of the correct type for the column.
+   *
+   * @param column the column that the predicate applies to
+   * @param values list of values which the column values must match
+   * @param <T> the type of values, must match the type of the column
+   * @return an IN list predicate
+   */
+  public static <T> KuduPredicate newInListPredicate(final ColumnSchema 
column, List<T> values) {
+    if (values.isEmpty()) return none(column);
+    T t = values.get(0);
+
+    SortedSet<byte[]> vals = new TreeSet<>(new Comparator<byte[]>() {
+      @Override
+      public int compare(byte[] a, byte[] b) {
+        return KuduPredicate.compare(column, a, b);
+      }
+    });
+
+    if (t instanceof Boolean) {
+      checkColumn(column, Type.BOOL);
+      for (T value : values) {
+        vals.add(Bytes.fromBoolean((Boolean) value));
+      }
+    } else if (t instanceof Byte) {
+      checkColumn(column, Type.INT8);
+      for (T value : values) {
+        vals.add(new byte[] {(Byte) value});
+      }
+    } else if (t instanceof Short) {
+      checkColumn(column, Type.INT16);
+      for (T value : values) {
+        vals.add(Bytes.fromShort((Short) value));
+      }
+    } else if (t instanceof Integer) {
+      checkColumn(column, Type.INT32);
+      for (T value : values) {
+        vals.add(Bytes.fromInt((Integer) value));
+      }
+    } else if (t instanceof Long) {
+      checkColumn(column, Type.INT64, Type.UNIXTIME_MICROS);
+      for (T value : values) {
+        vals.add(Bytes.fromLong((Long) value));
+      }
+    } else if (t instanceof Float) {
+      checkColumn(column, Type.FLOAT);
+      for (T value : values) {
+        vals.add(Bytes.fromFloat((Float) value));
+      }
+    } else if (t instanceof Double) {
+      checkColumn(column, Type.DOUBLE);
+      for (T value : values) {
+        vals.add(Bytes.fromDouble((Double) value));
+      }
+    } else if (t instanceof String) {
+      checkColumn(column, Type.STRING);
+      for (T value : values) {
+        vals.add(Bytes.fromString((String) value));
+      }
+    } else if (t instanceof byte[]) {
+      checkColumn(column, Type.BINARY);
+      for (T value : values) {
+        vals.add((byte[]) value);
+      }
+    } else {
+      throw new IllegalArgumentException(String.format("illegal type for IN 
list values: %s",
+                                                       
t.getClass().getName()));
+    }
+
+    return buildInList(column, vals);
+  }
+
+  /**
    * @param type the predicate type
    * @param column the column to which the predicate applies
    * @param lower the lower bound serialized value if this is a Range 
predicate,
@@ -328,6 +414,20 @@ public class KuduPredicate {
     this.column = column;
     this.lower = lower;
     this.upper = upper;
+    this.inListValues = null;
+  }
+
+  /**
+   * Constructor for IN list predicate.
+   * @param column the column to which the predicate applies
+   * @param inListValues the encoded IN list values
+   */
+  private KuduPredicate(ColumnSchema column, byte[][] inListValues) {
+    this.column = column;
+    this.type = PredicateType.IN_LIST;
+    this.lower = null;
+    this.upper = null;
+    this.inListValues = inListValues;
   }
 
   /**
@@ -367,57 +467,132 @@ public class KuduPredicate {
   KuduPredicate merge(KuduPredicate other) {
     Preconditions.checkArgument(column.equals(other.column),
                                 "predicates from different columns may not be 
merged");
-    if (type == PredicateType.NONE || other.type == PredicateType.NONE) {
-      return none(column);
-    }
 
-    if (type == PredicateType.IS_NOT_NULL) {
-      // NOT NULL is less selective than all other predicate types, so the
-      // intersection of NOT NULL with any other predicate is just the other
-      // predicate.
-      //
-      // Note: this will no longer be true when an IS NULL predicate type is
-      // added.
-      return other;
-    }
+    // NONE predicates dominate.
+    if (other.type == PredicateType.NONE) return other;
+
+    // NOT NULL is dominated by all other predicates.
+    // Note: this will no longer be true when an IS NULL predicate type is
+    // added.
+    if (other.type == PredicateType.IS_NOT_NULL) return this;
 
-    if (type == PredicateType.EQUALITY) {
-      if (other.type == PredicateType.EQUALITY) {
-        if (compare(lower, other.lower) != 0) {
-          return none(this.column);
+    switch (type) {
+      case NONE: return this;
+      case IS_NOT_NULL: return other;
+      case EQUALITY: {
+        if (other.type == PredicateType.EQUALITY) {
+          if (compare(column, lower, other.lower) != 0) {
+            return none(this.column);
+          } else {
+            return this;
+          }
+        } else if (other.type == PredicateType.RANGE) {
+          if (other.rangeContains(lower)) {
+            return this;
+          } else {
+            return none(this.column);
+          }
         } else {
-          return this;
+          Preconditions.checkState(other.type == PredicateType.IN_LIST);
+          return other.merge(this);
         }
-      } else {
-        if ((other.lower == null || compare(lower, other.lower) >= 0) &&
-            (other.upper == null || compare(lower, other.upper) < 0)) {
-          return this;
+      }
+      case RANGE: {
+        if (other.type == PredicateType.EQUALITY || other.type == 
PredicateType.IN_LIST) {
+          return other.merge(this);
         } else {
-          return none(this.column);
+          Preconditions.checkState(other.type == PredicateType.RANGE);
+          byte[] newLower = other.lower == null ||
+              (lower != null && compare(column, lower, other.lower) >= 0) ? 
lower : other.lower;
+          byte[] newUpper = other.upper == null ||
+              (upper != null && compare(column, upper, other.upper) <= 0) ? 
upper : other.upper;
+          if (newLower != null && newUpper != null && compare(column, 
newLower, newUpper) >= 0) {
+            return none(column);
+          } else {
+            if (newLower != null && newUpper != null && 
areConsecutive(newLower, newUpper)) {
+              return new KuduPredicate(PredicateType.EQUALITY, column, 
newLower, null);
+            } else {
+              return new KuduPredicate(PredicateType.RANGE, column, newLower, 
newUpper);
+            }
+          }
         }
       }
-    } else {
-      if (other.type == PredicateType.EQUALITY) {
-        return other.merge(this);
-      } else {
-        byte[] newLower = other.lower == null ||
-            (lower != null && compare(lower, other.lower) >= 0) ? lower : 
other.lower;
-        byte[] newUpper = other.upper == null ||
-            (upper != null && compare(upper, other.upper) <= 0) ? upper : 
other.upper;
-        if (newLower != null && newUpper != null && compare(newLower, 
newUpper) >= 0) {
-          return none(column);
-        } else {
-          if (newLower != null && newUpper != null && areConsecutive(newLower, 
newUpper)) {
-            return new KuduPredicate(PredicateType.EQUALITY, column, newLower, 
null);
+      case IN_LIST: {
+        if (other.type == PredicateType.EQUALITY) {
+          if (this.inListContains(other.lower)) {
+            return other;
           } else {
-            return new KuduPredicate(PredicateType.RANGE, column, newLower, 
newUpper);
+            return none(column);
+          }
+        } else if (other.type == PredicateType.RANGE) {
+          List<byte[]> values = new ArrayList<>();
+          for (byte[] value : inListValues) {
+            if (other.rangeContains(value)) {
+              values.add(value);
+            }
           }
+          return buildInList(column, values);
+        } else {
+          Preconditions.checkState(other.type == PredicateType.IN_LIST);
+          List<byte[]> values = new ArrayList<>();
+          for (byte[] value : inListValues) {
+            if (other.inListContains(value)) {
+              values.add(value);
+            }
+          }
+          return buildInList(column, values);
         }
       }
+      default: throw new IllegalStateException(String.format("unknown 
predicate type %s", this));
     }
   }
 
   /**
+   * Builds an IN list predicate from a collection of raw values. The 
collection
+   * must be sorted and deduplicated.
+   *
+   * @param column the column
+   * @param values the IN list values
+   * @return an IN list predicate
+   */
+  private static KuduPredicate buildInList(ColumnSchema column, 
Collection<byte[]> values) {
+    // IN (true, false) predicates can be simplified to IS NOT NULL.
+    if (column.getType().getDataType() == Common.DataType.BOOL && 
values.size() > 1) {
+      return KuduPredicate.newIsNotNullPredicate(column);
+    }
+
+    switch (values.size()) {
+      case 0: return KuduPredicate.none(column);
+      case 1: return new KuduPredicate(PredicateType.EQUALITY, column,
+                                       values.iterator().next(), null);
+      default: return new KuduPredicate(column, values.toArray(new 
byte[values.size()][]));
+    }
+  }
+
+  /**
+   * @param value the value to check for
+   * @return {@code true} if this IN list predicate contains the value
+   */
+  boolean inListContains(byte[] value) {
+    final Comparator<byte[]> comparator = new Comparator<byte[]>() {
+      @Override
+      public int compare(byte[] a, byte[] b) {
+        return KuduPredicate.compare(column, a, b);
+      }
+    };
+    return Arrays.binarySearch(inListValues, value, comparator) >= 0;
+  }
+
+  /**
+   * @param value the value to check
+   * @return {@code true} if this RANGE predicate contains the value
+   */
+  boolean rangeContains(byte[] value) {
+    return (lower == null || compare(column, value, lower) >= 0) &&
+           (upper == null || compare(column, value, upper) < 0);
+  }
+
+  /**
    * @return the schema of the predicate column
    */
   ColumnSchema getColumn() {
@@ -426,7 +601,7 @@ public class KuduPredicate {
 
   /**
    * Convert the predicate to the protobuf representation.
-   * @return the protobuf message for this predicate.
+   * @return the protobuf message for this predicate
    */
   @InterfaceAudience.Private
   public Common.ColumnPredicatePB toPB() {
@@ -452,6 +627,13 @@ public class KuduPredicate {
         builder.setIsNotNull(builder.getIsNotNullBuilder());
         break;
       }
+      case IN_LIST: {
+        Common.ColumnPredicatePB.InList.Builder inListBuilder = 
builder.getInListBuilder();
+        for (byte[] value : inListValues) {
+          inListBuilder.addValues(ByteString.copyFrom(value));
+        }
+        break;
+      }
       case NONE: throw new IllegalStateException(
           "can not convert None predicate to protobuf message");
       default: throw new IllegalArgumentException(
@@ -466,7 +648,7 @@ public class KuduPredicate {
    */
   @InterfaceAudience.Private
   public static KuduPredicate fromPB(Schema schema, Common.ColumnPredicatePB 
pb) {
-    ColumnSchema column = schema.getColumn(pb.getColumn());
+    final ColumnSchema column = schema.getColumn(pb.getColumn());
     switch (pb.getPredicateCase()) {
       case EQUALITY:
         return new KuduPredicate(PredicateType.EQUALITY, column,
@@ -479,18 +661,34 @@ public class KuduPredicate {
       }
       case IS_NOT_NULL:
         return newIsNotNullPredicate(column);
+      case IN_LIST: {
+        Common.ColumnPredicatePB.InList inList = pb.getInList();
+
+        SortedSet<byte[]> values = new TreeSet<>(new Comparator<byte[]>() {
+          @Override
+          public int compare(byte[] a, byte[] b) {
+            return KuduPredicate.compare(column, a, b);
+          }
+        });
+
+        for (ByteString value : inList.getValuesList()) {
+          values.add(value.toByteArray());
+        }
+        return buildInList(column, values);
+      }
       default:
         throw new IllegalArgumentException("unknown predicate type");
     }
   }
 
   /**
-   * Compares two bounds based on the type of this predicate's column.
+   * Compares two bounds based on the type of the column.
+   * @param column the column which the values belong to
    * @param a the first serialized value
    * @param b the second serialized value
    * @return the comparison of the serialized values based on the column type
    */
-  private int compare(byte[] a, byte[] b) {
+  private static int compare(ColumnSchema column, byte[] a, byte[] b) {
     switch (column.getType().getDataType()) {
       case BOOL:
         return Boolean.compare(Bytes.getBoolean(a), Bytes.getBoolean(b));
@@ -583,6 +781,13 @@ public class KuduPredicate {
   }
 
   /**
+   * @return the IN list values. Always kept sorted and de-duplicated.
+   */
+  byte[][] getInListValues() {
+    return inListValues;
+  }
+
+  /**
    * Returns the maximum value for the integer type.
    * @param type an integer type
    * @return the maximum value
@@ -674,6 +879,13 @@ public class KuduPredicate {
                                column.getName(), valueToString(upper));
         }
       }
+      case IN_LIST: {
+        List<String> strings = new ArrayList<>(inListValues.length);
+        for (byte[] value : inListValues) {
+          strings.add(valueToString(value));
+        }
+        return String.format("`%s` IN (%s)", column.getName(), Joiner.on(", 
").join(strings));
+      }
       case IS_NOT_NULL: return String.format("`%s` IS NOT NULL", 
column.getName());
       case NONE: return String.format("`%s` NONE", column.getName());
       default: throw new IllegalArgumentException(String.format("unknown 
predicate type %s", type));
@@ -688,11 +900,13 @@ public class KuduPredicate {
     return type == that.type &&
         column.equals(that.column) &&
         Arrays.equals(lower, that.lower) &&
-        Arrays.equals(upper, that.upper);
+        Arrays.equals(upper, that.upper) &&
+        Arrays.deepEquals(inListValues, that.inListValues);
   }
 
   @Override
   public int hashCode() {
-    return Objects.hashCode(type, column, Arrays.hashCode(lower), 
Arrays.hashCode(upper));
+    return Objects.hashCode(type, column, Arrays.hashCode(lower),
+                            Arrays.hashCode(upper), 
Arrays.deepHashCode(inListValues));
   }
 }

http://git-wip-us.apache.org/repos/asf/kudu/blob/cb92799b/java/kudu-client/src/main/java/org/apache/kudu/client/PartitionPruner.java
----------------------------------------------------------------------
diff --git 
a/java/kudu-client/src/main/java/org/apache/kudu/client/PartitionPruner.java 
b/java/kudu-client/src/main/java/org/apache/kudu/client/PartitionPruner.java
index 910083f..59c85d5 100644
--- a/java/kudu-client/src/main/java/org/apache/kudu/client/PartitionPruner.java
+++ b/java/kudu-client/src/main/java/org/apache/kudu/client/PartitionPruner.java
@@ -373,7 +373,12 @@ public class PartitionPruner {
           row.setRaw(idx, predicate.getLower());
           pushedPredicates++;
           break;
-        case IS_NOT_NULL: break loop;
+        case IS_NOT_NULL:
+          break loop;
+        case IN_LIST:
+          row.setRaw(idx, predicate.getInListValues()[0]);
+          pushedPredicates++;
+          break;
         default:
           throw new IllegalArgumentException(
               String.format("unexpected predicate type can not be pushed into 
key: %s", predicate));
@@ -435,6 +440,13 @@ public class PartitionPruner {
           break loop;
         case IS_NOT_NULL:
           break loop;
+        case IN_LIST: {
+          byte[][] values = predicate.getInListValues();
+          row.setRaw(idx, values[values.length - 1]);
+          pushedPredicates++;
+          finalPredicate = predicate;
+          break;
+        }
         default:
           throw new IllegalArgumentException(
               String.format("unexpected predicate type can not be pushed into 
key: %s", predicate));
@@ -444,9 +456,10 @@ public class PartitionPruner {
     // If no predicates were pushed, no need to do any more work.
     if (pushedPredicates == 0) return AsyncKuduClient.EMPTY_ARRAY;
 
-    // Step 2: If the final predicate is an equality predicate, increment the
+    // Step 2: If the final predicate is an equality or IN-list predicate, 
increment the
     // key to convert it to an exclusive upper bound.
-    if (finalPredicate.getType() == KuduPredicate.PredicateType.EQUALITY) {
+    if (finalPredicate.getType() == KuduPredicate.PredicateType.EQUALITY ||
+        finalPredicate.getType() == KuduPredicate.PredicateType.IN_LIST) {
       incrementKey(row, rangePartitionColumnIdxs.subList(0, pushedPredicates));
     }
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/cb92799b/java/kudu-client/src/test/java/org/apache/kudu/client/TestKuduClient.java
----------------------------------------------------------------------
diff --git 
a/java/kudu-client/src/test/java/org/apache/kudu/client/TestKuduClient.java 
b/java/kudu-client/src/test/java/org/apache/kudu/client/TestKuduClient.java
index e59ea8e..d78a433 100644
--- a/java/kudu-client/src/test/java/org/apache/kudu/client/TestKuduClient.java
+++ b/java/kudu-client/src/test/java/org/apache/kudu/client/TestKuduClient.java
@@ -337,6 +337,23 @@ public class TestKuduClient extends BaseKuduTest {
        KuduPredicate.newIsNotNullPredicate(schema.getColumn("c2")),
        KuduPredicate.newIsNotNullPredicate(schema.getColumn("key"))
     ).size());
+
+    // IN list
+    assertEquals(3, scanTableToStrings(table,
+       KuduPredicate.newInListPredicate(schema.getColumn("key"),
+                                        ImmutableList.of("key_30", "key_01", 
"invalid", "key_99"))
+    ).size());
+    assertEquals(3, scanTableToStrings(table,
+       KuduPredicate.newInListPredicate(schema.getColumn("c2"),
+                                        ImmutableList.of("c2_30", "c2_1", 
"invalid", "c2_99"))
+    ).size());
+    assertEquals(2, scanTableToStrings(table,
+       KuduPredicate.newInListPredicate(schema.getColumn("c2"),
+                                        ImmutableList.of("c2_30", "c2_1", 
"invalid", "c2_99")),
+       KuduPredicate.newIsNotNullPredicate(schema.getColumn("c2")),
+       KuduPredicate.newInListPredicate(schema.getColumn("key"),
+                                        ImmutableList.of("key_30", "key_45", 
"invalid", "key_99"))
+    ).size());
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/kudu/blob/cb92799b/java/kudu-client/src/test/java/org/apache/kudu/client/TestKuduPredicate.java
----------------------------------------------------------------------
diff --git 
a/java/kudu-client/src/test/java/org/apache/kudu/client/TestKuduPredicate.java 
b/java/kudu-client/src/test/java/org/apache/kudu/client/TestKuduPredicate.java
index 3ad39fe..785594f 100644
--- 
a/java/kudu-client/src/test/java/org/apache/kudu/client/TestKuduPredicate.java
+++ 
b/java/kudu-client/src/test/java/org/apache/kudu/client/TestKuduPredicate.java
@@ -18,11 +18,15 @@
 package org.apache.kudu.client;
 
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
 import org.junit.Assert;
 import org.junit.Test;
 import org.apache.kudu.ColumnSchema;
 import org.apache.kudu.Type;
 
+import java.util.Arrays;
+
 import static org.apache.kudu.client.KuduPredicate.ComparisonOp.EQUAL;
 import static org.apache.kudu.client.KuduPredicate.ComparisonOp.GREATER;
 import static org.apache.kudu.client.KuduPredicate.ComparisonOp.GREATER_EQUAL;
@@ -64,6 +68,18 @@ public class TestKuduPredicate {
     return new KuduPredicate(RANGE, intCol, Bytes.fromInt(lower), 
Bytes.fromInt(upper));
   }
 
+  private static KuduPredicate intInList(Integer... values) {
+    return KuduPredicate.newInListPredicate(intCol, Arrays.asList(values));
+  }
+
+  private static KuduPredicate boolInList(Boolean... values) {
+    return KuduPredicate.newInListPredicate(boolCol, Arrays.asList(values));
+  }
+
+  private static KuduPredicate stringInList(String... values) {
+    return KuduPredicate.newInListPredicate(stringCol, Arrays.asList(values));
+  }
+
   private void testMerge(KuduPredicate a,
                          KuduPredicate b,
                          KuduPredicate expected) {
@@ -351,6 +367,113 @@ public class TestKuduPredicate {
               KuduPredicate.newComparisonPredicate(intCol, EQUAL, 7),
               KuduPredicate.none(intCol));
 
+    // IN list + IN list
+
+    // | | |
+    //   | | |
+    testMerge(intInList(0, 10, 20),
+              intInList(20, 10, 20, 30),
+              intInList(10, 20));
+
+    // |   |
+    //    | |
+    testMerge(intInList(0, 20),
+              intInList(15, 30),
+              KuduPredicate.none(intCol));
+
+    // IN list + NOT NULL
+    testMerge(intInList(10),
+              KuduPredicate.newIsNotNullPredicate(intCol),
+              KuduPredicate.newComparisonPredicate(intCol, EQUAL, 10));
+
+    testMerge(intInList(10, -100),
+              KuduPredicate.newIsNotNullPredicate(intCol),
+              intInList(-100, 10));
+
+    // IN list + Equality
+
+    // | | |
+    //   |
+    // =
+    //   |
+    testMerge(intInList(0, 10, 20),
+              KuduPredicate.newComparisonPredicate(intCol, EQUAL, 10),
+              KuduPredicate.newComparisonPredicate(intCol, EQUAL, 10));
+
+    // | | |
+    //       |
+    // =
+    // none
+    testMerge(intInList(0, 10, 20),
+              KuduPredicate.newComparisonPredicate(intCol, EQUAL, 30),
+              KuduPredicate.none(intCol));
+
+    // IN list + Range
+
+    // | | | | |
+    //   [---)
+    // =
+    //   | |
+    testMerge(intInList(0, 10, 20, 30, 40),
+              intRange(10, 30),
+              intInList(10, 20));
+
+    // | |   | |
+    //    [--)
+    // =
+    // none
+    testMerge(intInList(0, 10, 20, 30),
+              intRange(25, 30),
+              KuduPredicate.none(intCol));
+
+    // | | | |
+    //    [------>
+    // =
+    //   | |
+    testMerge(intInList(0, 10, 20, 30),
+              KuduPredicate.newComparisonPredicate(intCol, GREATER_EQUAL, 15),
+              intInList(20, 30));
+
+    // | | |
+    //    [------>
+    // =
+    //     |
+    testMerge(intInList(0, 10, 20),
+              KuduPredicate.newComparisonPredicate(intCol, GREATER_EQUAL, 15),
+              KuduPredicate.newComparisonPredicate(intCol, EQUAL, 20));
+
+    // | |
+    //    [------>
+    // =
+    // none
+    testMerge(intInList(0, 10),
+              KuduPredicate.newComparisonPredicate(intCol, GREATER_EQUAL, 15),
+              KuduPredicate.none(intCol));
+
+    // | | | |
+    // <--)
+    // =
+    // | |
+    testMerge(intInList(0, 10, 20, 30),
+              KuduPredicate.newComparisonPredicate(intCol, LESS, 15),
+              intInList(0, 10));
+
+    // |  | |
+    // <--)
+    // =
+    // |
+    testMerge(intInList(0, 10, 20),
+              KuduPredicate.newComparisonPredicate(intCol, LESS, 10),
+              KuduPredicate.newComparisonPredicate(intCol, EQUAL, 0));
+
+    //      | |
+    // <--)
+    // =
+    // none
+    testMerge(intInList(10, 20),
+              KuduPredicate.newComparisonPredicate(intCol, LESS, 5),
+              KuduPredicate.none(intCol));
+
     // None
 
     // None AND
@@ -431,6 +554,22 @@ public class TestKuduPredicate {
               KuduPredicate.newComparisonPredicate(stringCol, LESS, "a\0\0"),
               new KuduPredicate(RANGE, stringCol,
                                 Bytes.fromString("a"), 
Bytes.fromString("a\0\0")));
+
+    //     [----->
+    //   | | | |
+    // =
+    //     [--)
+    testMerge(KuduPredicate.newComparisonPredicate(stringCol, GREATER_EQUAL, 
"a"),
+              stringInList("a", "c", "b", ""),
+              stringInList("a", "b", "c"));
+
+    //   IS NOT NULL
+    //   | | | |
+    // =
+    //     [--)
+    testMerge(KuduPredicate.newIsNotNullPredicate(stringCol),
+              stringInList("a", "c", "b", ""),
+              stringInList("", "a", "b", "c"));
   }
 
   @Test
@@ -467,6 +606,21 @@ public class TestKuduPredicate {
     // b <= true
     Assert.assertEquals(KuduPredicate.newIsNotNullPredicate(boolCol),
                         KuduPredicate.newComparisonPredicate(boolCol, 
LESS_EQUAL, true));
+
+    // b IN ()
+    Assert.assertEquals(KuduPredicate.none(boolCol), boolInList());
+
+    // b IN (true)
+    Assert.assertEquals(KuduPredicate.newComparisonPredicate(boolCol, EQUAL, 
true),
+                        boolInList(true, true, true));
+
+    // b IN (false)
+    Assert.assertEquals(KuduPredicate.newComparisonPredicate(boolCol, EQUAL, 
false),
+                        boolInList(false));
+
+    // b IN (false, true)
+    Assert.assertEquals(KuduPredicate.newIsNotNullPredicate(boolCol),
+                        boolInList(false, true, false, true));
   }
 
   /**
@@ -493,6 +647,10 @@ public class TestKuduPredicate {
                                 new byte[] { (byte) 0 },
                                 new byte[] { (byte) 10 }));
 
+    testMerge(KuduPredicate.newInListPredicate(byteCol, 
ImmutableList.of((byte) 12, (byte) 14, (byte) 16, (byte) 18)),
+              KuduPredicate.newInListPredicate(byteCol, 
ImmutableList.of((byte) 14, (byte) 18, (byte) 20)),
+              KuduPredicate.newInListPredicate(byteCol, 
ImmutableList.of((byte) 14, (byte) 18)));
+
     testMerge(KuduPredicate.newComparisonPredicate(shortCol, GREATER_EQUAL, 0),
               KuduPredicate.newComparisonPredicate(shortCol, LESS, 10),
               new KuduPredicate(RANGE,
@@ -500,6 +658,10 @@ public class TestKuduPredicate {
                                 Bytes.fromShort((short) 0),
                                 Bytes.fromShort((short) 10)));
 
+    testMerge(KuduPredicate.newInListPredicate(shortCol, 
ImmutableList.of((short) 12, (short) 14, (short) 16, (short) 18)),
+              KuduPredicate.newInListPredicate(shortCol, 
ImmutableList.of((short) 14, (short) 18, (short) 20)),
+              KuduPredicate.newInListPredicate(shortCol, 
ImmutableList.of((short) 14, (short) 18)));
+
     testMerge(KuduPredicate.newComparisonPredicate(longCol, GREATER_EQUAL, 0),
               KuduPredicate.newComparisonPredicate(longCol, LESS, 10),
               new KuduPredicate(RANGE,
@@ -507,6 +669,10 @@ public class TestKuduPredicate {
                                 Bytes.fromLong(0),
                                 Bytes.fromLong(10)));
 
+    testMerge(KuduPredicate.newInListPredicate(longCol, ImmutableList.of(12L, 
14L, 16L, 18L)),
+              KuduPredicate.newInListPredicate(longCol, ImmutableList.of(14L, 
18L, 20L)),
+              KuduPredicate.newInListPredicate(longCol, ImmutableList.of(14L, 
18L)));
+
     testMerge(KuduPredicate.newComparisonPredicate(floatCol, GREATER_EQUAL, 
123.45f),
               KuduPredicate.newComparisonPredicate(floatCol, LESS, 678.90f),
               new KuduPredicate(RANGE,
@@ -514,6 +680,10 @@ public class TestKuduPredicate {
                                 Bytes.fromFloat(123.45f),
                                 Bytes.fromFloat(678.90f)));
 
+    testMerge(KuduPredicate.newInListPredicate(floatCol, ImmutableList.of(12f, 
14f, 16f, 18f)),
+              KuduPredicate.newInListPredicate(floatCol, ImmutableList.of(14f, 
18f, 20f)),
+              KuduPredicate.newInListPredicate(floatCol, ImmutableList.of(14f, 
18f)));
+
     testMerge(KuduPredicate.newComparisonPredicate(doubleCol, GREATER_EQUAL, 
123.45),
               KuduPredicate.newComparisonPredicate(doubleCol, LESS, 678.90),
               new KuduPredicate(RANGE,
@@ -521,6 +691,10 @@ public class TestKuduPredicate {
                                 Bytes.fromDouble(123.45),
                                 Bytes.fromDouble(678.90)));
 
+    testMerge(KuduPredicate.newInListPredicate(doubleCol, 
ImmutableList.of(12d, 14d, 16d, 18d)),
+              KuduPredicate.newInListPredicate(doubleCol, 
ImmutableList.of(14d, 18d, 20d)),
+              KuduPredicate.newInListPredicate(doubleCol, 
ImmutableList.of(14d, 18d)));
+
     testMerge(KuduPredicate.newComparisonPredicate(binaryCol, GREATER_EQUAL,
                                                    new byte[] { 0, 1, 2, 3, 4, 
5, 6 }),
               KuduPredicate.newComparisonPredicate(binaryCol, LESS, new byte[] 
{ 10 }),
@@ -528,6 +702,10 @@ public class TestKuduPredicate {
                                 binaryCol,
                                 new byte[] { 0, 1, 2, 3, 4, 5, 6 },
                                 new byte[] { 10 }));
+
+    testMerge(KuduPredicate.newInListPredicate(binaryCol, 
ImmutableList.of("a".getBytes(), "b".getBytes(), "c".getBytes(), 
"d".getBytes())),
+              KuduPredicate.newInListPredicate(binaryCol, 
ImmutableList.of("b".getBytes(), "d".getBytes(), "e".getBytes())),
+              KuduPredicate.newInListPredicate(binaryCol, 
ImmutableList.of("b".getBytes(), "d".getBytes())));
   }
 
   @Test
@@ -624,5 +802,33 @@ public class TestKuduPredicate {
                         KuduPredicate.newComparisonPredicate(stringCol, EQUAL, 
"my string").toString());
     Assert.assertEquals("`binary` = 0xAB01CD", 
KuduPredicate.newComparisonPredicate(
         binaryCol, EQUAL, new byte[] { (byte) 0xAB, (byte) 0x01, (byte) 0xCD 
}).toString());
+    Assert.assertEquals("`int` IN (-10, 0, 10)",
+                        intInList(10, 0, -10).toString());
+    Assert.assertEquals("`string` IS NOT NULL",
+                        
KuduPredicate.newIsNotNullPredicate(stringCol).toString());
+
+    Assert.assertEquals("`bool` = true", KuduPredicate.newInListPredicate(
+        boolCol, ImmutableList.of(true)).toString());
+    Assert.assertEquals("`bool` = false", KuduPredicate.newInListPredicate(
+        boolCol, ImmutableList.of(false)).toString());
+    Assert.assertEquals("`bool` IS NOT NULL", KuduPredicate.newInListPredicate(
+        boolCol, ImmutableList.of(false, true, true)).toString());
+    Assert.assertEquals("`byte` IN (1, 10, 100)", 
KuduPredicate.newInListPredicate(
+        byteCol, ImmutableList.of((byte) 1, (byte) 10, (byte) 
100)).toString());
+    Assert.assertEquals("`short` IN (1, 10, 100)", 
KuduPredicate.newInListPredicate(
+        shortCol, ImmutableList.of((short) 1, (short) 100, (short) 
10)).toString());
+    Assert.assertEquals("`int` IN (1, 10, 100)", 
KuduPredicate.newInListPredicate(
+        intCol, ImmutableList.of(1, 100, 10)).toString());
+    Assert.assertEquals("`long` IN (1, 10, 100)", 
KuduPredicate.newInListPredicate(
+        longCol, ImmutableList.of(1L, 100L, 10L)).toString());
+    Assert.assertEquals("`float` IN (78.9, 123.456)", 
KuduPredicate.newInListPredicate(
+        floatCol, ImmutableList.of(123.456f, 78.9f)).toString());
+    Assert.assertEquals("`double` IN (78.9, 123.456)", 
KuduPredicate.newInListPredicate(
+        doubleCol, ImmutableList.of(123.456d, 78.9d)).toString());
+    Assert.assertEquals("`string` IN (\"a\", \"my string\")",
+                        KuduPredicate.newInListPredicate(stringCol, 
ImmutableList.of("my string", "a")).toString());
+    Assert.assertEquals("`binary` IN (0x00, 0xAB01CD)", 
KuduPredicate.newInListPredicate(
+        binaryCol, ImmutableList.of(new byte[] { (byte) 0xAB, (byte) 0x01, 
(byte) 0xCD },
+                                    new byte[] { (byte) 0x00 })).toString());
   }
 }

http://git-wip-us.apache.org/repos/asf/kudu/blob/cb92799b/java/kudu-client/src/test/java/org/apache/kudu/client/TestPartitionPruner.java
----------------------------------------------------------------------
diff --git 
a/java/kudu-client/src/test/java/org/apache/kudu/client/TestPartitionPruner.java
 
b/java/kudu-client/src/test/java/org/apache/kudu/client/TestPartitionPruner.java
index 6c3faff..3a5610f 100644
--- 
a/java/kudu-client/src/test/java/org/apache/kudu/client/TestPartitionPruner.java
+++ 
b/java/kudu-client/src/test/java/org/apache/kudu/client/TestPartitionPruner.java
@@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableList;
 import org.apache.kudu.ColumnSchema;
 import org.apache.kudu.Schema;
 import org.apache.kudu.Type;
+import org.apache.kudu.client.KuduPredicate.ComparisonOp;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -277,124 +278,144 @@ public class TestPartitionPruner extends BaseKuduTest {
 
     // c < -10
     assertEquals(1, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.LESS, -10)));
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.LESS, -10)));
 
     // c = -10
     assertEquals(1, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.EQUAL, -10)));
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.EQUAL, -10)));
 
     // c < 10
     assertEquals(2, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.LESS, 10)));
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.LESS, 10)));
 
     // c < 100
     assertEquals(3, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.LESS, 100)));
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.LESS, 100)));
 
 
     // c >= -10
     assertEquals(3, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.GREATER_EQUAL, -10)));
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.GREATER_EQUAL, 
-10)));
 
     // c >= 0
     assertEquals(3, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.GREATER_EQUAL, -10)));
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.GREATER_EQUAL, 
-10)));
 
     // c >= 5
     assertEquals(2, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.GREATER_EQUAL, 5)));
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.GREATER_EQUAL, 
5)));
 
     // c >= 10
     assertEquals(2, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.GREATER_EQUAL, 10)));
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.GREATER_EQUAL, 
10)));
 
     // c >= 100
     assertEquals(1, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.GREATER_EQUAL, 100)));
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.GREATER_EQUAL, 
100)));
 
     // c >= -10
     // c < 0
     assertEquals(1, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.GREATER_EQUAL, -10),
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.LESS, 0)));
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.GREATER_EQUAL, 
-10),
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.LESS, 0)));
 
     // c >= 5
     // c < 100
     assertEquals(2, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.GREATER_EQUAL, 5),
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.LESS, 100)));
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.GREATER_EQUAL, 5),
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.LESS, 100)));
 
     // b = ""
     assertEquals(3, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(b, 
KuduPredicate.ComparisonOp.EQUAL, "")));
+        KuduPredicate.newComparisonPredicate(b, ComparisonOp.EQUAL, "")));
 
     // b >= "z"
     assertEquals(3, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(b, 
KuduPredicate.ComparisonOp.GREATER_EQUAL, "z")));
+        KuduPredicate.newComparisonPredicate(b, ComparisonOp.GREATER_EQUAL, 
"z")));
 
     // b < "a"
     assertEquals(3, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(b, 
KuduPredicate.ComparisonOp.LESS, "a")));
+        KuduPredicate.newComparisonPredicate(b, ComparisonOp.LESS, "a")));
 
     // b >= "m"
     // b < "z"
     assertEquals(3, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(b, 
KuduPredicate.ComparisonOp.GREATER_EQUAL, "m"),
-        KuduPredicate.newComparisonPredicate(b, 
KuduPredicate.ComparisonOp.LESS, "z")));
+        KuduPredicate.newComparisonPredicate(b, ComparisonOp.GREATER_EQUAL, 
"m"),
+        KuduPredicate.newComparisonPredicate(b, ComparisonOp.LESS, "z")));
 
     // c >= 10
     // b >= "r"
     assertEquals(1, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.GREATER_EQUAL, 10),
-        KuduPredicate.newComparisonPredicate(b, 
KuduPredicate.ComparisonOp.GREATER_EQUAL, "r")));
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.GREATER_EQUAL, 
10),
+        KuduPredicate.newComparisonPredicate(b, ComparisonOp.GREATER_EQUAL, 
"r")));
 
     // c >= 10
     // b < "r"
     assertEquals(2, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.GREATER_EQUAL, 10),
-        KuduPredicate.newComparisonPredicate(b, 
KuduPredicate.ComparisonOp.LESS, "r")));
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.GREATER_EQUAL, 
10),
+        KuduPredicate.newComparisonPredicate(b, ComparisonOp.LESS, "r")));
 
     // c = 10
     // b < "r"
     assertEquals(1, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.EQUAL, 10),
-        KuduPredicate.newComparisonPredicate(b, 
KuduPredicate.ComparisonOp.LESS, "r")));
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.EQUAL, 10),
+        KuduPredicate.newComparisonPredicate(b, ComparisonOp.LESS, "r")));
 
     // c < 0
     // b < "m"
     assertEquals(1, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.EQUAL, 0),
-        KuduPredicate.newComparisonPredicate(b, 
KuduPredicate.ComparisonOp.LESS, "m")));
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.EQUAL, 0),
+        KuduPredicate.newComparisonPredicate(b, ComparisonOp.LESS, "m")));
 
     // c < 0
     // b < "z"
     assertEquals(1, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.LESS, 0),
-        KuduPredicate.newComparisonPredicate(b, 
KuduPredicate.ComparisonOp.LESS, "z")));
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.LESS, 0),
+        KuduPredicate.newComparisonPredicate(b, ComparisonOp.LESS, "z")));
 
     // c = 0
     // b = "m\0"
     assertEquals(1, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.EQUAL, 0),
-        KuduPredicate.newComparisonPredicate(b, 
KuduPredicate.ComparisonOp.EQUAL, "m\0")));
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.EQUAL, 0),
+        KuduPredicate.newComparisonPredicate(b, ComparisonOp.EQUAL, "m\0")));
 
     // c = 0
     // b < "m"
     assertEquals(1, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.EQUAL, 0),
-        KuduPredicate.newComparisonPredicate(b, 
KuduPredicate.ComparisonOp.LESS, "m")));
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.EQUAL, 0),
+        KuduPredicate.newComparisonPredicate(b, ComparisonOp.LESS, "m")));
 
     // c = 0
     // b < "m\0"
     assertEquals(2, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.EQUAL, 0),
-        KuduPredicate.newComparisonPredicate(b, 
KuduPredicate.ComparisonOp.LESS, "m\0")));
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.EQUAL, 0),
+        KuduPredicate.newComparisonPredicate(b, ComparisonOp.LESS, "m\0")));
 
     // c = 0
     // c = 2
     assertEquals(0, countPartitions(table, partitions,
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.EQUAL, 0),
-        KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.EQUAL, 2)));
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.EQUAL, 0),
+        KuduPredicate.newComparisonPredicate(c, ComparisonOp.EQUAL, 2)));
+
+    // a IN (1, 2)
+    assertEquals(1, countPartitions(table, partitions,
+        KuduPredicate.newInListPredicate(c, ImmutableList.of((byte) 1, (byte) 
2))));
+
+    // a IN (0, 1, 2)
+    assertEquals(2, countPartitions(table, partitions,
+        KuduPredicate.newInListPredicate(c, ImmutableList.of((byte) 0, (byte) 
1, (byte) 2))));
+
+    // a IN (-10, 0)
+    // B < "m"
+    assertEquals(1, countPartitions(table, partitions,
+        KuduPredicate.newInListPredicate(c, ImmutableList.of((byte) -10, 
(byte) 0)),
+        KuduPredicate.newComparisonPredicate(b, ComparisonOp.LESS, "m")));
+
+    // a IN (-10, 0)
+    // B < "m\0"
+    assertEquals(2, countPartitions(table, partitions,
+        KuduPredicate.newInListPredicate(c, ImmutableList.of((byte) -10, 
(byte) 0)),
+        KuduPredicate.newComparisonPredicate(b, ComparisonOp.LESS, "m\0")));
   }
 
   @Test
@@ -425,41 +446,45 @@ public class TestPartitionPruner extends BaseKuduTest {
 
     // a = 0;
     assertEquals(2, countPartitions(table, partitions,
-          KuduPredicate.newComparisonPredicate(a, 
KuduPredicate.ComparisonOp.EQUAL, 0)));
+          KuduPredicate.newComparisonPredicate(a, ComparisonOp.EQUAL, 0)));
 
     // a >= 0;
     assertEquals(4, countPartitions(table, partitions,
-          KuduPredicate.newComparisonPredicate(a, 
KuduPredicate.ComparisonOp.GREATER_EQUAL, 0)));
+          KuduPredicate.newComparisonPredicate(a, ComparisonOp.GREATER_EQUAL, 
0)));
 
     // a >= 0;
     // a < 1;
     assertEquals(2, countPartitions(table, partitions,
-          KuduPredicate.newComparisonPredicate(a, 
KuduPredicate.ComparisonOp.GREATER_EQUAL, 0),
-          KuduPredicate.newComparisonPredicate(a, 
KuduPredicate.ComparisonOp.LESS, 1)));
+          KuduPredicate.newComparisonPredicate(a, ComparisonOp.GREATER_EQUAL, 
0),
+          KuduPredicate.newComparisonPredicate(a, ComparisonOp.LESS, 1)));
 
     // a >= 0;
     // a < 2;
     assertEquals(4, countPartitions(table, partitions,
-          KuduPredicate.newComparisonPredicate(a, 
KuduPredicate.ComparisonOp.GREATER_EQUAL, 0),
-          KuduPredicate.newComparisonPredicate(a, 
KuduPredicate.ComparisonOp.LESS, 2)));
+          KuduPredicate.newComparisonPredicate(a, ComparisonOp.GREATER_EQUAL, 
0),
+          KuduPredicate.newComparisonPredicate(a, ComparisonOp.LESS, 2)));
 
     // b = 1;
     assertEquals(4, countPartitions(table, partitions,
-          KuduPredicate.newComparisonPredicate(b, 
KuduPredicate.ComparisonOp.EQUAL, 1)));
+          KuduPredicate.newComparisonPredicate(b, ComparisonOp.EQUAL, 1)));
 
     // b = 1;
     // c = 2;
     assertEquals(2, countPartitions(table, partitions,
-          KuduPredicate.newComparisonPredicate(b, 
KuduPredicate.ComparisonOp.EQUAL, 1),
-          KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.EQUAL, 2)));
+          KuduPredicate.newComparisonPredicate(b, ComparisonOp.EQUAL, 1),
+          KuduPredicate.newComparisonPredicate(c, ComparisonOp.EQUAL, 2)));
 
     // a = 0;
     // b = 1;
     // c = 2;
     assertEquals(1, countPartitions(table, partitions,
-          KuduPredicate.newComparisonPredicate(a, 
KuduPredicate.ComparisonOp.EQUAL, 0),
-          KuduPredicate.newComparisonPredicate(b, 
KuduPredicate.ComparisonOp.EQUAL, 1),
-          KuduPredicate.newComparisonPredicate(c, 
KuduPredicate.ComparisonOp.EQUAL, 2)));
+          KuduPredicate.newComparisonPredicate(a, ComparisonOp.EQUAL, 0),
+          KuduPredicate.newComparisonPredicate(b, ComparisonOp.EQUAL, 1),
+          KuduPredicate.newComparisonPredicate(c, ComparisonOp.EQUAL, 2)));
+
+    // a IN (0, 10)
+    assertEquals(4, countPartitions(table, partitions,
+          KuduPredicate.newInListPredicate(c, ImmutableList.of((byte) 0, 
(byte) 10))));
   }
 
   @Test
@@ -468,9 +493,9 @@ public class TestPartitionPruner extends BaseKuduTest {
     // (host STRING, metric STRING, timestamp UNIXTIME_MICROS, value DOUBLE)
     // PRIMARY KEY (host, metric, time)
     // DISTRIBUTE BY
-    //    RANGE(time) SPLIT ROWS [(10)],
-    //        (PARTITION       VALUES < 10,
-    //         PARTITION 10 <= VALUES);
+    //    RANGE(time)
+    //        (PARTITION VALUES < 10,
+    //         PARTITION VALUES >= 10);
     //    HASH (host, metric) 2 PARTITIONS;
 
     ColumnSchema host = new ColumnSchema.ColumnSchemaBuilder("host", 
Type.STRING).key(true).build();
@@ -498,55 +523,55 @@ public class TestPartitionPruner extends BaseKuduTest {
 
     // host = "a"
     assertEquals(4, countPartitions(table, partitions,
-          KuduPredicate.newComparisonPredicate(host, 
KuduPredicate.ComparisonOp.EQUAL, "a")));
+          KuduPredicate.newComparisonPredicate(host, ComparisonOp.EQUAL, 
"a")));
 
     // host = "a"
     // metric = "a"
     assertEquals(2, countPartitions(table, partitions,
-          KuduPredicate.newComparisonPredicate(host, 
KuduPredicate.ComparisonOp.EQUAL, "a"),
-          KuduPredicate.newComparisonPredicate(metric, 
KuduPredicate.ComparisonOp.EQUAL, "a")));
+          KuduPredicate.newComparisonPredicate(host, ComparisonOp.EQUAL, "a"),
+          KuduPredicate.newComparisonPredicate(metric, ComparisonOp.EQUAL, 
"a")));
 
     // host = "a"
     // metric = "a"
     // timestamp >= 9;
     assertEquals(2, countPartitions(table, partitions,
-          KuduPredicate.newComparisonPredicate(host, 
KuduPredicate.ComparisonOp.EQUAL, "a"),
-          KuduPredicate.newComparisonPredicate(metric, 
KuduPredicate.ComparisonOp.EQUAL, "a"),
-          KuduPredicate.newComparisonPredicate(timestamp, 
KuduPredicate.ComparisonOp.GREATER_EQUAL, 9)));
+          KuduPredicate.newComparisonPredicate(host, ComparisonOp.EQUAL, "a"),
+          KuduPredicate.newComparisonPredicate(metric, ComparisonOp.EQUAL, 
"a"),
+          KuduPredicate.newComparisonPredicate(timestamp, 
ComparisonOp.GREATER_EQUAL, 9)));
 
     // host = "a"
     // metric = "a"
     // timestamp >= 10;
     // timestamp < 20;
     assertEquals(1, countPartitions(table, partitions,
-          KuduPredicate.newComparisonPredicate(host, 
KuduPredicate.ComparisonOp.EQUAL, "a"),
-          KuduPredicate.newComparisonPredicate(metric, 
KuduPredicate.ComparisonOp.EQUAL, "a"),
-          KuduPredicate.newComparisonPredicate(timestamp, 
KuduPredicate.ComparisonOp.GREATER_EQUAL, 10),
-          KuduPredicate.newComparisonPredicate(timestamp, 
KuduPredicate.ComparisonOp.LESS, 20)));
+          KuduPredicate.newComparisonPredicate(host, ComparisonOp.EQUAL, "a"),
+          KuduPredicate.newComparisonPredicate(metric, ComparisonOp.EQUAL, 
"a"),
+          KuduPredicate.newComparisonPredicate(timestamp, 
ComparisonOp.GREATER_EQUAL, 10),
+          KuduPredicate.newComparisonPredicate(timestamp, ComparisonOp.LESS, 
20)));
 
     // host = "a"
     // metric = "a"
     // timestamp < 10;
     assertEquals(1, countPartitions(table, partitions,
-          KuduPredicate.newComparisonPredicate(host, 
KuduPredicate.ComparisonOp.EQUAL, "a"),
-          KuduPredicate.newComparisonPredicate(metric, 
KuduPredicate.ComparisonOp.EQUAL, "a"),
-          KuduPredicate.newComparisonPredicate(timestamp, 
KuduPredicate.ComparisonOp.LESS, 10)));
+          KuduPredicate.newComparisonPredicate(host, ComparisonOp.EQUAL, "a"),
+          KuduPredicate.newComparisonPredicate(metric, ComparisonOp.EQUAL, 
"a"),
+          KuduPredicate.newComparisonPredicate(timestamp, ComparisonOp.LESS, 
10)));
 
     // host = "a"
     // metric = "a"
     // timestamp >= 10;
     assertEquals(1, countPartitions(table, partitions,
-          KuduPredicate.newComparisonPredicate(host, 
KuduPredicate.ComparisonOp.EQUAL, "a"),
-          KuduPredicate.newComparisonPredicate(metric, 
KuduPredicate.ComparisonOp.EQUAL, "a"),
-          KuduPredicate.newComparisonPredicate(timestamp, 
KuduPredicate.ComparisonOp.GREATER_EQUAL, 10)));
+          KuduPredicate.newComparisonPredicate(host, ComparisonOp.EQUAL, "a"),
+          KuduPredicate.newComparisonPredicate(metric, ComparisonOp.EQUAL, 
"a"),
+          KuduPredicate.newComparisonPredicate(timestamp, 
ComparisonOp.GREATER_EQUAL, 10)));
 
     // host = "a"
     // metric = "a"
     // timestamp = 10;
     assertEquals(1, countPartitions(table, partitions,
-          KuduPredicate.newComparisonPredicate(host, 
KuduPredicate.ComparisonOp.EQUAL, "a"),
-          KuduPredicate.newComparisonPredicate(metric, 
KuduPredicate.ComparisonOp.EQUAL, "a"),
-          KuduPredicate.newComparisonPredicate(timestamp, 
KuduPredicate.ComparisonOp.EQUAL, 10)));
+          KuduPredicate.newComparisonPredicate(host, ComparisonOp.EQUAL, "a"),
+          KuduPredicate.newComparisonPredicate(metric, ComparisonOp.EQUAL, 
"a"),
+          KuduPredicate.newComparisonPredicate(timestamp, ComparisonOp.EQUAL, 
10)));
 
     // partition key < (hash=1)
     assertEquals(2, countPartitions(table, partitions, new byte[] {}, new 
byte[] { 0, 0, 0, 1 }));
@@ -557,11 +582,34 @@ public class TestPartitionPruner extends BaseKuduTest {
     // timestamp = 10
     // partition key < (hash=1)
     assertEquals(1, countPartitions(table, partitions, new byte[] {}, new 
byte[] { 0, 0, 0, 1 },
-          KuduPredicate.newComparisonPredicate(timestamp, 
KuduPredicate.ComparisonOp.EQUAL, 10)));
+          KuduPredicate.newComparisonPredicate(timestamp, ComparisonOp.EQUAL, 
10)));
 
     // timestamp = 10
     // partition key >= (hash=1)
     assertEquals(1, countPartitions(table, partitions, new byte[] { 0, 0, 0, 1 
}, new byte[] {},
-          KuduPredicate.newComparisonPredicate(timestamp, 
KuduPredicate.ComparisonOp.EQUAL, 10)));
+          KuduPredicate.newComparisonPredicate(timestamp, ComparisonOp.EQUAL, 
10)));
+
+    // timestamp IN (0, 9)
+    // host = "a"
+    // metric IN ("foo", "bar")
+    //
+    // We do not prune hash partitions based on IN list predicates (yet),
+    // so the IN list on the hash columns is really just testing that it 
doesn't fail.
+    assertEquals(2, countPartitions(table, partitions,
+          KuduPredicate.newInListPredicate(timestamp, ImmutableList.of(0L, 
9L)),
+          KuduPredicate.newComparisonPredicate(host, ComparisonOp.EQUAL, "a"),
+          KuduPredicate.newInListPredicate(metric, ImmutableList.of("foo", 
"bar"))));
+
+    // timestamp IN (10, 100)
+    assertEquals(2, countPartitions(table, partitions,
+          KuduPredicate.newInListPredicate(timestamp, ImmutableList.of(10L, 
100L))));
+
+    // timestamp IN (9, 10)
+    assertEquals(4, countPartitions(table, partitions,
+          KuduPredicate.newInListPredicate(timestamp, ImmutableList.of(9L, 
10L))));
+
+    // timestamp IS NOT NULL
+    assertEquals(4, countPartitions(table, partitions,
+          KuduPredicate.newIsNotNullPredicate(timestamp)));
   }
 }

Reply via email to