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:03 UTC

[1/7] incubator-kudu git commit: Add internal IS NOT NULL predicate type

Repository: incubator-kudu
Updated Branches:
  refs/heads/master d5938e951 -> 626c3a839


Add internal IS NOT NULL predicate type

This commit adds an internal IS NOT NULL predicate type, and changes LESS_EQUAL
predicate simplification to result in the IS NOT NULL predicate type when the
conversion to a LESS predicate fails and the column is nullable. This fixes
scans with predicates such as 'WHERE my_nullable_int8_col <= 127`. A followup
commit will contain comprehensive predicate tests covering this case.

Change-Id: Ifcf29b1f274df2ef5c5ac7a7a17cc06dfd59e191
Reviewed-on: http://gerrit.cloudera.org:8080/2671
Reviewed-by: Todd Lipcon <to...@apache.org>
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/9b8b0202
Tree: http://git-wip-us.apache.org/repos/asf/incubator-kudu/tree/9b8b0202
Diff: http://git-wip-us.apache.org/repos/asf/incubator-kudu/diff/9b8b0202

Branch: refs/heads/master
Commit: 9b8b020210905b0cc4dd81e0ca905e098d0cad33
Parents: d5938e9
Author: Dan Burkert <da...@cloudera.com>
Authored: Wed Mar 30 15:29:10 2016 -0700
Committer: Dan Burkert <da...@cloudera.com>
Committed: Thu Mar 31 03:49:45 2016 +0000

----------------------------------------------------------------------
 src/kudu/client/scan_predicate.cc        |  2 +-
 src/kudu/client/scanner-internal.cc      |  4 ++
 src/kudu/common/column_predicate-test.cc | 61 +++++++++++++++++++++++++--
 src/kudu/common/column_predicate.cc      | 48 +++++++++++++++++++--
 src/kudu/common/column_predicate.h       |  6 +++
 src/kudu/common/common.proto             |  3 ++
 src/kudu/tserver/tablet_service.cc       |  4 ++
 7 files changed, 120 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/9b8b0202/src/kudu/client/scan_predicate.cc
