You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by ad...@apache.org on 2016/04/01 05:16:08 UTC

[6/7] incubator-kudu git commit: [c++ client] add comprehensive scan predicate tests

[c++ client] add comprehensive scan predicate tests

Previously we had very little test coverage on predicates across column types.

There is unfortunately a lot of code duplication between the test cases, but
this is unavoidable because we don't have a way to generically set column values
in a PartialRow or create a predicate.

Change-Id: Id4a4c05a41a1e7984f330fe6ff912f3f9924c3a7
Reviewed-on: http://gerrit.cloudera.org:8080/2677
Reviewed-by: Adar Dembo <ad...@cloudera.com>
Tested-by: Kudu Jenkins


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

Branch: refs/heads/master
Commit: 74c6ae907ea2a701a9e21a5bbb1d94f331fe7c86
Parents: 5eb9fcf
Author: Dan Burkert <da...@cloudera.com>
Authored: Wed Mar 30 15:34:24 2016 -0700
Committer: Dan Burkert <da...@cloudera.com>
Committed: Fri Apr 1 01:20:01 2016 +0000

----------------------------------------------------------------------
 src/kudu/client/CMakeLists.txt    |   1 +
 src/kudu/client/predicate-test.cc | 732 +++++++++++++++++++++++++++++++++
 2 files changed, 733 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/74c6ae90/src/kudu/client/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/kudu/client/CMakeLists.txt b/src/kudu/client/CMakeLists.txt
index 4ebae41..c3f909a 100644
--- a/src/kudu/client/CMakeLists.txt
+++ b/src/kudu/client/CMakeLists.txt
@@ -236,3 +236,4 @@ endif()
 set(KUDU_TEST_LINK_LIBS kudu_client integration-tests ${KUDU_MIN_TEST_LIBS})
 ADD_KUDU_TEST(client-test)
 ADD_KUDU_TEST(client-unittest)
