Repository: kudu
Updated Branches:
  refs/heads/master 117e7d906 -> 8386ef9e2


Follow-up improvements to locate_row

- Added test coverage for a string literal with a space in it.
- Abbreviated the tests by adding a couple of lambdas.
- Print out all tablet ids when multiple tablets match the primary key.
  This should never happen, but if it does this output will be useful.
- Couple of other small, non-functional changes.

Change-Id: I9f4cca87963eee1d6da45fcbd3c27f782c35e6f6
Reviewed-on: http://gerrit.cloudera.org:8080/11715
Reviewed-by: Adar Dembo <a...@cloudera.com>
Tested-by: Kudu Jenkins


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

Branch: refs/heads/master
Commit: f0ed88bbe728d7a739a0a0ecce35aaf7535f4933
Parents: 117e7d9
Author: Will Berkeley <wdberke...@gmail.org>
Authored: Wed Oct 17 14:27:48 2018 -0700
Committer: Will Berkeley <wdberke...@gmail.com>
Committed: Thu Oct 18 00:18:25 2018 +0000

----------------------------------------------------------------------
 src/kudu/tools/kudu-admin-test.cc   | 194 +++++++++----------------------
 src/kudu/tools/tool_action_table.cc |  20 +++-
 2 files changed, 71 insertions(+), 143 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kudu/blob/f0ed88bb/src/kudu/tools/kudu-admin-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/kudu-admin-test.cc 
b/src/kudu/tools/kudu-admin-test.cc
index d68fb4b..ecf6323 100644
--- a/src/kudu/tools/kudu-admin-test.cc
+++ b/src/kudu/tools/kudu-admin-test.cc
@@ -1502,7 +1502,7 @@ TEST_F(AdminCliTest, TestLocateRow) {
   }, &stdout, &stderr);
   ASSERT_TRUE(s.ok()) << ToolRunInfo(s, stdout, stderr);
 
-  // Grab list of tablet_ids from any tserver and check the output.
+  // Grab list of tablet_ids from the tserver and check the output.
   vector<TServerDetails*> tservers;
   vector<string> tablet_ids;
   AppendValuesFromMap(tablet_servers_, &tservers);
@@ -1512,45 +1512,30 @@ TEST_F(AdminCliTest, TestLocateRow) {
   ASSERT_EQ(1, tablet_ids.size());
   ASSERT_STR_CONTAINS(stdout, tablet_ids[0]);
 
-  // Test a couple of error cases.
+  // Test a few error cases.
+  const auto check_bad_input = [&](const string& json, const string& error) {
+    string out, err;
+    Status s = RunKuduTool({
+      "table",
+      "locate_row",
+      cluster_->master()->bound_rpc_addr().ToString(),
+      kTableId,
+      json,
+    }, &out, &err);
+    ASSERT_TRUE(s.IsRuntimeError());
+    ASSERT_STR_CONTAINS(err, error);
+  };
+
   // String instead of int.
-  stdout.clear();
-  stderr.clear();
-  s = RunKuduTool({
-    "table",
-    "locate_row",
-    cluster_->master()->bound_rpc_addr().ToString(),
-    kTableId,
-    "[\"foo\"]"
-  }, &stdout, &stderr);
-  ASSERT_TRUE(s.IsRuntimeError());
-  ASSERT_STR_CONTAINS(stderr, "unable to parse");
+  NO_FATALS(check_bad_input("[\"foo\"]", "unable to parse"));
 
   // Float instead of int.
-  stdout.clear();
-  stderr.clear();
-  s = RunKuduTool({
-    "table",
-    "locate_row",
-    cluster_->master()->bound_rpc_addr().ToString(),
-    kTableId,
-    "[1.2]"
-  }, &stdout, &stderr);
-  ASSERT_TRUE(s.IsRuntimeError());
-  ASSERT_STR_CONTAINS(stderr, "unable to parse");
+  NO_FATALS(check_bad_input("[1.2]", "unable to parse"));
 
   // Overflow (recall the key is INT32).
-  stdout.clear();
-  stderr.clear();
-  s = RunKuduTool({
-    "table",
-    "locate_row",
-    cluster_->master()->bound_rpc_addr().ToString(),
-    kTableId,
-    Substitute("[$0]", std::to_string(std::numeric_limits<int64_t>::max()))
-  }, &stdout, &stderr);
-  ASSERT_TRUE(s.IsRuntimeError());
-  ASSERT_STR_CONTAINS(stderr, "out of range");
+  NO_FATALS(check_bad_input(
+      Substitute("[$0]", std::to_string(std::numeric_limits<int64_t>::max())),
+      "out of range"));
 }
 
 TEST_F(AdminCliTest, TestLocateRowMore) {
@@ -1608,7 +1593,7 @@ TEST_F(AdminCliTest, TestLocateRowMore) {
     "locate_row",
     cluster_->master()->bound_rpc_addr().ToString(),
     kAnotherTableId,
-    "[\"foo\",0]"
+    "[\"foo bar\",0]"
   }, &stdout, &stderr);
   ASSERT_TRUE(s.ok()) << ToolRunInfo(s, stdout, stderr);
   StripWhiteSpace(&stdout);
