diff --git a/hbase-native-client/core/client-test.cc b/hbase-native-client/core/client-test.cc index 6462e6a..1a9fe11 100644 --- a/hbase-native-client/core/client-test.cc +++ b/hbase-native-client/core/client-test.cc @@ -278,6 +278,37 @@ TEST_F(ClientTest, CheckAndPut) { ASSERT_FALSE(result) << "CheckAndPut shouldn't replace value"; } +TEST_F(ClientTest, CheckAndDelete) { + // Using TestUtil to populate test data + ClientTest::test_util->CreateTable("checkDel", "d"); + + // Create TableName and Row to be fetched from HBase + auto tn = folly::to("checkDel"); + auto row = "test1"; + + // Create a client + hbase::Client client(*ClientTest::test_util->conf()); + + // Get connection to HBase Table + auto table = client.Table(tn); + ASSERT_TRUE(table) << "Unable to get connection to Table."; + + auto val1 = "value1"; + + // Perform Puts + table->Put(Put{row}.AddColumn("d", "1", val1)); + table->Put(Put{row}.AddColumn("d", "2", "value2")); + auto result = + table->CheckAndDelete(row, "d", "1", val1, hbase::Delete{row}.AddColumn("d", "2")); + ASSERT_TRUE(result) << "CheckAndDelete didn't replace value"; + + // Perform the Get + hbase::Get get(row); + auto result1 = table->Get(get); + EXPECT_EQ(val1, *(result1->Value("d", "1"))); + ASSERT_FALSE(result1->Value("d", "2")) << "Column 2 should be gone"; +} + TEST_F(ClientTest, PutGet) { // Using TestUtil to populate test data ClientTest::test_util->CreateTable("t", "d"); diff --git a/hbase-native-client/core/raw-async-table.cc b/hbase-native-client/core/raw-async-table.cc index 46d9dfd..53ab526 100644 --- a/hbase-native-client/core/raw-async-table.cc +++ b/hbase-native-client/core/raw-async-table.cc @@ -137,6 +137,33 @@ folly::Future RawAsyncTable::CheckAndPut(const std::string& row, const std return caller->Call().then([caller](const auto r) { return r; }); } +folly::Future RawAsyncTable::CheckAndDelete(const std::string& row, const std::string& family, + const std::string& qualifier, + const std::string& value, + const hbase::Delete& del, + const pb::CompareType& compare_op) { + auto caller = + CreateCallerBuilder(row, connection_conf_->write_rpc_timeout()) + ->action([=, &del](std::shared_ptr controller, + std::shared_ptr loc, + std::shared_ptr rpc_client) -> folly::Future { + return Call( + rpc_client, controller, loc, del, + // request conversion + [=, &del](const hbase::Delete& del, + const std::string& region_name) -> std::unique_ptr { + auto checkReq = RequestConverter::CheckAndDeleteToMutateRequest( + row, family, qualifier, value, compare_op, del, region_name); + return checkReq; + }, + // response conversion + &ResponseConverter::BoolFromMutateResponse); + }) + ->Build(); + + return caller->Call().then([caller](const auto r) { return r; }); +} + folly::Future RawAsyncTable::Delete(const hbase::Delete& del) { auto caller = CreateCallerBuilder(del.row(), connection_conf_->write_rpc_timeout()) diff --git a/hbase-native-client/core/raw-async-table.h b/hbase-native-client/core/raw-async-table.h index 068f230..ca12be6 100644 --- a/hbase-native-client/core/raw-async-table.h +++ b/hbase-native-client/core/raw-async-table.h @@ -72,6 +72,11 @@ class RawAsyncTable { const hbase::Put& put, const pb::CompareType& compare_op = pb::CompareType::EQUAL); + folly::Future CheckAndDelete(const std::string& row, const std::string& family, + const std::string& qualifier, const std::string& value, + const hbase::Delete& del, + const pb::CompareType& compare_op = pb::CompareType::EQUAL); + void Scan(const hbase::Scan& scan, std::shared_ptr consumer); void Close() {} diff --git a/hbase-native-client/core/request-converter.cc b/hbase-native-client/core/request-converter.cc index 0cd9c7c..47c09d1 100644 --- a/hbase-native-client/core/request-converter.cc +++ b/hbase-native-client/core/request-converter.cc @@ -297,6 +297,27 @@ std::unique_ptr RequestConverter::CheckAndPutToMutateRequest( return pb_req; } +std::unique_ptr RequestConverter::CheckAndDeleteToMutateRequest( + const std::string &row, const std::string &family, const std::string &qualifier, + const std::string &value, const pb::CompareType compare_op, const hbase::Delete &del, + const std::string ®ion_name) { + auto pb_req = Request::mutate(); + auto pb_msg = std::static_pointer_cast(pb_req->req_msg()); + + pb_msg->set_allocated_mutation( + ToMutation(MutationType::MutationProto_MutationType_DELETE, del, -1).release()); + ::hbase::pb::Condition *cond = pb_msg->mutable_condition(); + cond->set_row(row); + cond->set_family(family); + cond->set_qualifier(qualifier); + cond->set_allocated_comparator( + Comparator::ToProto(*(ComparatorFactory::BinaryComparator(value).get())).release()); + cond->set_compare_type(compare_op); + + RequestConverter::SetRegion(region_name, pb_msg->mutable_region()); + return pb_req; +} + std::unique_ptr RequestConverter::DeleteToMutateRequest(const Delete &del, const std::string ®ion_name) { auto pb_req = Request::mutate(); diff --git a/hbase-native-client/core/request-converter.h b/hbase-native-client/core/request-converter.h index 0755f42..bcea278 100644 --- a/hbase-native-client/core/request-converter.h +++ b/hbase-native-client/core/request-converter.h @@ -89,6 +89,11 @@ class RequestConverter { const std::string &value, const pb::CompareType compare_op, const hbase::Put &put, const std::string ®ion_name); + static std::unique_ptr CheckAndDeleteToMutateRequest( + const std::string &row, const std::string &family, const std::string &qualifier, + const std::string &value, const pb::CompareType compare_op, const hbase::Delete &del, + const std::string ®ion_name); + static std::unique_ptr IncrementToMutateRequest(const Increment &incr, const std::string ®ion_name); diff --git a/hbase-native-client/core/table.cc b/hbase-native-client/core/table.cc index f20078d..b89c1a2 100644 --- a/hbase-native-client/core/table.cc +++ b/hbase-native-client/core/table.cc @@ -80,6 +80,13 @@ bool Table::CheckAndPut(const std::string &row, const std::string &family, return context.get(operation_timeout()); } +bool Table::CheckAndDelete(const std::string &row, const std::string &family, + const std::string &qualifier, const std::string &value, + const hbase::Delete &del, const pb::CompareType &compare_op) { + auto context = async_table_->CheckAndDelete(row, family, qualifier, value, del, compare_op); + return context.get(operation_timeout()); +} + void Table::Delete(const hbase::Delete &del) { auto future = async_table_->Delete(del); future.get(operation_timeout()); diff --git a/hbase-native-client/core/table.h b/hbase-native-client/core/table.h index 3c486af..cc37182 100644 --- a/hbase-native-client/core/table.h +++ b/hbase-native-client/core/table.h @@ -86,6 +86,29 @@ class Table { void Delete(const hbase::Delete &del); /** + * Atomically checks if a row/family/qualifier value matches the expected + * value. If it does, it adds the delete. If the passed value is null, the + * check is for the lack of column (ie: non-existence) + * + * The expected value argument of this call is on the left and the current + * value of the cell is on the right side of the comparison operator. + * + * Ie. eg. GREATER operator means expected value > existing <=> add the delete. + * + * @param row to check + * @param family column family to check + * @param qualifier column qualifier to check + * @param compare_op comparison operator to use + * @param value the expected value + * @param del data to delete if check succeeds + * @return true if the new delete was executed, false otherwise + */ + bool CheckAndDelete(const std::string &row, const std::string &family, + const std::string &qualifier, const std::string &value, + const hbase::Delete &del, + const pb::CompareType &compare_op = pb::CompareType::EQUAL); + + /** * @brief - Increments some data in the table. * @param - increment Increment object to perform HBase Increment operation. */