+ADD_KUDU_TEST(predicate-test)

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/74c6ae90/src/kudu/client/predicate-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/client/predicate-test.cc b/src/kudu/client/predicate-test.cc
new file mode 100644
index 0000000..76bf5d4
--- /dev/null
+++ b/src/kudu/client/predicate-test.cc
@@ -0,0 +1,732 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include <algorithm>
+#include <cmath>
+#include <gtest/gtest.h>
+#include <limits>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "kudu/client/client.h"
+#include "kudu/gutil/stringprintf.h"
+#include "kudu/gutil/strings/escaping.h"
+#include "kudu/gutil/strings/substitute.h"
+#include "kudu/integration-tests/cluster_verifier.h"
+#include "kudu/integration-tests/mini_cluster.h"
+#include "kudu/util/test_util.h"
+
+using std::count_if;
+using std::numeric_limits;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+namespace kudu {
+namespace client {
+
+using sp::shared_ptr;
+
+class PredicateTest : public KuduTest {
+
+ protected:
+
+  void SetUp() override {
+    // Set up the mini cluster
+    cluster_.reset(new MiniCluster(env_.get(), MiniClusterOptions()));
+    ASSERT_OK(cluster_->Start());
+    KuduClientBuilder client_builder;
+    ASSERT_OK(cluster_->CreateClient(&client_builder, &client_));
+  }
+
+  void TearDown() override {
+    if (cluster_) {
+      cluster_->Shutdown();
+      cluster_.reset();
+    }
+  }
+
+  // Creates a key/value table schema with an int64 key and value of the
+  // specified type.
+  shared_ptr<KuduTable> CreateAndOpenTable(KuduColumnSchema::DataType value_type) {
+    KuduSchema schema;
+    {
+      KuduSchemaBuilder builder;
+      builder.AddColumn("key")->NotNull()->Type(KuduColumnSchema::INT64)->PrimaryKey();
+      builder.AddColumn("value")->Type(value_type);
+      CHECK_OK(builder.Build(&schema));
+    }
+    unique_ptr<client::KuduTableCreator> table_creator(client_->NewTableCreator());
+    CHECK_OK(table_creator->table_name("table")
+        .schema(&schema)
+        .num_replicas(1)
+        .Create());
+
+    shared_ptr<KuduTable> table;
+    CHECK_OK(client_->OpenTable("table", &table));
+    return table;
+  }
+
+  // Creates a new session in manual flush mode.
+  shared_ptr<KuduSession> CreateSession() {
+    shared_ptr<KuduSession> session = client_->NewSession();
+    session->SetTimeoutMillis(10000);
+    CHECK_OK(session->SetFlushMode(KuduSession::MANUAL_FLUSH));
+    return session;
+  }
+
+  // Count the rows in a table which satisfy the specified predicates.
+  int CountRows(const shared_ptr<KuduTable>& table,
+                const vector<KuduPredicate*>& predicates) {
+    KuduScanner scanner(table.get());
+    CHECK_OK(scanner.SetTimeoutMillis(5000));
+    for (KuduPredicate* predicate : predicates) {
+      CHECK_OK(scanner.AddConjunctPredicate(predicate));
+    }
+    CHECK_OK(scanner.Open());
+
+    int rows = 0;
+    while (scanner.HasMoreRows()) {
+      KuduScanBatch batch;
+      CHECK_OK(scanner.NextBatch(&batch));
+      rows += batch.NumRows();
+    }
+    return rows;
+  }
+
+  // Returns a vector of ints from -50 (inclusive) to 50 (exclusive), and
+  // boundary values.
+  template <typename T>
+  vector<T> CreateIntValues() {
+    vector<T> values;
+    for (int i = -50; i < 50; i++) {
+      values.push_back(i);
+    }
+    values.push_back(numeric_limits<T>::min());
+    values.push_back(numeric_limits<T>::min() + 1);
+    values.push_back(numeric_limits<T>::max() - 1);
+    values.push_back(numeric_limits<T>::max());
+    return values;
+  }
+
+  // Returns a vector of ints for testing as predicate boundaries.
+  template <typename T>
+  vector<T> CreateIntTestValues() {
+    return {
+      numeric_limits<T>::min(),
+      numeric_limits<T>::min() + 1,
+      -51,
+      50,
+      0,
+      49,
+      50,
+      numeric_limits<T>::max() - 1,
+      numeric_limits<T>::max(),
+    };
+  }
+
+  // Returns a vector of floating point numbers from -50.50 (inclusive) to 49.49
+  // (exclusive) (100 values), plus min, max, two normals around 0, two
+  // subnormals around 0, positive and negatic infinity, and NaN.
+  template <typename T>
+  vector<T> CreateFloatingPointValues() {
+    vector<T> values;
+    for (int i = -50; i < 50; i++) {
+      values.push_back(static_cast<T>(i) + static_cast<T>(i) / 100);
+    }
+
+    // Add special values (listed in ascending order)
+    values.push_back(-numeric_limits<T>::infinity());
+    values.push_back(numeric_limits<T>::lowest());
+    values.push_back(-numeric_limits<T>::min());
+    values.push_back(-numeric_limits<T>::denorm_min());
+    values.push_back(-0.0);
+    values.push_back(numeric_limits<T>::denorm_min());
+    values.push_back(numeric_limits<T>::min());
+    values.push_back(numeric_limits<T>::max());
+    values.push_back(numeric_limits<T>::infinity());
+
+    // Add special NaN value
+    // TODO: uncomment after fixing KUDU-1386
+    // values.push_back(numeric_limits<T>::quiet_NaN());
+
+    return values;
+  }
+
+  /// Returns a vector of floating point numbers for creating test predicates.
+  template <typename T>
+  vector<T> CreateFloatingPointTestValues() {
+    return {
+      -numeric_limits<T>::infinity(),
+      numeric_limits<T>::lowest(),
+      -100.0,
+      -1.1,
+      -1.0,
+      -numeric_limits<T>::min(),
+      -numeric_limits<T>::denorm_min(),
+      -0.0,
+      0.0,
+      numeric_limits<T>::denorm_min(),
+      numeric_limits<T>::min(),
+      1.0,
+      1.1,
+      100.0,
+      numeric_limits<T>::max(),
+      numeric_limits<T>::infinity(),
+
+      // TODO: uncomment after fixing KUDU-1386
+      // numeric_limits<T>::quiet_NaN();
+    };
+  }
+
+  // Returns a vector of string values.
+  vector<string> CreateStringValues() {
+    return {
+      string("", 0),
+      string("\0", 1),
+      string("\0\0", 2),
+      string("a", 1),
+      string("a\0", 2),
+      string("a\0a", 3),
+      string("aa\0", 3),
+    };
+  }
+
+  // Check integer predicates against the specified table. The table must have
+  // key/value rows with values from CreateIntValues, plus a null value.
+  template <typename T>
+  void CheckIntPredicates(shared_ptr<KuduTable> table) {
+    vector<T> values = CreateIntValues<T>();
+    vector<T> test_values = CreateIntTestValues<T>();
+    ASSERT_EQ(values.size() + 1, CountRows(table, {}));
+
+    for (T v : test_values) {
+      SCOPED_TRACE(strings::Substitute("test value: $0", v));
+
+      { // value = v
+        int count = count_if(values.begin(), values.end(),
+                             [&] (T value) { return value == v; });
+        ASSERT_EQ(count, CountRows(table, {
+              table->NewComparisonPredicate("value",
+                                            KuduPredicate::EQUAL,
+                                            KuduValue::FromInt(v)),
+        }));
+      }
+
+      { // value >= v
+        int count = count_if(values.begin(), values.end(),
+                             [&] (T value) { return value >= v; });
+        ASSERT_EQ(count, CountRows(table, {
+              table->NewComparisonPredicate("value",
+                                            KuduPredicate::GREATER_EQUAL,
+                                            KuduValue::FromInt(v)),
+        }));
+      }
+
+      { // value <= v
+        int count = count_if(values.begin(), values.end(),
+                             [&] (T value) { return value <= v; });
+        ASSERT_EQ(count, CountRows(table, {
+              table->NewComparisonPredicate("value",
+                                            KuduPredicate::LESS_EQUAL,
+                                            KuduValue::FromInt(v)),
+        }));
+      }
+
+      { // value >= 0
+        // value <= v
+        int count = count_if(values.begin(), values.end(),
+                             [&] (T value) { return value >= 0 && value <= v; });
+        ASSERT_EQ(count, CountRows(table, {
+              table->NewComparisonPredicate("value",
+                                            KuduPredicate::GREATER_EQUAL,
+                                            KuduValue::FromInt(0)),
+              table->NewComparisonPredicate("value",
+                                            KuduPredicate::LESS_EQUAL,
+                                            KuduValue::FromInt(v)),
+        }));
+      }
+
+      { // value >= v
+        // value <= 0
+        int count = count_if(values.begin(), values.end(),
+                             [&] (T value) { return value >= v && value <= 0; });
+        ASSERT_EQ(count, CountRows(table, {
+              table->NewComparisonPredicate("value",
+                                            KuduPredicate::GREATER_EQUAL,
+                                            KuduValue::FromInt(v)),
+              table->NewComparisonPredicate("value",
+                                            KuduPredicate::LESS_EQUAL,
+                                            KuduValue::FromInt(0)),
+        }));
+      }
+    }
+  }
+
+  // Check string predicates against the specified table.
+  void CheckStringPredicates(shared_ptr<KuduTable> table) {
+
+    vector<string> values = CreateStringValues();
+    ASSERT_EQ(values.size() + 1, CountRows(table, {}));
+
+    // Add some additional values to check against.
+    vector<string> test_values = values;
+    test_values.push_back("aa");
+    test_values.push_back(string("\1", 1));
+    test_values.push_back(string("a\1", 1));
+
+    for (const string& v : test_values) {
+      SCOPED_TRACE(strings::Substitute("test value: '$0'", strings::CHexEscape(v)));
+
+      { // value = v
+        int count = count_if(values.begin(), values.end(),
+                             [&] (const string& value) { return value == v; });
+        ASSERT_EQ(count, CountRows(table, {
+              table->NewComparisonPredicate("value",
+                                            KuduPredicate::EQUAL,
+                                            KuduValue::CopyString(v)),
+        }));
+      }
+
+      { // value >= v
+        int count = count_if(values.begin(), values.end(),
+                             [&] (const string& value) { return value >= v; });
+        ASSERT_EQ(count, CountRows(table, {
+              table->NewComparisonPredicate("value",
+                                            KuduPredicate::GREATER_EQUAL,
+                                            KuduValue::CopyString(v)),
+        }));
+      }
+
+      { // value <= v
+        int count = count_if(values.begin(), values.end(),
+                             [&] (const string& value) { return value <= v; });
+        ASSERT_EQ(count, CountRows(table, {
+              table->NewComparisonPredicate("value",
+                                            KuduPredicate::LESS_EQUAL,
+                                            KuduValue::CopyString(v)),
+        }));
+      }
+
+      { // value >= "a"
+        // value <= v
+        int count = count_if(values.begin(), values.end(),
+                             [&] (const string& value) { return value >= "a" && value <= v; });
+        ASSERT_EQ(count, CountRows(table, {
+              table->NewComparisonPredicate("value",
+                                            KuduPredicate::GREATER_EQUAL,
+                                            KuduValue::CopyString("a")),
+              table->NewComparisonPredicate("value",
+                                            KuduPredicate::LESS_EQUAL,
+                                            KuduValue::CopyString(v)),
+        }));
+      }
+
+      { // value >= v
+        // value <= "a"
+        int count = count_if(values.begin(), values.end(),
+                             [&] (const string& value) { return value >= v && value <= "a"; });
+        ASSERT_EQ(count, CountRows(table, {
+              table->NewComparisonPredicate("value",
+                                            KuduPredicate::GREATER_EQUAL,
+                                            KuduValue::CopyString(v)),
+              table->NewComparisonPredicate("value",
+                                            KuduPredicate::LESS_EQUAL,
+                                            KuduValue::CopyString("a")),
+        }));
+      }
+    }
+  }
+
+  shared_ptr<KuduClient> client_;
+  gscoped_ptr<MiniCluster> cluster_;
+};
+
+TEST_F(PredicateTest, TestBoolPredicates) {
+  shared_ptr<KuduTable> table = CreateAndOpenTable(KuduColumnSchema::BOOL);
+  shared_ptr<KuduSession> session = CreateSession();
+
+  int i = 0;
+  for (bool b : { false, true }) {
+      unique_ptr<KuduInsert> insert(table->NewInsert());
+      ASSERT_OK(insert->mutable_row()->SetInt64("key", i++));
+      ASSERT_OK(insert->mutable_row()->SetBool("value", b));
+      ASSERT_OK(session->Apply(insert.release()));
+  }
+
+  // Insert null value
+  unique_ptr<KuduInsert> insert(table->NewInsert());
+  ASSERT_OK(insert->mutable_row()->SetInt64("key", i++));
+  ASSERT_OK(session->Apply(insert.release()));
+  ASSERT_OK(session->Flush());
+
+  ASSERT_EQ(3, CountRows(table, {}));
+
+  { // value = false
+    KuduPredicate* pred = table->NewComparisonPredicate("value",
+                                                        KuduPredicate::EQUAL,
+                                                        KuduValue::FromBool(false));
+    ASSERT_EQ(1, CountRows(table, { pred }));
+  }
+
+  { // value = true
+    KuduPredicate* pred = table->NewComparisonPredicate("value",
+                                                        KuduPredicate::EQUAL,
+                                                        KuduValue::FromBool(true));
+    ASSERT_EQ(1, CountRows(table, { pred }));
+  }
+
+  { // value >= true
+    KuduPredicate* pred = table->NewComparisonPredicate("value",
+                                                        KuduPredicate::GREATER_EQUAL,
+                                                        KuduValue::FromBool(true));
+    ASSERT_EQ(1, CountRows(table, { pred }));
+  }
+
+  { // value >= false
+    KuduPredicate* pred = table->NewComparisonPredicate("value",
+                                                        KuduPredicate::GREATER_EQUAL,
+                                                        KuduValue::FromBool(false));
+    ASSERT_EQ(2, CountRows(table, { pred }));
+  }
+
+  { // value <= false
+    KuduPredicate* pred = table->NewComparisonPredicate("value",
+                                                        KuduPredicate::LESS_EQUAL,
+                                                        KuduValue::FromBool(false));
+    ASSERT_EQ(1, CountRows(table, { pred }));
+  }
+
+  { // value <= true
+    KuduPredicate* pred = table->NewComparisonPredicate("value",
+                                                        KuduPredicate::LESS_EQUAL,
+                                                        KuduValue::FromBool(true));
+    ASSERT_EQ(2, CountRows(table, { pred }));
+  }
+}
+
+TEST_F(PredicateTest, TestInt8Predicates) {
+  shared_ptr<KuduTable> table = CreateAndOpenTable(KuduColumnSchema::INT8);
+  shared_ptr<KuduSession> session = CreateSession();
+
+  int i = 0;
+  for (int8_t value : CreateIntValues<int8_t>()) {
+      unique_ptr<KuduInsert> insert(table->NewInsert());
+      ASSERT_OK(insert->mutable_row()->SetInt64("key", i++));
+      ASSERT_OK(insert->mutable_row()->SetInt8("value", value));
+      ASSERT_OK(session->Apply(insert.release()));
+  }
+  unique_ptr<KuduInsert> null_insert(table->NewInsert());
+  ASSERT_OK(null_insert->mutable_row()->SetInt64("key", i++));
+  ASSERT_OK(null_insert->mutable_row()->SetNull("value"));
+  ASSERT_OK(session->Apply(null_insert.release()));
+  ASSERT_OK(session->Flush());
+
+  CheckIntPredicates<int8_t>(table);
+}
+
+TEST_F(PredicateTest, TestInt16Predicates) {
+  shared_ptr<KuduTable> table = CreateAndOpenTable(KuduColumnSchema::INT16);
+  shared_ptr<KuduSession> session = CreateSession();
+
+  int i = 0;
+  for (int16_t value : CreateIntValues<int16_t>()) {
+      unique_ptr<KuduInsert> insert(table->NewInsert());
+      ASSERT_OK(insert->mutable_row()->SetInt64("key", i++));
+      ASSERT_OK(insert->mutable_row()->SetInt16("value", value));
+      ASSERT_OK(session->Apply(insert.release()));
+  }
+  unique_ptr<KuduInsert> null_insert(table->NewInsert());
+  ASSERT_OK(null_insert->mutable_row()->SetInt64("key", i++));
+  ASSERT_OK(null_insert->mutable_row()->SetNull("value"));
+  ASSERT_OK(session->Apply(null_insert.release()));
+  ASSERT_OK(session->Flush());
+
+  CheckIntPredicates<int16_t>(table);
+}
+
+TEST_F(PredicateTest, TestInt32Predicates) {
+  shared_ptr<KuduTable> table = CreateAndOpenTable(KuduColumnSchema::INT32);
+  shared_ptr<KuduSession> session = CreateSession();
+
+  int i = 0;
+  for (int32_t value : CreateIntValues<int32_t>()) {
+      unique_ptr<KuduInsert> insert(table->NewInsert());
+      ASSERT_OK(insert->mutable_row()->SetInt64("key", i++));
+      ASSERT_OK(insert->mutable_row()->SetInt32("value", value));
+      ASSERT_OK(session->Apply(insert.release()));
+  }
+  unique_ptr<KuduInsert> null_insert(table->NewInsert());
+  ASSERT_OK(null_insert->mutable_row()->SetInt64("key", i++));
+  ASSERT_OK(null_insert->mutable_row()->SetNull("value"));
+  ASSERT_OK(session->Apply(null_insert.release()));
+  ASSERT_OK(session->Flush());
+
+  CheckIntPredicates<int32_t>(table);
+}
+
+TEST_F(PredicateTest, TestInt64Predicates) {
+  shared_ptr<KuduTable> table = CreateAndOpenTable(KuduColumnSchema::INT64);
+  shared_ptr<KuduSession> session = CreateSession();
+
+  int i = 0;
+  for (int64_t value : CreateIntValues<int64_t>()) {
+      unique_ptr<KuduInsert> insert(table->NewInsert());
+      ASSERT_OK(insert->mutable_row()->SetInt64("key", i++));
+      ASSERT_OK(insert->mutable_row()->SetInt64("value", value));
+      ASSERT_OK(session->Apply(insert.release()));
+  }
+  unique_ptr<KuduInsert> null_insert(table->NewInsert());
+  ASSERT_OK(null_insert->mutable_row()->SetInt64("key", i++));
+  ASSERT_OK(null_insert->mutable_row()->SetNull("value"));
+  ASSERT_OK(session->Apply(null_insert.release()));
+  ASSERT_OK(session->Flush());
+
+  CheckIntPredicates<int64_t>(table);
+}
+
+TEST_F(PredicateTest, TestTimestampPredicates) {
+  shared_ptr<KuduTable> table = CreateAndOpenTable(KuduColumnSchema::TIMESTAMP);
+  shared_ptr<KuduSession> session = CreateSession();
+
+  int i = 0;
+  for (int64_t value : CreateIntValues<int64_t>()) {
+      unique_ptr<KuduInsert> insert(table->NewInsert());
+      ASSERT_OK(insert->mutable_row()->SetInt64("key", i++));
+      ASSERT_OK(insert->mutable_row()->SetTimestamp("value", value));
+      ASSERT_OK(session->Apply(insert.release()));
+  }
+  unique_ptr<KuduInsert> null_insert(table->NewInsert());
+  ASSERT_OK(null_insert->mutable_row()->SetInt64("key", i++));
+  ASSERT_OK(null_insert->mutable_row()->SetNull("value"));
+  ASSERT_OK(session->Apply(null_insert.release()));
+  ASSERT_OK(session->Flush());
+
+  CheckIntPredicates<int64_t>(table);
+}
+
+TEST_F(PredicateTest, TestFloatPredicates) {
+  shared_ptr<KuduTable> table = CreateAndOpenTable(KuduColumnSchema::FLOAT);
+  shared_ptr<KuduSession> session = CreateSession();
+
+  vector<float> values = CreateFloatingPointValues<float>();
+  vector<float> test_values = CreateFloatingPointTestValues<float>();
+
+  int i = 0;
+  for (float value : values) {
+      unique_ptr<KuduInsert> insert(table->NewInsert());
+      ASSERT_OK(insert->mutable_row()->SetInt64("key", i++));
+      ASSERT_OK(insert->mutable_row()->SetFloat("value", value));
+      ASSERT_OK(session->Apply(insert.release()));
+  }
+  unique_ptr<KuduInsert> null_insert(table->NewInsert());
+  ASSERT_OK(null_insert->mutable_row()->SetInt64("key", i++));
+  ASSERT_OK(null_insert->mutable_row()->SetNull("value"));
+  ASSERT_OK(session->Apply(null_insert.release()));
+  ASSERT_OK(session->Flush());
+
+  ASSERT_EQ(values.size() + 1, CountRows(table, {}));
+
+  for (float v : test_values) {
+    SCOPED_TRACE(strings::Substitute("test value: $0", v));
+
+    { // value = v
+      int count = count_if(values.begin(), values.end(),
+                           [&] (float value) { return value == v; });
+      ASSERT_EQ(count, CountRows(table, {
+            table->NewComparisonPredicate("value", KuduPredicate::EQUAL, KuduValue::FromFloat(v)),
+      }));
+    }
+
+    { // value >= v
+      int count = count_if(values.begin(), values.end(),
+                           [&] (float value) { return value >= v; });
+      ASSERT_EQ(count, CountRows(table, {
+            table->NewComparisonPredicate("value",
+                                          KuduPredicate::GREATER_EQUAL,
+                                          KuduValue::FromFloat(v)),
+      }));
+    }
+
+    { // value <= v
+      int count = count_if(values.begin(), values.end(),
+                           [&] (float value) { return value <= v; });
+      ASSERT_EQ(count, CountRows(table, {
+            table->NewComparisonPredicate("value",
+                                          KuduPredicate::LESS_EQUAL,
+                                          KuduValue::FromFloat(v)),
+      }));
+    }
+
+    { // value >= 0
+      // value <= v
+      int count = count_if(values.begin(), values.end(),
+                           [&] (float value) { return value >= 0.0 && value <= v; });
+      ASSERT_EQ(count, CountRows(table, {
+            table->NewComparisonPredicate("value",
+                                          KuduPredicate::GREATER_EQUAL,
+                                          KuduValue::FromFloat(0.0)),
+            table->NewComparisonPredicate("value",
+                                          KuduPredicate::LESS_EQUAL,
+                                          KuduValue::FromFloat(v)),
+      }));
+    }
+
+    { // value >= v
+      // value <= 0.0
+      int count = count_if(values.begin(), values.end(),
+                           [&] (float value) { return value >= v && value <= 0.0; });
+      ASSERT_EQ(count, CountRows(table, {
+            table->NewComparisonPredicate("value",
+                                          KuduPredicate::GREATER_EQUAL,
+                                          KuduValue::FromFloat(v)),
+            table->NewComparisonPredicate("value",
+                                          KuduPredicate::LESS_EQUAL,
+                                          KuduValue::FromFloat(0.0)),
+      }));
+    }
+  }
+}
+
+TEST_F(PredicateTest, TestDoublePredicates) {
+  shared_ptr<KuduTable> table = CreateAndOpenTable(KuduColumnSchema::DOUBLE);
+  shared_ptr<KuduSession> session = CreateSession();
+
+  vector<double> values = CreateFloatingPointValues<double>();
+  vector<double> test_values = CreateFloatingPointTestValues<double>();
+
+  int i = 0;
+  for (double value : values) {
+      unique_ptr<KuduInsert> insert(table->NewInsert());
+      ASSERT_OK(insert->mutable_row()->SetInt64("key", i++));
+      ASSERT_OK(insert->mutable_row()->SetDouble("value", value));
+      ASSERT_OK(session->Apply(insert.release()));
+  }
+  unique_ptr<KuduInsert> null_insert(table->NewInsert());
+  ASSERT_OK(null_insert->mutable_row()->SetInt64("key", i++));
+  ASSERT_OK(null_insert->mutable_row()->SetNull("value"));
+  ASSERT_OK(session->Apply(null_insert.release()));
+  ASSERT_OK(session->Flush());
+
+  ASSERT_EQ(values.size() + 1, CountRows(table, {}));
+
+  for (double v : test_values) {
+    SCOPED_TRACE(strings::Substitute("test value: $0", v));
+
+    { // value = v
+      int count = count_if(values.begin(), values.end(),
+                           [&] (double value) { return value == v; });
+      ASSERT_EQ(count, CountRows(table, {
+            table->NewComparisonPredicate("value", KuduPredicate::EQUAL, KuduValue::FromDouble(v)),
+      }));
+    }
+
+    { // value >= v
+      int count = count_if(values.begin(), values.end(),
+                           [&] (double value) { return value >= v; });
+      ASSERT_EQ(count, CountRows(table, {
+            table->NewComparisonPredicate("value",
+                                          KuduPredicate::GREATER_EQUAL,
+                                          KuduValue::FromDouble(v)),
+      }));
+    }
+
+    { // value <= v
+      int count = count_if(values.begin(), values.end(),
+                           [&] (double value) { return value <= v; });
+      ASSERT_EQ(count, CountRows(table, {
+            table->NewComparisonPredicate("value",
+                                          KuduPredicate::LESS_EQUAL,
+                                          KuduValue::FromDouble(v)),
+      }));
+    }
+
+    { // value >= 0.0
+      // value <= v
+      int count = count_if(values.begin(), values.end(),
+                           [&] (double value) { return value >= 0.0 && value <= v; });
+      ASSERT_EQ(count, CountRows(table, {
+            table->NewComparisonPredicate("value",
+                                          KuduPredicate::GREATER_EQUAL,
+                                          KuduValue::FromDouble(0.0)),
+            table->NewComparisonPredicate("value",
+                                          KuduPredicate::LESS_EQUAL,
+                                          KuduValue::FromDouble(v)),
+      }));
+    }
+
+    { // value >= v
+      // value <= 0.0
+      int count = count_if(values.begin(), values.end(),
+                           [&] (double value) { return value >= v && value <= 0.0; });
+      ASSERT_EQ(count, CountRows(table, {
+            table->NewComparisonPredicate("value",
+                                          KuduPredicate::GREATER_EQUAL,
+                                          KuduValue::FromDouble(v)),
+            table->NewComparisonPredicate("value",
+                                          KuduPredicate::LESS_EQUAL,
+                                          KuduValue::FromDouble(0.0)),
+      }));
+    }
+  }
+}
+
+TEST_F(PredicateTest, TestStringPredicates) {
+  shared_ptr<KuduTable> table = CreateAndOpenTable(KuduColumnSchema::STRING);
+  shared_ptr<KuduSession> session = CreateSession();
+
+  vector<string> values = CreateStringValues();
+  int i = 0;
+  for (const string& value : values) {
+      unique_ptr<KuduInsert> insert(table->NewInsert());
+      ASSERT_OK(insert->mutable_row()->SetInt64("key", i++));
+      ASSERT_OK(insert->mutable_row()->SetString("value", value));
+      ASSERT_OK(session->Apply(insert.release()));
+  }
+  unique_ptr<KuduInsert> null_insert(table->NewInsert());
+  ASSERT_OK(null_insert->mutable_row()->SetInt64("key", i++));
+  ASSERT_OK(null_insert->mutable_row()->SetNull("value"));
+  ASSERT_OK(session->Apply(null_insert.release()));
+  ASSERT_OK(session->Flush());
+
+  CheckStringPredicates(table);
+}
+
+TEST_F(PredicateTest, TestBinaryPredicates) {
+  shared_ptr<KuduTable> table = CreateAndOpenTable(KuduColumnSchema::BINARY);
+  shared_ptr<KuduSession> session = CreateSession();
+
+  vector<string> values = CreateStringValues();
+  int i = 0;
+  for (const string& value : values) {
+      unique_ptr<KuduInsert> insert(table->NewInsert());
+      ASSERT_OK(insert->mutable_row()->SetInt64("key", i++));
+      ASSERT_OK(insert->mutable_row()->SetBinary("value", value));
+      ASSERT_OK(session->Apply(insert.release()));
+  }
+  unique_ptr<KuduInsert> null_insert(table->NewInsert());
+  ASSERT_OK(null_insert->mutable_row()->SetInt64("key", i++));
+  ASSERT_OK(null_insert->mutable_row()->SetNull("value"));
+  ASSERT_OK(session->Apply(null_insert.release()));
+  ASSERT_OK(session->Flush());
+
+  CheckStringPredicates(table);
+}
+
+} // namespace client
+} // namespace kudu