@@ -1633,117 +1618,50 @@ TEST_F(AdminCliTest, TestLocateRowMore) {
       << "expected to find tablet id " << tablet_id_for_2;
   ASSERT_NE(tablet_id_for_0, tablet_id_for_2);
 
+  // Test a few error cases.
+  const auto check_bad_input = [&](const string& json, const string& error) {
+    string out, err;
+    Status s = RunKuduTool({
+      "table",
+      "locate_row",
+      cluster_->master()->bound_rpc_addr().ToString(),
+      kAnotherTableId,
+      json,
+    }, &out, &err);
+    ASSERT_TRUE(s.IsRuntimeError());
+    ASSERT_STR_CONTAINS(err, error);
+  };
+
   // Test locating a row lying in a non-covered range.
-  stdout.clear();
-  stderr.clear();
-  s = RunKuduTool({
-    "table",
-    "locate_row",
-    cluster_->master()->bound_rpc_addr().ToString(),
-    kAnotherTableId,
-    "[\"foo\",1]"
-  }, &stdout, &stderr);
-  ASSERT_TRUE(s.IsRuntimeError()) << ToolRunInfo(s, stdout, stderr);
-  ASSERT_STR_CONTAINS(stderr, "row does not belong to any currently existing 
tablet");
+  NO_FATALS(check_bad_input(
+      "[\"foo\",1]",
+      "row does not belong to any currently existing tablet"));
 
   // Test providing a missing or incomplete primary key.
-  stdout.clear();
-  stderr.clear();
-  s = RunKuduTool({
-    "table",
-    "locate_row",
-    cluster_->master()->bound_rpc_addr().ToString(),
-    kAnotherTableId,
-    "[]"
-  }, &stdout, &stderr);
-  ASSERT_TRUE(s.IsRuntimeError()) << ToolRunInfo(s, stdout, stderr);
-  ASSERT_STR_CONTAINS(
-      stderr,
-      "wrong number of key columns specified: expected 2 but received 0");
-
-  stdout.clear();
-  stderr.clear();
-  s = RunKuduTool({
-    "table",
-    "locate_row",
-    cluster_->master()->bound_rpc_addr().ToString(),
-    kAnotherTableId,
-    "[\"foo\"]"
-  }, &stdout, &stderr);
-  ASSERT_TRUE(s.IsRuntimeError()) << ToolRunInfo(s, stdout, stderr);
-  ASSERT_STR_CONTAINS(
-      stderr,
-      "wrong number of key columns specified: expected 2 but received 1");
-
-  stdout.clear();
-  stderr.clear();
-  s = RunKuduTool({
-    "table",
-    "locate_row",
-    cluster_->master()->bound_rpc_addr().ToString(),
-    kAnotherTableId,
-    "[\"foo\",]"
-  }, &stdout, &stderr);
-  ASSERT_TRUE(s.IsRuntimeError()) << ToolRunInfo(s, stdout, stderr);
-  ASSERT_STR_CONTAINS(
-      stderr,
-      "JSON text is corrupt");
+  NO_FATALS(check_bad_input(
+      "[]",
+      "wrong number of key columns specified: expected 2 but received 0"));
+  NO_FATALS(check_bad_input(
+      "[\"foo\"]",
+      "wrong number of key columns specified: expected 2 but received 1"));
 
   // Test providing too many key column values.
-  stdout.clear();
-  stderr.clear();
-  s = RunKuduTool({
-    "table",
-    "locate_row",
-    cluster_->master()->bound_rpc_addr().ToString(),
-    kAnotherTableId,
-    "[\"foo\",2,\"bar\"]"
-  }, &stdout, &stderr);
-  ASSERT_TRUE(s.IsRuntimeError()) << ToolRunInfo(s, stdout, stderr);
-  ASSERT_STR_CONTAINS(
-      stderr,
-      "wrong number of key columns specified: expected 2 but received 3");
+  NO_FATALS(check_bad_input(
+      "[\"foo\",2,\"bar\"]",
+      "wrong number of key columns specified: expected 2 but received 3"));
 
   // Test providing an invalid value for a key column when there's multiple
   // key columns.
-  stdout.clear();
-  stderr.clear();
-  s = RunKuduTool({
-    "table",
-    "locate_row",
-    cluster_->master()->bound_rpc_addr().ToString(),
-    kAnotherTableId,
-    "[\"foo\",\"bar\"]"
-  }, &stdout, &stderr);
-  ASSERT_TRUE(s.IsRuntimeError()) << ToolRunInfo(s, stdout, stderr);
-  ASSERT_STR_CONTAINS(stderr, "unable to parse");
+  NO_FATALS(check_bad_input("[\"foo\",\"bar\"]", "unable to parse"));
 
   // Test providing bad json.
-  stdout.clear();
-  stderr.clear();
-  s = RunKuduTool({
-    "table",
-    "locate_row",
-    cluster_->master()->bound_rpc_addr().ToString(),
-    kAnotherTableId,
-    "["
-  }, &stdout, &stderr);
-  ASSERT_TRUE(s.IsRuntimeError()) << ToolRunInfo(s, stdout, stderr);
-  ASSERT_STR_CONTAINS(stderr, "JSON text is corrupt");
+  NO_FATALS(check_bad_input("[", "JSON text is corrupt"));
+  NO_FATALS(check_bad_input("[\"foo\",]", "JSON text is corrupt"));
 
   // Test providing valid JSON that's not an array.
-  stdout.clear();
-  stderr.clear();
-  s = RunKuduTool({
-    "table",
-    "locate_row",
-    cluster_->master()->bound_rpc_addr().ToString(),
-    kAnotherTableId,
-    "{ \"key_hash\" : \"foo\", \"key_range\" : 2 }"
-  }, &stdout, &stderr);
-  ASSERT_TRUE(s.IsRuntimeError()) << ToolRunInfo(s, stdout, stderr);
-  ASSERT_STR_CONTAINS(stderr,
-                      "Wrong type during field extraction: expected object 
array");
+  NO_FATALS(check_bad_input(
+      "{ \"key_hash\" : \"foo\", \"key_range\" : 2 }",
+      "Wrong type during field extraction: expected object array"));
 }
 
 TEST_F(AdminCliTest, TestLocateRowAndCheckRowPresence) {
@@ -1794,8 +1712,10 @@ TEST_F(AdminCliTest, TestLocateRowAndCheckRowPresence) {
 
   // Test the case when the row exists. Since the scan is done by a subprocess
   // using a different client instance, it's possible the scan will not
-  // immediately retrieve the row even though the write has already succeeded,
-  // so we ASSERT_EVENTUALLY.
+  // immediately retrieve the row even though the write has already succeeded.
+  // This use case is what timestamp propagation is for, but there's no way to
+  // propagate a timestamp to a tool (and there shouldn't be). Instead, we
+  // ASSERT_EVENTUALLY.
   ASSERT_EVENTUALLY([&]() {
     stdout.clear();
     stderr.clear();

http://git-wip-us.apache.org/repos/asf/kudu/blob/f0ed88bb/src/kudu/tools/tool_action_table.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/tool_action_table.cc 
b/src/kudu/tools/tool_action_table.cc
index 2156dba..310cb97 100644
--- a/src/kudu/tools/tool_action_table.cc
+++ b/src/kudu/tools/tool_action_table.cc
@@ -71,6 +71,7 @@ using client::KuduScanTokenBuilder;
 using client::KuduTable;
 using client::KuduTableAlterer;
 using client::internal::ReplicaController;
+using std::cerr;
 using std::cout;
 using std::endl;
 using std::string;
@@ -204,7 +205,9 @@ Status LocateRow(const RunnerContext& context) {
   JsonReader reader(row_str);
   RETURN_NOT_OK(reader.Init());
   vector<const rapidjson::Value*> values;
-  RETURN_NOT_OK(reader.ExtractObjectArray(reader.root(), nullptr, &values));
+  RETURN_NOT_OK(reader.ExtractObjectArray(reader.root(),
+                                          /*field=*/nullptr,
+                                          &values));
 
   const auto& schema = table->schema();
   vector<int> key_indexes;
@@ -230,7 +233,7 @@ Status LocateRow(const RunnerContext& context) {
       case KuduColumnSchema::UNIXTIME_MICROS: {
         int64_t value;
         RETURN_NOT_OK_PREPEND(
-            reader.ExtractInt64(values[i], nullptr, &value),
+            reader.ExtractInt64(values[i], /*field=*/nullptr, &value),
             Substitute("unable to parse value for column '$0' of type $1",
                        col_name,
                        KuduColumnSchema::DataTypeToString(type)));
@@ -244,7 +247,7 @@ Status LocateRow(const RunnerContext& context) {
       case KuduColumnSchema::STRING: {
         string value;
         RETURN_NOT_OK_PREPEND(
-            reader.ExtractString(values[i], nullptr, &value),
+            reader.ExtractString(values[i], /*field=*/nullptr, &value),
             Substitute("unable to parse value for column '$0' of type $1",
                        col_name,
                        KuduColumnSchema::DataTypeToString(type)));
@@ -286,9 +289,14 @@ Status LocateRow(const RunnerContext& context) {
     return Status::NotFound("row does not belong to any currently existing 
tablet");
   }
   if (tokens.size() > 1) {
-    // This should be impossible.
-    return Status::IllegalState(
-        "all primary key columns specified but more than one matching 
tablet?");
+    // This should be impossible. But if it does happen, we'd like to know what
+    // all the matching tablets were.
+    for (const auto& token : tokens) {
+      cerr << token->tablet().id() << endl;
+    }
+    return Status::IllegalState(Substitute(
+          "all primary key columns specified but found $0 matching tablets!",
+          tokens.size()));
   }
   cout << tokens[0]->tablet().id() << endl;
 

Reply via email to