----------------------------------------------------------------------
diff --git a/src/kudu/client/scan_predicate.cc b/src/kudu/client/scan_predicate.cc
index 1520a0b..fc44ba7 100644
--- a/src/kudu/client/scan_predicate.cc
+++ b/src/kudu/client/scan_predicate.cc
@@ -60,10 +60,10 @@ ComparisonPredicateData::ComparisonPredicateData(ColumnSchema col,
       op_(op),
       val_(val) {
 }
+
 ComparisonPredicateData::~ComparisonPredicateData() {
 }
 
-
 Status ComparisonPredicateData::AddToScanSpec(ScanSpec* spec, Arena* arena) {
   void* val_void;
   RETURN_NOT_OK(val_->data_->CheckTypeAndGetPointer(col_.name(),

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/9b8b0202/src/kudu/client/scanner-internal.cc
----------------------------------------------------------------------
diff --git a/src/kudu/client/scanner-internal.cc b/src/kudu/client/scanner-internal.cc
index 568f381..3a3fc11 100644
--- a/src/kudu/client/scanner-internal.cc
+++ b/src/kudu/client/scanner-internal.cc
@@ -122,6 +122,10 @@ void ColumnPredicateIntoPB(const ColumnPredicate& predicate,
       }
       return;
     };
+    case PredicateType::IsNotNull: {
+      pb->mutable_is_not_null();
+      return;
+    };
     case PredicateType::None: LOG(FATAL) << "None predicate may not be converted to protobuf";
   }
   LOG(FATAL) << "unknown predicate type";

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/9b8b0202/src/kudu/common/column_predicate-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/common/column_predicate-test.cc b/src/kudu/common/column_predicate-test.cc
index 274a774..9585a24 100644
--- a/src/kudu/common/column_predicate-test.cc
+++ b/src/kudu/common/column_predicate-test.cc
@@ -188,20 +188,58 @@ class TestColumnPredicate : public KuduTest {
               ColumnPredicate::None(column),
               ColumnPredicate::None(column),
               PredicateType::None);
+
+    // IS NOT NULL
+
+    // IS NOT NULL AND
+    // IS NOT NULL
+    // =
+    // IS NOT NULL
+    TestMerge(ColumnPredicate::IsNotNull(column),
+              ColumnPredicate::IsNotNull(column),
+              ColumnPredicate::IsNotNull(column),
+              PredicateType::IsNotNull);
+
+    // IS NOT NULL AND
+    // None
+    // =
+    // None
+    TestMerge(ColumnPredicate::IsNotNull(column),
+              ColumnPredicate::None(column),
+              ColumnPredicate::None(column),
+              PredicateType::None);
+
+    // IS NOT NULL AND
+    // |
+    // =
+    // |
+    TestMerge(ColumnPredicate::IsNotNull(column),
+              ColumnPredicate::Equality(column, &values[0]),
+              ColumnPredicate::Equality(column, &values[0]),
+              PredicateType::Equality);
+
+    // IS NOT NULL AND
+    // [------)
+    // =
+    // [------)
+    TestMerge(ColumnPredicate::IsNotNull(column),
+              ColumnPredicate::Range(column, &values[0], &values[2]),
+              ColumnPredicate::Range(column, &values[0], &values[2]),
+              PredicateType::Range);
   }
 };
 
 TEST_F(TestColumnPredicate, TestMerge) {
-  TestMergeCombinations(ColumnSchema("c", INT8),
+  TestMergeCombinations(ColumnSchema("c", INT8, true),
                         vector<int8_t> { 0, 1, 2, 3, 4, 5, 6 });
 
-  TestMergeCombinations(ColumnSchema("c", INT32),
+  TestMergeCombinations(ColumnSchema("c", INT32, true),
                         vector<int32_t> { -100, -10, -1, 0, 1, 10, 100 });
 
-  TestMergeCombinations(ColumnSchema("c", STRING),
+  TestMergeCombinations(ColumnSchema("c", STRING, true),
                         vector<Slice> { "a", "b", "c", "d", "e", "f", "g" });
 
-  TestMergeCombinations(ColumnSchema("c", BINARY),
+  TestMergeCombinations(ColumnSchema("c", BINARY, true),
                         vector<Slice> { Slice("", 0),
                                         Slice("\0", 1),
                                         Slice("\0\0", 2),
@@ -265,6 +303,21 @@ TEST_F(TestColumnPredicate, TestInclusiveRange) {
     ASSERT_EQ(boost::none, ColumnPredicate::InclusiveRange(column, nullptr, &max, &arena));
   }
   {
+    ColumnSchema column("c", INT32, true);
+    int32_t zero = 0;
+    int32_t two = 2;
+    int32_t three = 3;
+    int32_t max = INT32_MAX;
+
+    ASSERT_EQ(ColumnPredicate::Range(column, &zero, &three),
+              ColumnPredicate::InclusiveRange(column, &zero, &two, &arena));
+    ASSERT_EQ(ColumnPredicate::Range(column, &zero, nullptr),
+              ColumnPredicate::InclusiveRange(column, &zero, &max, &arena));
+
+    ASSERT_EQ(ColumnPredicate::IsNotNull(column),
+              ColumnPredicate::InclusiveRange(column, nullptr, &max, &arena));
+  }
+  {
     ColumnSchema column("c", STRING);
     Slice zero("", 0);
     Slice two("\0\0", 2);

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/9b8b0202/src/kudu/common/column_predicate.cc
----------------------------------------------------------------------
diff --git a/src/kudu/common/column_predicate.cc b/src/kudu/common/column_predicate.cc
index d1c8c67..e7aef58 100644
--- a/src/kudu/common/column_predicate.cc
+++ b/src/kudu/common/column_predicate.cc
@@ -67,7 +67,14 @@ boost::optional<ColumnPredicate> ColumnPredicate::InclusiveRange(ColumnSchema co
     memcpy(buf, upper, size);
     if (!key_util::IncrementCell(column, buf, arena)) {
       if (lower == nullptr) {
-        return boost::none;
+        if (column.is_nullable()) {
+          // If incrementing the upper bound fails and the column is nullable,
+          // then return an IS NOT NULL predicate, so that null values will be
+          // filtered.
+          return ColumnPredicate::IsNotNull(move(column));
+        } else {
+          return boost::none;
+        }
       } else {
         upper = nullptr;
       }
@@ -78,6 +85,11 @@ boost::optional<ColumnPredicate> ColumnPredicate::InclusiveRange(ColumnSchema co
   return ColumnPredicate::Range(move(column), lower, upper);
 }
 
+ColumnPredicate ColumnPredicate::IsNotNull(ColumnSchema column) {
+  CHECK(column.is_nullable());
+  return ColumnPredicate(PredicateType::IsNotNull, move(column), nullptr, nullptr);
+}
+
 ColumnPredicate ColumnPredicate::None(ColumnSchema column) {
   return ColumnPredicate(PredicateType::None, move(column), nullptr, nullptr);
 }
@@ -90,8 +102,9 @@ void ColumnPredicate::SetToNone() {
 
 void ColumnPredicate::Simplify() {
   switch (predicate_type_) {
-    case PredicateType::None: return;
-    case PredicateType::Equality: return;
+    case PredicateType::None:
+    case PredicateType::Equality:
+    case PredicateType::IsNotNull: return;
     case PredicateType::Range: {
       if (lower_ != nullptr && upper_ != nullptr) {
         if (column_.type_info()->Compare(lower_, upper_) >= 0) {
@@ -121,6 +134,18 @@ void ColumnPredicate::Merge(const ColumnPredicate& other) {
       MergeIntoEquality(other);
       return;
     };
+    case PredicateType::IsNotNull: {
+      // 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.
+      predicate_type_ = other.predicate_type_;
+      lower_ = other.lower_;
+      upper_ = other.upper_;
+      return;
+    };
   }
   LOG(FATAL) << "unknown predicate type";
 }
@@ -163,6 +188,7 @@ void ColumnPredicate::MergeIntoRange(const ColumnPredicate& other) {
       }
       return;
     };
+    case PredicateType::IsNotNull: return;
   }
   LOG(FATAL) << "unknown predicate type";
 }
@@ -189,6 +215,7 @@ void ColumnPredicate::MergeIntoEquality(const ColumnPredicate& other) {
       }
       return;
     };
+    case PredicateType::IsNotNull: return;
   }
   LOG(FATAL) << "unknown predicate type";
 }
@@ -260,6 +287,17 @@ void ColumnPredicate::Evaluate(const ColumnBlock& block, SelectionVector *sel) c
         });
         return;
     };
+    case PredicateType::IsNotNull: {
+      if (!block.is_nullable()) return;
+      // TODO: make this more efficient by using bitwise operations on the
+      // null and selection vectors.
+      for (size_t i = 0; i < block.nrows(); i++) {
+        if (sel->IsRowSelected(i) && block.is_null(i)) {
+          BitmapClear(sel->mutable_bitmap(), i);
+        }
+      }
+      return;
+    }
   }
   LOG(FATAL) << "unknown predicate type";
 }
@@ -282,6 +320,9 @@ string ColumnPredicate::ToString() const {
     case PredicateType::Equality: {
       return strings::Substitute("`$0` = $1", column_.name(), column_.Stringify(lower_));
     };
+    case PredicateType::IsNotNull: {
+      return strings::Substitute("`$0` IS NOT NULL", column_.name());
+    };
   }
   LOG(FATAL) << "unknown predicate type";
 }
@@ -310,6 +351,7 @@ int SelectivityRank(const ColumnPredicate& predicate) {
     case PredicateType::None: return 0;
     case PredicateType::Equality: return 1;
     case PredicateType::Range: return 2;
+    case PredicateType::IsNotNull: return 3;
   }
   LOG(FATAL) << "unknown predicate type";
 }

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/9b8b0202/src/kudu/common/column_predicate.h
----------------------------------------------------------------------
diff --git a/src/kudu/common/column_predicate.h b/src/kudu/common/column_predicate.h
index c90d841..e1461e1 100644
--- a/src/kudu/common/column_predicate.h
+++ b/src/kudu/common/column_predicate.h
@@ -41,6 +41,9 @@ enum class PredicateType {
   // A predicate which evaluates to true if the column value falls within a
   // range.
   Range,
+
+  // A predicate which evaluates to true if the value is not null.
+  IsNotNull,
 };
 
 // A predicate which can be evaluated over a block of column values.
@@ -90,6 +93,9 @@ class ColumnPredicate {
                                                          const void* upper,
                                                          Arena* arena);
 
+  // Creates a new IS NOT NULL predicate for the column.
+  static ColumnPredicate IsNotNull(ColumnSchema column);
+
   // Returns the type of this predicate.
   PredicateType predicate_type() const {
     return predicate_type_;

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/9b8b0202/src/kudu/common/common.proto
----------------------------------------------------------------------
diff --git a/src/kudu/common/common.proto b/src/kudu/common/common.proto
index a04f9b4..a3505bb 100644
--- a/src/kudu/common/common.proto
+++ b/src/kudu/common/common.proto
@@ -299,8 +299,11 @@ message ColumnPredicatePB {
     optional bytes value = 1;
   }
 
+  message IsNotNull {}
+
   oneof predicate {
     Range range = 2;
     Equality equality = 3;
+    IsNotNull is_not_null = 4;
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/9b8b0202/src/kudu/tserver/tablet_service.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tserver/tablet_service.cc b/src/kudu/tserver/tablet_service.cc
index 044b59f..1082e38 100644
--- a/src/kudu/tserver/tablet_service.cc
+++ b/src/kudu/tserver/tablet_service.cc
@@ -1310,6 +1310,10 @@ static Status SetupScanSpec(const NewScanRequestPB& scan_pb,
         ret->AddPredicate(ColumnPredicate::Equality(col, value));
         break;
       };
+      case ColumnPredicatePB::kIsNotNull: {
+        ret->AddPredicate(ColumnPredicate::IsNotNull(col));
+        break;
+      };
       default: return Status::InvalidArgument("Unknown predicate type for column", col.name());
     }
   }


[3/7] incubator-kudu git commit: Bump to 0.9.0-SNAPSHOT

Posted by ad...@apache.org.
Bump to 0.9.0-SNAPSHOT

Change-Id: Iba036a6b988340a63438f550d731b3017dd4ddc1
Reviewed-on: http://gerrit.cloudera.org:8080/2668
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/7e2fe2a0
Tree: http://git-wip-us.apache.org/repos/asf/incubator-kudu/tree/7e2fe2a0
Diff: http://git-wip-us.apache.org/repos/asf/incubator-kudu/diff/7e2fe2a0

Branch: refs/heads/master
Commit: 7e2fe2a04ffab42b9e3915e87819e14d6d8a15d8
Parents: 8f4726d
Author: Jean-Daniel Cryans <jd...@apache.org>
Authored: Wed Mar 30 14:44:02 2016 -0700
Committer: Jean-Daniel Cryans <jd...@gerrit.cloudera.org>
Committed: Thu Mar 31 17:14:26 2016 +0000

----------------------------------------------------------------------
 java/interface-annotations/pom.xml | 2 +-
 java/kudu-client-tools/pom.xml     | 2 +-
 java/kudu-client/pom.xml           | 2 +-
 java/kudu-csd/pom.xml              | 4 ++--
 java/kudu-flume-sink/pom.xml       | 2 +-
 java/kudu-mapreduce/pom.xml        | 2 +-
 java/kudu-spark/pom.xml            | 2 +-
 java/pom.xml                       | 2 +-
 version.txt                        | 2 +-
 9 files changed, 10 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/7e2fe2a0/java/interface-annotations/pom.xml
----------------------------------------------------------------------
diff --git a/java/interface-annotations/pom.xml b/java/interface-annotations/pom.xml
index db0e8e7..c821363 100644
--- a/java/interface-annotations/pom.xml
+++ b/java/interface-annotations/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <groupId>org.kududb</groupId>
     <artifactId>kudu-parent</artifactId>
-    <version>0.8.0-SNAPSHOT</version>
+    <version>0.9.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>interface-annotations</artifactId>

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/7e2fe2a0/java/kudu-client-tools/pom.xml
----------------------------------------------------------------------
diff --git a/java/kudu-client-tools/pom.xml b/java/kudu-client-tools/pom.xml
index 00a4d87..87b8b20 100644
--- a/java/kudu-client-tools/pom.xml
+++ b/java/kudu-client-tools/pom.xml
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.kududb</groupId>
         <artifactId>kudu-parent</artifactId>
-        <version>0.8.0-SNAPSHOT</version>
+        <version>0.9.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>kudu-client-tools</artifactId>

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/7e2fe2a0/java/kudu-client/pom.xml
----------------------------------------------------------------------
diff --git a/java/kudu-client/pom.xml b/java/kudu-client/pom.xml
index 112b441..0ad1c47 100644
--- a/java/kudu-client/pom.xml
+++ b/java/kudu-client/pom.xml
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.kududb</groupId>
         <artifactId>kudu-parent</artifactId>
-        <version>0.8.0-SNAPSHOT</version>
+        <version>0.9.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>kudu-client</artifactId>

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/7e2fe2a0/java/kudu-csd/pom.xml
----------------------------------------------------------------------
diff --git a/java/kudu-csd/pom.xml b/java/kudu-csd/pom.xml
index 089e468..960131f 100644
--- a/java/kudu-csd/pom.xml
+++ b/java/kudu-csd/pom.xml
@@ -19,12 +19,12 @@ limitations under the License.
   <parent>
     <groupId>org.kududb</groupId>
     <artifactId>kudu-parent</artifactId>
-    <version>0.8.0-SNAPSHOT</version>
+    <version>0.9.0-SNAPSHOT</version>
   </parent>
 
   <groupId>com.cloudera.csd</groupId>
   <artifactId>KUDU</artifactId>
-  <version>0.8.0</version>
+  <version>0.9.0</version>
   <name>The Kudu CSD</name>
   <packaging>pom</packaging>
 

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/7e2fe2a0/java/kudu-flume-sink/pom.xml
----------------------------------------------------------------------
diff --git a/java/kudu-flume-sink/pom.xml b/java/kudu-flume-sink/pom.xml
index e7d796b..092b135 100644
--- a/java/kudu-flume-sink/pom.xml
+++ b/java/kudu-flume-sink/pom.xml
@@ -15,7 +15,7 @@
   <parent>
     <artifactId>kudu-parent</artifactId>
     <groupId>org.kududb</groupId>
-    <version>0.8.0-SNAPSHOT</version>
+    <version>0.9.0-SNAPSHOT</version>
   </parent>
   <artifactId>kudu-flume-sink</artifactId>
   <name>Kudu Flume NG Sink</name>

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/7e2fe2a0/java/kudu-mapreduce/pom.xml
----------------------------------------------------------------------
diff --git a/java/kudu-mapreduce/pom.xml b/java/kudu-mapreduce/pom.xml
index 4f2535c..3b90fa0 100644
--- a/java/kudu-mapreduce/pom.xml
+++ b/java/kudu-mapreduce/pom.xml
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.kududb</groupId>
         <artifactId>kudu-parent</artifactId>
-        <version>0.8.0-SNAPSHOT</version>
+        <version>0.9.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>kudu-mapreduce</artifactId>

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/7e2fe2a0/java/kudu-spark/pom.xml
----------------------------------------------------------------------
diff --git a/java/kudu-spark/pom.xml b/java/kudu-spark/pom.xml
index cc33f40..590b45d 100644
--- a/java/kudu-spark/pom.xml
+++ b/java/kudu-spark/pom.xml
@@ -17,7 +17,7 @@
     <parent>
         <groupId>org.kududb</groupId>
         <artifactId>kudu-parent</artifactId>
-        <version>0.8.0-SNAPSHOT</version>
+        <version>0.9.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>kudu-spark</artifactId>

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/7e2fe2a0/java/pom.xml
----------------------------------------------------------------------
diff --git a/java/pom.xml b/java/pom.xml
index 987764c..2a8fc7a 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -24,7 +24,7 @@
 
     <groupId>org.kududb</groupId>
     <artifactId>kudu-parent</artifactId>
-    <version>0.8.0-SNAPSHOT</version>
+    <version>0.9.0-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <name>Kudu</name>

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/7e2fe2a0/version.txt
----------------------------------------------------------------------
diff --git a/version.txt b/version.txt
index 35dae96..5ea35de 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-0.8.0-SNAPSHOT
+0.9.0-SNAPSHOT


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

Posted by ad...@apache.org.
[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


[2/7] incubator-kudu git commit: Change row projector schema equality check to use physical column type

Posted by ad...@apache.org.
Change row projector schema equality check to use physical column type

The row projector uses the column's physical type (e.g. for TIMESTAMP the
physical type is INT64) to build codegen cache keys, but used the normal column
type when comparing the projection schemas retrieved from the cache. This
resulted in spurious debug check failures.

Change-Id: I5e9a8d9c003be3942d3ce6f93d880adb45d8ded6
Reviewed-on: http://gerrit.cloudera.org:8080/2672
Reviewed-by: Todd Lipcon <to...@apache.org>
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/8f4726d8
Tree: http://git-wip-us.apache.org/repos/asf/incubator-kudu/tree/8f4726d8
Diff: http://git-wip-us.apache.org/repos/asf/incubator-kudu/diff/8f4726d8

Branch: refs/heads/master
Commit: 8f4726d83e6b6b7d76c59d06fde9231f0bb113b9
Parents: 9b8b020
Author: Dan Burkert <da...@cloudera.com>
Authored: Wed Mar 30 15:32:10 2016 -0700
Committer: Dan Burkert <da...@cloudera.com>
Committed: Thu Mar 31 03:49:51 2016 +0000

----------------------------------------------------------------------
 src/kudu/codegen/row_projector.cc | 4 ++--
 src/kudu/common/schema.h          | 7 ++++++-
 2 files changed, 8 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/8f4726d8/src/kudu/codegen/row_projector.cc
----------------------------------------------------------------------
diff --git a/src/kudu/codegen/row_projector.cc b/src/kudu/codegen/row_projector.cc
index dd16285..d95d05a 100644
--- a/src/kudu/codegen/row_projector.cc
+++ b/src/kudu/codegen/row_projector.cc
@@ -380,7 +380,7 @@ struct DefaultEquals {
 
 struct ColumnSchemaEqualsType {
   bool operator()(const ColumnSchema& s1, const ColumnSchema& s2) {
-    return s1.EqualsType(s2);
+    return s1.EqualsPhysicalType(s2);
   }
 };
 
@@ -418,7 +418,7 @@ bool ContainerEquals(const T& t1, const T& t2) {
 // the actual dependency on column identification - which is the effect
 // that those attributes have on the RowProjector's mapping (i.e., different
 // names and IDs are ok, so long as the mapping is the same). Note that
-// key columns are not given any special meaning in projection. Types
+// key columns are not given any special meaning in projection. Physical types
 // and nullability of columns must be exactly equal between the two
 // schema pairs.
 //

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/8f4726d8/src/kudu/common/schema.h
----------------------------------------------------------------------
diff --git a/src/kudu/common/schema.h b/src/kudu/common/schema.h
index 0c16c94..bebaff5 100644
--- a/src/kudu/common/schema.h
+++ b/src/kudu/common/schema.h
@@ -195,6 +195,11 @@ class ColumnSchema {
     return NULL;
   }
 
+  bool EqualsPhysicalType(const ColumnSchema& other) const {
+    return is_nullable_ == other.is_nullable_ &&
+           type_info()->physical_type() == other.type_info()->physical_type();
+  }
+
   bool EqualsType(const ColumnSchema &other) const {
     return is_nullable_ == other.is_nullable_ &&
            type_info()->type() == other.type_info()->type();
@@ -204,7 +209,7 @@ class ColumnSchema {
     if (!EqualsType(other) || this->name_ != other.name_)
       return false;
 
-    // For Key comparison checking the defauls doesn't make sense,
+    // For Key comparison checking the defaults doesn't make sense,
     // since we don't support them, for server vs user schema this comparison
     // will always fail, since the user does not specify the defaults.
     if (check_defaults) {


[5/7] incubator-kudu git commit: Fix OSX build breakage with clang versions newer than 700

Posted by ad...@apache.org.
Fix OSX build breakage with clang versions newer than 700

Change-Id: I1c54ae7a7e2fe12b4d9d305f4dcd5343ffa6ba86
Reviewed-on: http://gerrit.cloudera.org:8080/2692
Reviewed-by: Dan Burkert <da...@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/5eb9fcf9
Tree: http://git-wip-us.apache.org/repos/asf/incubator-kudu/tree/5eb9fcf9
Diff: http://git-wip-us.apache.org/repos/asf/incubator-kudu/diff/5eb9fcf9

Branch: refs/heads/master
Commit: 5eb9fcf9ecc646c36c95c1fa041ee485c74824c8
Parents: 959952a
Author: Misty Stanley-Jones <ms...@cloudera.com>
Authored: Thu Mar 31 16:50:12 2016 -0700
Committer: Dan Burkert <da...@cloudera.com>
Committed: Fri Apr 1 01:19:31 2016 +0000

----------------------------------------------------------------------
 cmake_modules/CompilerInfo.cmake | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/5eb9fcf9/cmake_modules/CompilerInfo.cmake
----------------------------------------------------------------------
diff --git a/cmake_modules/CompilerInfo.cmake b/cmake_modules/CompilerInfo.cmake
index 66c0d7b..d77080c 100644
--- a/cmake_modules/CompilerInfo.cmake
+++ b/cmake_modules/CompilerInfo.cmake
@@ -33,7 +33,7 @@ elseif("${COMPILER_VERSION_FULL}" MATCHES ".*based on LLVM.*")
     COMPILER_VERSION "${COMPILER_VERSION_FULL}")
 
 # clang on Mac OS X, XCode 7.
-elseif("${COMPILER_VERSION_FULL}" MATCHES ".*clang-700\\..*")
+elseif("${COMPILER_VERSION_FULL}" MATCHES ".*clang-7")
   set(COMPILER_FAMILY "clang")
   set(COMPILER_VERSION "3.7.0svn")
 


[7/7] incubator-kudu git commit: KUDU-495 (part 1): new sys_catalog writing API

Posted by ad...@apache.org.
KUDU-495 (part 1): new sys_catalog writing API

The new API allows any arbitrary combination of table or tablet
creation/deletion. With some additional changes, we won't need to "roll
forward" any persistent state following a master failover. There are no
functional changes to the master here; just API updates.

I was hoping to use designated initializer lists to set up WriteActions, but
apparently it's a C-only feature [1].

1. http://stackoverflow.com/q/18731707

Change-Id: I7f5e5ecbff33435c964ced2e421566c41c387e9e
Reviewed-on: http://gerrit.cloudera.org:8080/2648
Tested-by: Kudu Jenkins
Reviewed-by: David Ribeiro Alves <da...@cloudera.com>


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

Branch: refs/heads/master
Commit: 626c3a83988efe2de4dcdaccafe71742a37af9c6
Parents: 74c6ae9
Author: Adar Dembo <ad...@cloudera.com>
Authored: Mon Mar 28 15:16:53 2016 -0700
Committer: Adar Dembo <ad...@cloudera.com>
Committed: Fri Apr 1 03:12:59 2016 +0000

----------------------------------------------------------------------
 src/kudu/master/catalog_manager.cc  |  34 ++++++--
 src/kudu/master/sys_catalog-test.cc |  29 +++++--
 src/kudu/master/sys_catalog.cc      | 145 +++++++++++++------------------
 src/kudu/master/sys_catalog.h       |  38 ++++----
 4 files changed, 132 insertions(+), 114 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/626c3a83/src/kudu/master/catalog_manager.cc
----------------------------------------------------------------------
diff --git a/src/kudu/master/catalog_manager.cc b/src/kudu/master/catalog_manager.cc
index 6165ded..0322d7d 100644
--- a/src/kudu/master/catalog_manager.cc
+++ b/src/kudu/master/catalog_manager.cc
@@ -830,7 +830,9 @@ Status CatalogManager::CreateTable(const CreateTableRequestPB* orig_req,
   }
 
   // e. Write Tablets to sys-tablets (in "preparing" state)
-  s = sys_catalog_->AddTablets(tablets);
+  SysCatalogTable::Actions tablet_actions;
+  tablet_actions.tablets_to_add = tablets;
+  s = sys_catalog_->Write(tablet_actions);
   if (!s.ok()) {
     s = s.CloneAndPrepend(Substitute("An error occurred while inserting to sys-tablets: $0",
                                      s.ToString()));
@@ -843,7 +845,9 @@ Status CatalogManager::CreateTable(const CreateTableRequestPB* orig_req,
 
   // f. Update the on-disk table state to "running".
   table->mutable_metadata()->mutable_dirty()->pb.set_state(SysTablesEntryPB::RUNNING);
-  s = sys_catalog_->AddTable(table.get());
+  SysCatalogTable::Actions table_actions;
+  table_actions.table_to_add = table.get();
+  s = sys_catalog_->Write(table_actions);
   if (!s.ok()) {
     s = s.CloneAndPrepend(Substitute("An error occurred while inserting to sys-tablets: $0",
                                      s.ToString()));
@@ -975,7 +979,9 @@ Status CatalogManager::DeleteTable(const DeleteTableRequestPB* req,
                               Substitute("Deleted at $0", LocalTimeAsString()));
 
   // 3. Update sys-catalog with the removed table state.
-  Status s = sys_catalog_->UpdateTable(table.get());
+  SysCatalogTable::Actions actions;
+  actions.table_to_update = table.get();
+  Status s = sys_catalog_->Write(actions);
   if (!s.ok()) {
     // The mutation will be aborted when 'l' exits the scope on early return.
     s = s.CloneAndPrepend(Substitute("An error occurred while updating sys tables: $0",
@@ -1182,7 +1188,9 @@ Status CatalogManager::AlterTable(const AlterTableRequestPB* req,
 
   // 5. Update sys-catalog with the new table schema.
   TRACE("Updating metadata on disk");
-  Status s = sys_catalog_->UpdateTable(table.get());
+  SysCatalogTable::Actions actions;
+  actions.table_to_update = table.get();
+  Status s = sys_catalog_->Write(actions);
   if (!s.ok()) {
     s = s.CloneAndPrepend(
         Substitute("An error occurred while updating sys-catalog tables entry: $0",
@@ -1597,7 +1605,9 @@ Status CatalogManager::HandleReportedTablet(TSDescriptor* ts_desc,
   table_lock.Unlock();
   // We update the tablets each time that someone reports it.
   // This shouldn't be very frequent and should only happen when something in fact changed.
-  Status s = sys_catalog_->UpdateTablets({ tablet.get() });
+  SysCatalogTable::Actions actions;
+  actions.tablets_to_update.push_back(tablet.get());
+  Status s = sys_catalog_->Write(actions);
   if (!s.ok()) {
     LOG(WARNING) << "Error updating tablets: " << s.ToString() << ". Tablet report was: "
                  << report.ShortDebugString();
@@ -2450,7 +2460,9 @@ void CatalogManager::DeleteTabletsAndSendRequests(const scoped_refptr<TableInfo>
 
     TabletMetadataLock tablet_lock(tablet.get(), TabletMetadataLock::WRITE);
     tablet_lock.mutable_data()->set_state(SysTabletsEntryPB::DELETED, deletion_msg);
-    CHECK_OK(sys_catalog_->UpdateTablets({ tablet.get() }));
+    SysCatalogTable::Actions actions;
+    actions.tablets_to_update.push_back(tablet.get());
+    CHECK_OK(sys_catalog_->Write(actions));
     tablet_lock.Commit();
   }
 }
@@ -2624,7 +2636,9 @@ Status CatalogManager::HandleTabletSchemaVersionReport(TabletInfo *tablet, uint3
   l.mutable_data()->set_state(SysTablesEntryPB::RUNNING,
                               Substitute("Current schema version=$0", current_version));
 
-  Status s = sys_catalog_->UpdateTable(table);
+  SysCatalogTable::Actions actions;
+  actions.table_to_update = table;
+  Status s = sys_catalog_->Write(actions);
   if (!s.ok()) {
     LOG(WARNING) << "An error occurred while updating sys-tables: " << s.ToString();
     return s;
@@ -2737,8 +2751,10 @@ Status CatalogManager::ProcessPendingAssignments(
 
   // Update the sys catalog with the new set of tablets/metadata.
   if (s.ok()) {
-    s = sys_catalog_->AddAndUpdateTablets(deferred.tablets_to_add,
-                                          deferred.tablets_to_update);
+    SysCatalogTable::Actions actions;
+    actions.tablets_to_add = deferred.tablets_to_add;
+    actions.tablets_to_update = deferred.tablets_to_update;
+    s = sys_catalog_->Write(actions);
     if (!s.ok()) {
       s = s.CloneAndPrepend("An error occurred while persisting the updated tablet metadata");
     }

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/626c3a83/src/kudu/master/sys_catalog-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/master/sys_catalog-test.cc b/src/kudu/master/sys_catalog-test.cc
index 51de9b0..62729e9 100644
--- a/src/kudu/master/sys_catalog-test.cc
+++ b/src/kudu/master/sys_catalog-test.cc
@@ -126,7 +126,9 @@ TEST_F(SysCatalogTest, TestSysCatalogTablesOperations) {
     l.mutable_data()->pb.set_state(SysTablesEntryPB::PREPARING);
     ASSERT_OK(SchemaToPB(Schema(), l.mutable_data()->pb.mutable_schema()));
     // Add the table
-    ASSERT_OK(master_->catalog_manager()->sys_catalog()->AddTable(table.get()));
+    SysCatalogTable::Actions actions;
+    actions.table_to_add = table.get();
+    ASSERT_OK(master_->catalog_manager()->sys_catalog()->Write(actions));
     l.Commit();
   }
 
@@ -141,7 +143,9 @@ TEST_F(SysCatalogTest, TestSysCatalogTablesOperations) {
     TableMetadataLock l(table.get(), TableMetadataLock::WRITE);
     l.mutable_data()->pb.set_version(1);
     l.mutable_data()->pb.set_state(SysTablesEntryPB::REMOVED);
-    ASSERT_OK(master_->catalog_manager()->sys_catalog()->UpdateTable(table.get()));
+    SysCatalogTable::Actions actions;
+    actions.table_to_update = table.get();
+    ASSERT_OK(master_->catalog_manager()->sys_catalog()->Write(actions));
     l.Commit();
   }
 
@@ -152,7 +156,9 @@ TEST_F(SysCatalogTest, TestSysCatalogTablesOperations) {
 
   // Delete the table
   loader.Reset();
-  ASSERT_OK(master_->catalog_manager()->sys_catalog()->DeleteTable(table.get()));
+  SysCatalogTable::Actions actions;
+  actions.table_to_delete = table.get();
+  ASSERT_OK(master_->catalog_manager()->sys_catalog()->Write(actions));
   ASSERT_OK(master_->catalog_manager()->sys_catalog()->VisitTables(&loader));
   ASSERT_EQ(0, loader.tables.size());
 }
@@ -261,7 +267,9 @@ TEST_F(SysCatalogTest, TestSysCatalogTabletsOperations) {
     loader.Reset();
     TabletMetadataLock l1(tablet1.get(), TabletMetadataLock::WRITE);
     TabletMetadataLock l2(tablet2.get(), TabletMetadataLock::WRITE);
-    ASSERT_OK(sys_catalog->AddTablets(tablets));
+    SysCatalogTable::Actions actions;
+    actions.tablets_to_add = tablets;
+    ASSERT_OK(sys_catalog->Write(actions));
     l1.Commit();
     l2.Commit();
 
@@ -278,7 +286,9 @@ TEST_F(SysCatalogTest, TestSysCatalogTabletsOperations) {
 
     TabletMetadataLock l1(tablet1.get(), TabletMetadataLock::WRITE);
     l1.mutable_data()->pb.set_state(SysTabletsEntryPB::RUNNING);
-    ASSERT_OK(sys_catalog->UpdateTablets(tablets));
+    SysCatalogTable::Actions actions;
+    actions.tablets_to_update = tablets;
+    ASSERT_OK(sys_catalog->Write(actions));
     l1.Commit();
 
     loader.Reset();
@@ -304,7 +314,10 @@ TEST_F(SysCatalogTest, TestSysCatalogTabletsOperations) {
     l2.mutable_data()->pb.set_state(SysTabletsEntryPB::RUNNING);
 
     loader.Reset();
-    ASSERT_OK(sys_catalog->AddAndUpdateTablets(to_add, to_update));
+    SysCatalogTable::Actions actions;
+    actions.tablets_to_add = to_add;
+    actions.tablets_to_update = to_update;
+    ASSERT_OK(sys_catalog->Write(actions));
 
     l1.Commit();
     l2.Commit();
@@ -324,7 +337,9 @@ TEST_F(SysCatalogTest, TestSysCatalogTabletsOperations) {
     tablets.push_back(tablet3.get());
 
     loader.Reset();
-    ASSERT_OK(master_->catalog_manager()->sys_catalog()->DeleteTablets(tablets));
+    SysCatalogTable::Actions actions;
+    actions.tablets_to_delete = tablets;
+    ASSERT_OK(master_->catalog_manager()->sys_catalog()->Write(actions));
     ASSERT_OK(master_->catalog_manager()->sys_catalog()->VisitTablets(&loader));
     ASSERT_EQ(1, loader.tablets.size());
     ASSERT_TRUE(MetadatasEqual(tablet2.get(), loader.tablets[0]));

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/626c3a83/src/kudu/master/sys_catalog.cc
----------------------------------------------------------------------
diff --git a/src/kudu/master/sys_catalog.cc b/src/kudu/master/sys_catalog.cc
index bd8f7fd..553b728 100644
--- a/src/kudu/master/sys_catalog.cc
+++ b/src/kudu/master/sys_catalog.cc
@@ -355,77 +355,80 @@ Schema SysCatalogTable::BuildTableSchema() {
   return builder.Build();
 }
 
+SysCatalogTable::Actions::Actions()
+    : table_to_add(nullptr),
+      table_to_update(nullptr),
+      table_to_delete(nullptr) {
+}
+
+Status SysCatalogTable::Write(const Actions& actions) {
+  TRACE_EVENT0("master", "SysCatalogTable::Write");
+
+  WriteRequestPB req;
+  WriteResponsePB resp;
+  req.set_tablet_id(kSysCatalogTabletId);
+  RETURN_NOT_OK(SchemaToPB(schema_, req.mutable_schema()));
+
+  if (actions.table_to_add) {
+    RETURN_NOT_OK(ReqAddTable(&req, actions.table_to_add));
+  }
+  if (actions.table_to_update) {
+    RETURN_NOT_OK(ReqUpdateTable(&req, actions.table_to_update));
+  }
+  if (actions.table_to_delete) {
+    RETURN_NOT_OK(ReqDeleteTable(&req, actions.table_to_delete));
+  }
+
+  RETURN_NOT_OK(ReqAddTablets(&req, actions.tablets_to_add));
+  RETURN_NOT_OK(ReqUpdateTablets(&req, actions.tablets_to_update));
+  RETURN_NOT_OK(ReqDeleteTablets(&req, actions.tablets_to_delete));
+
+  RETURN_NOT_OK(SyncWrite(&req, &resp));
+  return Status::OK();
+}
+
 // ==================================================================
 // Table related methods
 // ==================================================================
 
-Status SysCatalogTable::AddTable(const TableInfo *table) {
-  TRACE_EVENT1("master", "SysCatalogTable::AddTable",
-               "table", table->ToString());
+Status SysCatalogTable::ReqAddTable(WriteRequestPB* req, const TableInfo* table) {
   faststring metadata_buf;
   if (!pb_util::SerializeToString(table->metadata().dirty().pb, &metadata_buf)) {
     return Status::Corruption("Unable to serialize SysCatalogTablesEntryPB for tablet",
                               table->metadata().dirty().name());
   }
 
-  WriteRequestPB req;
-  WriteResponsePB resp;
-  req.set_tablet_id(kSysCatalogTabletId);
-  RETURN_NOT_OK(SchemaToPB(schema_, req.mutable_schema()));
-
   KuduPartialRow row(&schema_);
   CHECK_OK(row.SetInt8(kSysCatalogTableColType, TABLES_ENTRY));
   CHECK_OK(row.SetString(kSysCatalogTableColId, table->id()));
   CHECK_OK(row.SetString(kSysCatalogTableColMetadata, metadata_buf));
-  RowOperationsPBEncoder enc(req.mutable_row_operations());
+  RowOperationsPBEncoder enc(req->mutable_row_operations());
   enc.Add(RowOperationsPB::INSERT, row);
-
-  RETURN_NOT_OK(SyncWrite(&req, &resp));
   return Status::OK();
 }
 
-Status SysCatalogTable::UpdateTable(const TableInfo *table) {
-  TRACE_EVENT1("master", "SysCatalogTable::UpdateTable",
-               "table", table->ToString());
-
+Status SysCatalogTable::ReqUpdateTable(WriteRequestPB* req, const TableInfo* table) {
   faststring metadata_buf;
   if (!pb_util::SerializeToString(table->metadata().dirty().pb, &metadata_buf)) {
     return Status::Corruption("Unable to serialize SysCatalogTablesEntryPB for tablet",
                               table->id());
   }
 
-  WriteRequestPB req;
-  WriteResponsePB resp;
-  req.set_tablet_id(kSysCatalogTabletId);
-  RETURN_NOT_OK(SchemaToPB(schema_, req.mutable_schema()));
-
   KuduPartialRow row(&schema_);
   CHECK_OK(row.SetInt8(kSysCatalogTableColType, TABLES_ENTRY));
   CHECK_OK(row.SetString(kSysCatalogTableColId, table->id()));
   CHECK_OK(row.SetString(kSysCatalogTableColMetadata, metadata_buf));
-  RowOperationsPBEncoder enc(req.mutable_row_operations());
+  RowOperationsPBEncoder enc(req->mutable_row_operations());
   enc.Add(RowOperationsPB::UPDATE, row);
-
-  RETURN_NOT_OK(SyncWrite(&req, &resp));
   return Status::OK();
 }
 
-Status SysCatalogTable::DeleteTable(const TableInfo *table) {
-  TRACE_EVENT1("master", "SysCatalogTable::DeleteTable",
-               "table", table->ToString());
-  WriteRequestPB req;
-  WriteResponsePB resp;
-  req.set_tablet_id(kSysCatalogTabletId);
-  RETURN_NOT_OK(SchemaToPB(schema_, req.mutable_schema()));
-
+Status SysCatalogTable::ReqDeleteTable(WriteRequestPB* req, const TableInfo* table) {
   KuduPartialRow row(&schema_);
   CHECK_OK(row.SetInt8(kSysCatalogTableColType, TABLES_ENTRY));
   CHECK_OK(row.SetString(kSysCatalogTableColId, table->id()));
-
-  RowOperationsPBEncoder enc(req.mutable_row_operations());
+  RowOperationsPBEncoder enc(req->mutable_row_operations());
   enc.Add(RowOperationsPB::DELETE, row);
-
-  RETURN_NOT_OK(SyncWrite(&req, &resp));
   return Status::OK();
 }
 
@@ -476,13 +479,12 @@ Status SysCatalogTable::VisitTableFromRow(const RowBlockRow& row,
 // Tablet related methods
 // ==================================================================
 
-Status SysCatalogTable::AddTabletsToPB(const vector<TabletInfo*>& tablets,
-                                       RowOperationsPB::Type op_type,
-                                       RowOperationsPB* ops) const {
+Status SysCatalogTable::ReqAddTablets(WriteRequestPB* req,
+                                      const vector<TabletInfo*>& tablets) {
   faststring metadata_buf;
   KuduPartialRow row(&schema_);
-  RowOperationsPBEncoder enc(ops);
-  for (const TabletInfo *tablet : tablets) {
+  RowOperationsPBEncoder enc(req->mutable_row_operations());
+  for (auto tablet : tablets) {
     if (!pb_util::SerializeToString(tablet->metadata().dirty().pb, &metadata_buf)) {
       return Status::Corruption("Unable to serialize SysCatalogTabletsEntryPB for tablet",
                                 tablet->tablet_id());
@@ -491,65 +493,42 @@ Status SysCatalogTable::AddTabletsToPB(const vector<TabletInfo*>& tablets,
     CHECK_OK(row.SetInt8(kSysCatalogTableColType, TABLETS_ENTRY));
     CHECK_OK(row.SetString(kSysCatalogTableColId, tablet->tablet_id()));
     CHECK_OK(row.SetString(kSysCatalogTableColMetadata, metadata_buf));
-    enc.Add(op_type, row);
+    enc.Add(RowOperationsPB::INSERT, row);
   }
+
   return Status::OK();
 }
 
-Status SysCatalogTable::AddAndUpdateTablets(const vector<TabletInfo*>& tablets_to_add,
-                                            const vector<TabletInfo*>& tablets_to_update) {
-  TRACE_EVENT2("master", "AddAndUpdateTablets",
-               "num_add", tablets_to_add.size(),
-               "num_update", tablets_to_update.size());
-
-  WriteRequestPB req;
-  WriteResponsePB resp;
-  req.set_tablet_id(kSysCatalogTabletId);
-  RETURN_NOT_OK(SchemaToPB(schema_, req.mutable_schema()));
-
-  // Insert new Tablets
-  if (!tablets_to_add.empty()) {
-    RETURN_NOT_OK(AddTabletsToPB(tablets_to_add, RowOperationsPB::INSERT,
-                                 req.mutable_row_operations()));
-  }
+Status SysCatalogTable::ReqUpdateTablets(WriteRequestPB* req,
+                                         const vector<TabletInfo*>& tablets) {
+  faststring metadata_buf;
+  KuduPartialRow row(&schema_);
+  RowOperationsPBEncoder enc(req->mutable_row_operations());
+  for (auto tablet : tablets) {
+    if (!pb_util::SerializeToString(tablet->metadata().dirty().pb, &metadata_buf)) {
+      return Status::Corruption("Unable to serialize SysCatalogTabletsEntryPB for tablet",
+                                tablet->tablet_id());
+    }
 
-  // Update already existing Tablets
-  if (!tablets_to_update.empty()) {
-    RETURN_NOT_OK(AddTabletsToPB(tablets_to_update, RowOperationsPB::UPDATE,
-                                 req.mutable_row_operations()));
+    CHECK_OK(row.SetInt8(kSysCatalogTableColType, TABLETS_ENTRY));
+    CHECK_OK(row.SetString(kSysCatalogTableColId, tablet->tablet_id()));
+    CHECK_OK(row.SetString(kSysCatalogTableColMetadata, metadata_buf));
+    enc.Add(RowOperationsPB::UPDATE, row);
   }
 
-  RETURN_NOT_OK(SyncWrite(&req, &resp));
   return Status::OK();
 }
 
-Status SysCatalogTable::AddTablets(const vector<TabletInfo*>& tablets) {
-  vector<TabletInfo*> empty_tablets;
-  return AddAndUpdateTablets(tablets, empty_tablets);
-}
-
-Status SysCatalogTable::UpdateTablets(const vector<TabletInfo*>& tablets) {
-  vector<TabletInfo*> empty_tablets;
-  return AddAndUpdateTablets(empty_tablets, tablets);
-}
-
-Status SysCatalogTable::DeleteTablets(const vector<TabletInfo*>& tablets) {
-  TRACE_EVENT1("master", "DeleteTablets",
-               "num_tablets", tablets.size());
-  WriteRequestPB req;
-  WriteResponsePB resp;
-  req.set_tablet_id(kSysCatalogTabletId);
-  RETURN_NOT_OK(SchemaToPB(schema_, req.mutable_schema()));
-
-  RowOperationsPBEncoder enc(req.mutable_row_operations());
+Status SysCatalogTable::ReqDeleteTablets(WriteRequestPB* req,
+                                         const vector<TabletInfo*>& tablets) {
   KuduPartialRow row(&schema_);
-  for (const TabletInfo* tablet : tablets) {
+  RowOperationsPBEncoder enc(req->mutable_row_operations());
+  for (auto tablet : tablets) {
     CHECK_OK(row.SetInt8(kSysCatalogTableColType, TABLETS_ENTRY));
     CHECK_OK(row.SetString(kSysCatalogTableColId, tablet->tablet_id()));
     enc.Add(RowOperationsPB::DELETE, row);
   }
 
-  RETURN_NOT_OK(SyncWrite(&req, &resp));
   return Status::OK();
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/626c3a83/src/kudu/master/sys_catalog.h
----------------------------------------------------------------------
diff --git a/src/kudu/master/sys_catalog.h b/src/kudu/master/sys_catalog.h
index a588082..6f563e6 100644
--- a/src/kudu/master/sys_catalog.h
+++ b/src/kudu/master/sys_catalog.h
@@ -93,25 +93,22 @@ class SysCatalogTable {
   // Create the new Metadata and initialize the TabletPeer for the sys-table.
   Status CreateNew(FsManager *fs_manager);
 
-  // ==================================================================
-  // Tables related methods
-  // ==================================================================
-  Status AddTable(const TableInfo* table);
-  Status UpdateTable(const TableInfo* table);
-  Status DeleteTable(const TableInfo* table);
+  // Perform a series of table/tablet actions in one WriteTransaction.
+  struct Actions {
+    Actions();
+
+    TableInfo* table_to_add;
+    TableInfo* table_to_update;
+    TableInfo* table_to_delete;
+    std::vector<TabletInfo*> tablets_to_add;
+    std::vector<TabletInfo*> tablets_to_update;
+    std::vector<TabletInfo*> tablets_to_delete;
+  };
+  Status Write(const Actions& actions);
 
   // Scan of the table-related entries.
   Status VisitTables(TableVisitor* visitor);
 
-  // ==================================================================
-  // Tablets related methods
-  // ==================================================================
-  Status AddTablets(const vector<TabletInfo*>& tablets);
-  Status UpdateTablets(const vector<TabletInfo*>& tablets);
-  Status AddAndUpdateTablets(const vector<TabletInfo*>& tablets_to_add,
-                             const vector<TabletInfo*>& tablets_to_update);
-  Status DeleteTablets(const vector<TabletInfo*>& tablets);
-
   // Scan of the tablet-related entries.
   Status VisitTablets(TabletVisitor* visitor);
 
@@ -180,6 +177,17 @@ class SysCatalogTable {
   // Crashes due to an invariant check if the rpc server is not running.
   void InitLocalRaftPeerPB();
 
+  // Add an operation to a write adding/updating/deleting a table or tablet.
+  Status ReqAddTable(tserver::WriteRequestPB* req, const TableInfo* table);
+  Status ReqUpdateTable(tserver::WriteRequestPB* req, const TableInfo* table);
+  Status ReqDeleteTable(tserver::WriteRequestPB* req, const TableInfo* table);
+  Status ReqAddTablets(tserver::WriteRequestPB* req,
+                       const std::vector<TabletInfo*>& tablets);
+  Status ReqUpdateTablets(tserver::WriteRequestPB* req,
+                          const std::vector<TabletInfo*>& tablets);
+  Status ReqDeleteTablets(tserver::WriteRequestPB* req,
+                          const std::vector<TabletInfo*>& tablets);
+
   // Table schema, without IDs, used to send messages to the TabletPeer
   Schema schema_;
   Schema key_schema_;


[4/7] incubator-kudu git commit: KUDU-1382 release notes for 0.8.0

Posted by ad...@apache.org.
KUDU-1382 release notes for 0.8.0

Also change ordered lists to bullet lists throughout
release notes.

Change-Id: I4e83f314cbb373f05b044bd46602713c7dbaee3e
Reviewed-on: http://gerrit.cloudera.org:8080/2655
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/959952a9
Tree: http://git-wip-us.apache.org/repos/asf/incubator-kudu/tree/959952a9
Diff: http://git-wip-us.apache.org/repos/asf/incubator-kudu/diff/959952a9

Branch: refs/heads/master
Commit: 959952a93c86292f45ded234065c75b430d42c4b
Parents: 7e2fe2a
Author: Jean-Daniel Cryans <jd...@apache.org>
Authored: Mon Mar 28 11:28:41 2016 -0700
Committer: Misty Stanley-Jones <mi...@apache.org>
Committed: Thu Mar 31 22:39:16 2016 +0000

----------------------------------------------------------------------
 docs/installation.adoc  |  19 +++----
 docs/release_notes.adoc | 127 +++++++++++++++++++++++++++++++++++--------
 2 files changed, 111 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/959952a9/docs/installation.adoc
----------------------------------------------------------------------
diff --git a/docs/installation.adoc b/docs/installation.adoc
index a9d890b..4d47bfa 100644
--- a/docs/installation.adoc
+++ b/docs/installation.adoc
@@ -621,15 +621,14 @@ in `java/kudu-client/target/apidocs/index.html`.
 // end::view_api[]
 
 [[upgrade]]
-== Upgrade from 0.6.0 to 0.7.0
+== Upgrade from 0.7.1 to 0.8.0
 
 Before upgrading, see <<client_compatibility>> and <<api_compatibility>>.
-To upgrade from Kudu 0.6.0 to 0.7.0, perform the following high-level steps, which
+To upgrade from Kudu 0.7.1 to 0.8.0, perform the following high-level steps, which
 are detailed in <<upgrade_procedure>>:
 
 . Shut down all Kudu services.
-. Install the new Kudu packages or parcels, or install Kudu 0.7.0 from source.
-. Ubuntu only: re-download the repository list file. See <<kudu_package_locations>>.
+. Install the new Kudu packages or parcels, or install Kudu 0.8.0 from source.
 . Restart all Kudu services.
 
 It is technically possible to upgrade Kudu using rolling restarts, but it has not
@@ -641,18 +640,16 @@ from the previous latest version to the newest.
 [[client_compatibility]]
 === Client compatibility
 
-Kudu 0.7.0 maintains wire compatibility with previous versions. This means that a Kudu 0.7.0
-client can communicate with a Kudu 0.6.0 cluster, and vice versa. For that reason,
-you do not need to upgrade client JARs at the same time the cluster is upgraded.
-
-The same wire compatibility guarantees apply to the Impala_Kudu fork that was released
-with Kudu 0.5.0.
+Masters and tablet servers should be upgraded before clients are upgraded. For specific
+information about client compatibility, see the
+link:release_notes.html#rn_0.8.0_incompatible_changes[Incompatible Changes] section
+of the release notes.
 
 [[api_compatibility]]
 
 === API Compatibility
 
-The Kudu 0.7.0 client API is compatible with Kudu 0.6.0.
+The Kudu 0.8.0 client API is compatible with Kudu 0.7.1.
 
 [[upgrade_procedure]]
 === Upgrade procedure

http://git-wip-us.apache.org/repos/asf/incubator-kudu/blob/959952a9/docs/release_notes.adoc
----------------------------------------------------------------------
diff --git a/docs/release_notes.adoc b/docs/release_notes.adoc
index 4697516..16f7456 100644
--- a/docs/release_notes.adoc
+++ b/docs/release_notes.adoc
@@ -53,6 +53,85 @@ By combining all of these properties, Kudu targets support for families of
 applications that are difficult or impossible to implement on current-generation
 Hadoop storage technologies.
 
+[[rn_0.8.0]]
+=== Release notes specific to 0.8.0
+
+Kudu 0.8.0 delivers incremental features, improvements, and bug fixes over the previous versions.
+
+See also +++<a href="https://issues.apache.org/jira/issues/?jql=project%20%3D%20KUDU%20AND%20status%20%3D%20Resolved
+%20AND%20fixVersion%20%3D%200.8.0">JIRAs resolved
+for Kudu 0.8.0</a>+++ and +++<a href="https://github.com/apache/incubator-kudu/compare/0.7.1...0.8.0">Git
+changes between 0.7.1 and 0.8.0</a>+++.
+
+To upgrade to Kudu 0.8.0, see link:installation.html#upgrade[Upgrade from 0.7.1 to 0.8.0].
+
+[[rn_0.8.0_incompatible_changes]]
+==== Incompatible changes
+
+- 0.8.0 clients are not fully compatible with servers running Kudu 0.7.1 or lower.
+In particular, scans that specify column predicates will fail. To work around this
+issue, upgrade all Kudu servers before upgrading clients.
+
+[[rn_0.8.0_new_features]]
+==== New features
+
+- link:https://issues.apache.org/jira/browse/KUDU-431[KUDU-431] A simple Flume
+  sink has been implemented.
+
+[[rn_0.8.0_improvements]]
+==== Improvements
+
+- link:https://issues.apache.org/jira/browse/KUDU-839[KUDU-839] Java RowError now uses an enum error code.
+
+- link:http://gerrit.cloudera.org:8080/#/c/2138/[Gerrit 2138] The handling of
+  column predicates has been re-implemented in the server and clients.
+
+- link:https://issues.apache.org/jira/browse/KUDU-1379[KUDU-1379] Partition pruning
+  has been implemented for C++ clients (but not yet for the Java client). This feature
+  allows you to avoid reading a tablet if you know it does not serve the row keys you are querying.
+
+- link:http://gerrit.cloudera.org:8080/#/c/2641[Gerrit 2641] Kudu now uses
+  `earliest-deadline-first` RPC scheduling and rejection. This changes the behavior
+  of the RPC service queue to prevent unfairness when processing a backlog of RPC
+  threads and to increase the likelihood that an RPC will be processed before it
+  can time out.
+
+
+[[rn_0.8.0_fixed_issues]]
+==== Fixed Issues
+
+- link:https://issues.cloudera.org/browse/KUDU-1337[KUDU-1337] Tablets from tables
+  that were deleted might be unnecessarily re-bootstrapped when the leader gets the
+  notification to delete itself after the replicas do.
+
+- link:https://issues.cloudera.org/browse/KUDU-969[KUDU-969] If a tablet server
+  shuts down while compacting a rowset and receiving updates for it, it might immediately
+  crash upon restart while bootstrapping that rowset's tablet.
+
+- link:https://issues.cloudera.org/browse/KUDU-1354[KUDU-1354] Due to a bug in Kudu's
+  MVCC implementation where row locks were released before the MVCC commit happened,
+  flushed data would include out-of-order transactions, triggering a crash on the
+  next compaction.
+
+- link:https://issues.apache.org/jira/browse/KUDU-1322[KUDU-1322] The C++ client
+  now retries write operations if the tablet it is trying to reach has already been
+  deleted.
+
+- link:http://gerrit.cloudera.org:8080/#/c/2571/[Gerrit 2571] Due to a bug in the
+  Java client, users were unable to close the `kudu-spark` shell because of
+  lingering non-daemon threads.
+
+[[rn_0.8.0_changes]]
+==== Other noteworthy changes
+
+- link:http://gerrit.cloudera.org:8080/#/c/2239/[Gerrit 2239] The concept of "feature flags"
+  was introduced in order to manage compability between different
+  Kudu versions. One case where this is helpful is if a newer client attempts to use
+  a feature unsupported by the currently-running tablet server. Rather than receiving
+  a cryptic error, the user gets an error message that is easier to interpret.
+  This is an internal change for Kudu system developers and requires no action by
+  users of the clients or API.
+
 [[rn_0.7.1]]
 === Release notes specific to 0.7.1
 
@@ -62,26 +141,26 @@ Kudu 0.7.1 is a bug fix release for 0.7.0.
 
 ==== Fixed Issues
 
-. https://issues.apache.org/jira/browse/KUDU-1325[KUDU-1325] fixes a tablet server crash that could
+- https://issues.apache.org/jira/browse/KUDU-1325[KUDU-1325] fixes a tablet server crash that could
 occur during table deletion. In some cases, while a table was being deleted, other replicas would
 attempt to re-replicate tablets to servers that had already processed the deletion. This could
 trigger a race condition that caused a crash.
 
-. https://issues.apache.org/jira/browse/KUDU-1341[KUDU-1341] fixes a potential data corruption and
+- https://issues.apache.org/jira/browse/KUDU-1341[KUDU-1341] fixes a potential data corruption and
 crash that could happen shortly after tablet server restarts in workloads that repeatedly delete
 and re-insert rows with the same primary key. In most cases, this corruption affected only a single
 replica and could be repaired by re-replicating from another.
 
-. https://issues.apache.org/jira/browse/KUDU-1343[KUDU-1343] fixes a bug in the Java client that
+- https://issues.apache.org/jira/browse/KUDU-1343[KUDU-1343] fixes a bug in the Java client that
 occurs when a scanner has to scan multiple batches from one tablet and then start scanning from
 another. In particular, this would affect any scans using the Java client that read large numbers
 of rows from multi-tablet tables.
 
-. https://issues.apache.org/jira/browse/KUDU-1345[KUDU-1345] fixes a bug where in some cases the
+- https://issues.apache.org/jira/browse/KUDU-1345[KUDU-1345] fixes a bug where in some cases the
 hybrid clock could jump backwards, resulting in a crash followed by an inability to
 restart the affected tablet server.
 
-. https://issues.apache.org/jira/browse/KUDU-1360[KUDU-1360] fixes a bug in the kudu-spark module
+- https://issues.apache.org/jira/browse/KUDU-1360[KUDU-1360] fixes a bug in the kudu-spark module
 which prevented reading rows with `NULL` values.
 
 [[rn_0.7.0]]
@@ -100,19 +179,19 @@ The upgrade instructions can be found at link:installation.html#upgrade[Upgrade
 [[rn_0.7.0_incompatible_changes]]
 ==== Incompatible changes
 
-. The C++ client includes a new API, `KuduScanBatch`, which performs better when a
+- The C++ client includes a new API, `KuduScanBatch`, which performs better when a
 large number of small rows are returned in a batch. The old API of `vector<KuduRowResult>`
 is deprecated.
 +
 NOTE: This change is API-compatible but *not* ABI-compatible.
 
-. The default replication factor has been changed from 1 to 3. Existing tables will
+- The default replication factor has been changed from 1 to 3. Existing tables will
 continue to use the replication factor they were created with. Applications that create
 tables may not work properly if they assume a replication factor of 1 and fewer than
 3 replicas are available. To use the previous default replication factor, start the
 master with the configuration flag `--default_num_replicas=1`.
 
-. The Python client has been completely rewritten, with a focus on improving code
+- The Python client has been completely rewritten, with a focus on improving code
 quality and testing. The read path (scanners) has been improved by adding many of
 the features already supported by the C++ and Java clients. The Python client is no
 longer considered experimental.
@@ -120,54 +199,54 @@ longer considered experimental.
 [[rn_0.7.0_new_features]]
 ==== New features
 
-. With the goal of Spark integration in mind, a new `kuduRDD` API has been added,
+- With the goal of Spark integration in mind, a new `kuduRDD` API has been added,
 which wraps `newAPIHadoopRDD` and includes a default source for Spark SQL.
 
 [[rn_0.7.0_improvements]]
 ==== Improvements
 
-. The Java client includes new methods `countPendingErrors()` and
+- The Java client includes new methods `countPendingErrors()` and
 `getPendingErrors()` on `KuduSession`. These methods allow you to count and
 retrieve outstanding row errors when configuring sessions with `AUTO_FLUSH_BACKGROUND`.
 
-. New server-level metrics allow you to monitor CPU usage and context switching.
+- New server-level metrics allow you to monitor CPU usage and context switching.
 
-. Kudu now builds on RHEL 7, CentOS 7, and SLES 12. Extra instructions are included
+- Kudu now builds on RHEL 7, CentOS 7, and SLES 12. Extra instructions are included
 for SLES 12.
 
 
 [[rn_0.7.0_fixed_issues]]
 ==== Fixed Issues
 
-. https://issues.cloudera.org/browse/KUDU-1288[KUDU-1288] fixes a severe file descriptor
+- https://issues.cloudera.org/browse/KUDU-1288[KUDU-1288] fixes a severe file descriptor
 leak, which could previously only be resolved by restarting the tablet server.
 
-. https://issues.cloudera.org/browse/KUDU-1250[KUDU-1250] fixes a hang in the Java
+- https://issues.cloudera.org/browse/KUDU-1250[KUDU-1250] fixes a hang in the Java
 client when processing an in-flight batch and the previous batch encountered an error.
 
 [[rn_0.7.0_changes]]
 ==== Other noteworthy changes
 
-. The file block manager's performance was improved, but it is still not recommended for
+- The file block manager's performance was improved, but it is still not recommended for
 real-world use.
 
-. The master now attempts to spread tablets more evenly across the cluster during
+- The master now attempts to spread tablets more evenly across the cluster during
 table creation. This has no impact on existing tables, but will improve the speed
 at which under-replicated tabletsare re-replicated after a tablet server failure.
 
-. All licensing documents have been modified to adhere to ASF guidelines.
+- All licensing documents have been modified to adhere to ASF guidelines.
 
-. Kudu now requires an out-of-tree build directory. Review the build instructions
+- Kudu now requires an out-of-tree build directory. Review the build instructions
 for additional information.
 
-. The `C++` client library is now explicitly built against the
+- The `C++` client library is now explicitly built against the
 link:https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html[old gcc5 ABI].
 If you use gcc5 to build a Kudu application, your application must use the old ABI
 as well. This is typically achieved by defining the `_GLIBCXX_USE_CXX11_ABI` macro
 at compile-time when building your application. For more information, see the
 previous link and link:http://developerblog.redhat.com/2015/02/05/gcc5-and-the-c11-abi/.
 
-. The Python client is no longer considered experimental.
+- The Python client is no longer considered experimental.
 
 ==== Limitations
 
@@ -185,15 +264,15 @@ Trusty, and SLES 12. Other operating systems may work but have not been tested.
 The 0.6.0 release contains incremental improvements and bug fixes. The most notable
 changes are:
 
-. The Java client's CreateTableBuilder and AlterTableBuilder classes have been renamed
+- The Java client's CreateTableBuilder and AlterTableBuilder classes have been renamed
 to CreateTableOptions and AlterTableOptions. Their methods now also return `this` objects,
 allowing them to be used as builders.
-. The Java client's AbstractKuduScannerBuilder#maxNumBytes() setter is now called
+- The Java client's AbstractKuduScannerBuilder#maxNumBytes() setter is now called
 batchSizeBytes as is the corresponding property in AsyncKuduScanner. This makes it
 consistent with the C++ client.
-. The "kudu-admin" tool can now list and delete tables via its new subcommands
+- The "kudu-admin" tool can now list and delete tables via its new subcommands
 "list_tables" and "delete_table <table_name>".
-. OSX is now supported for single-host development. Please consult its specific installation
+- OSX is now supported for single-host development. Please consult its specific installation
 instructions in link:installation.html#osx_from_source[OS X].
 
 ==== Limitations