blob: 8b8271a2f46837a105e8843d3d04363085914f9f [file]
// 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 <ctime>
#include <iostream>
#include <sstream>
#include <memory>
#include "kudu/client/client.h"
#include "kudu/client/row_result.h"
#include "kudu/client/stubs.h"
#include "kudu/client/value.h"
#include "kudu/client/write_op.h"
#include "kudu/common/partial_row.h"
#include "kudu/util/monotime.h"
using kudu::client::KuduClient;
using kudu::client::KuduClientBuilder;
using kudu::client::KuduColumnSchema;
using kudu::client::KuduDelete;
using kudu::client::KuduError;
using kudu::client::KuduInsert;
using kudu::client::KuduPredicate;
using kudu::client::KuduScanBatch;
using kudu::client::KuduScanner;
using kudu::client::KuduSchema;
using kudu::client::KuduSchemaBuilder;
using kudu::client::KuduSession;
using kudu::client::KuduTable;
using kudu::client::KuduTableAlterer;
using kudu::client::KuduTableCreator;
using kudu::client::KuduUpdate;
using kudu::client::KuduValue;
using kudu::client::KuduWriteOperation;
using kudu::client::sp::shared_ptr;
using kudu::KuduPartialRow;
using kudu::MonoDelta;
using kudu::Status;
using std::ostringstream;
using std::string;
using std::vector;
using std::unique_ptr;
static Status CreateClient(const vector<string>& master_addrs,
shared_ptr<KuduClient>* client) {
return KuduClientBuilder()
.master_server_addrs(master_addrs)
.default_admin_operation_timeout(MonoDelta::FromSeconds(20))
.Build(client);
}
static KuduSchema CreateSchema() {
KuduSchema schema;
KuduSchemaBuilder b;
// Columns which are not uniquely identifiable can still be used as primary keys by
// specifying them as non-unique primary key.
b.AddColumn("non_unique_key")->Type(KuduColumnSchema::INT32)->NotNull()->NonUniquePrimaryKey();
b.AddColumn("int_val")->Type(KuduColumnSchema::INT32)->NotNull();
KUDU_CHECK_OK(b.Build(&schema));
return schema;
}
static Status CreateTable(const shared_ptr<KuduClient>& client,
const string& table_name,
const KuduSchema& schema) {
vector<string> column_names;
// Use the non-unique key column for hash partitioning. The column at index 0 in the schema refers
// to the non-unique key column specified above. The auto-incrementing column is always inserted as
// the last key column.
column_names.push_back(schema.Column(0).name());
unique_ptr<KuduTableCreator> table_creator(client->NewTableCreator());
table_creator->table_name(table_name)
.schema(&schema)
.add_hash_partitions(column_names, 2);
Status s = table_creator->Create();
return s;
}
static Status InsertStaleCounterRows(const shared_ptr<KuduTable>& table, int num_rows,
int divisor) {
shared_ptr<KuduSession> session = table->client()->NewSession();
KUDU_RETURN_NOT_OK(session->SetFlushMode(KuduSession::MANUAL_FLUSH));
int stale_counter = 0;
for (int i = 0; i < num_rows; i++) {
if (i % divisor == 0) {
stale_counter++;
}
unique_ptr<KuduInsert> new_insert(table->NewInsert());
KuduPartialRow* row = new_insert->mutable_row();
// The auto-incrementing column is populated on the server-side automatically.
KUDU_CHECK_OK(row->SetInt32("non_unique_key", stale_counter));
KUDU_CHECK_OK(row->SetInt32("int_val", i % divisor));
KUDU_CHECK_OK(session->Apply(new_insert.release()));
}
KUDU_RETURN_NOT_OK(session->Flush());
return session->Close();
}
static Status ScanRows(const shared_ptr<KuduTable>& table, vector<KuduPredicate*> predicates) {
KuduScanner scanner(table.get());
for (int i = 0; i < predicates.size(); i++) {
KUDU_RETURN_NOT_OK(scanner.AddConjunctPredicate(predicates[i]));
}
KUDU_RETURN_NOT_OK(scanner.Open());
KuduScanBatch batch;
while (scanner.HasMoreRows()) {
KUDU_RETURN_NOT_OK(scanner.NextBatch(&batch));
for (KuduScanBatch::const_iterator it = batch.begin(); it != batch.end(); ++it) {
KuduScanBatch::RowPtr row(*it);
// The row contains the auto-incrementing column. If one doesn't requre it, it can be
// discarded through a projection.
KUDU_LOG(INFO) << row.ToString();
}
}
return Status::OK();
}
static Status UpdateRows(const shared_ptr<KuduTable>& table,
vector<KuduPredicate*> predicates, int new_val){
// It's necessary to specify the entire set of key columns when updating a particular row.
// An auto-incrementing column is auto-populated at the server side, and one way to retrieve
// its values is scanning the table with a projection that includes the auto-incrementing column.
KuduScanner scanner(table.get());
for (int i = 0; i < predicates.size(); i++) {
KUDU_RETURN_NOT_OK(scanner.AddConjunctPredicate(predicates[i]));
}
KUDU_RETURN_NOT_OK(scanner.Open());
shared_ptr<KuduSession> session = table->client()->NewSession();
KUDU_RETURN_NOT_OK(session->SetFlushMode(KuduSession::MANUAL_FLUSH));
while (scanner.HasMoreRows()) {
KuduScanBatch batch;
KUDU_RETURN_NOT_OK(scanner.NextBatch(&batch));
for (KuduScanBatch::const_iterator it = batch.begin(); it != batch.end(); ++it) {
KuduScanBatch::RowPtr row(*it);
int64_t auto_incrementing_counter;
int32_t non_unique_key, val;
KUDU_RETURN_NOT_OK(row.GetInt32("non_unique_key", &non_unique_key));
KUDU_RETURN_NOT_OK(row.GetInt64(KuduSchema::GetAutoIncrementingColumnName(),
&auto_incrementing_counter));
KUDU_RETURN_NOT_OK(row.GetInt32("int_val", &val));
unique_ptr<KuduUpdate> new_update(table->NewUpdate());
KuduPartialRow* update_row = new_update->mutable_row();
KUDU_RETURN_NOT_OK(update_row->SetInt32("non_unique_key", non_unique_key));
KUDU_RETURN_NOT_OK(update_row->SetInt64(KuduSchema::GetAutoIncrementingColumnName(),
auto_incrementing_counter));
KUDU_RETURN_NOT_OK(update_row->SetInt32("int_val", new_val));
KUDU_RETURN_NOT_OK(session->Apply(new_update.release()));
}
}
KUDU_RETURN_NOT_OK(session->Flush());
return session->Close();
}
static Status DeleteRows(const shared_ptr<KuduTable>& table,
vector<KuduPredicate*> predicates){
// It's necessary to specify the entire set of key columns when updating a particular row.
// An auto-incrementing column is auto-populated at the server side, and one way to retrieve
// its values is scanning the table with a projection that includes the auto-incrementing column.
KuduScanner scanner(table.get());
for (int i = 0; i < predicates.size(); i++) {
KUDU_RETURN_NOT_OK(scanner.AddConjunctPredicate(predicates[i]));
}
KUDU_RETURN_NOT_OK(scanner.Open());
KuduScanBatch batch;
shared_ptr<KuduSession> session = table->client()->NewSession();
KUDU_RETURN_NOT_OK(session->SetFlushMode(KuduSession::MANUAL_FLUSH));
while (scanner.HasMoreRows()) {
KUDU_RETURN_NOT_OK(scanner.NextBatch(&batch));
for (KuduScanBatch::const_iterator it = batch.begin(); it != batch.end(); ++it) {
KuduScanBatch::RowPtr row(*it);
int64_t auto_incrementing_counter;
int32_t non_unique_key;
KUDU_RETURN_NOT_OK(row.GetInt64(KuduSchema::GetAutoIncrementingColumnName(),
&auto_incrementing_counter));
KUDU_RETURN_NOT_OK(row.GetInt32("non_unique_key", &non_unique_key));
unique_ptr<KuduDelete> new_delete(table->NewDelete());
KuduPartialRow* delete_row = new_delete->mutable_row();
KUDU_RETURN_NOT_OK(delete_row->SetInt32("non_unique_key", non_unique_key));
KUDU_RETURN_NOT_OK(delete_row->SetInt64(KuduSchema::GetAutoIncrementingColumnName(),
auto_incrementing_counter));
KUDU_RETURN_NOT_OK(session->Apply(new_delete.release()));
}
}
KUDU_RETURN_NOT_OK(session->Flush());
return session->Close();
}
int main(int argc, char* argv[]) {
if (argc < 2) {
KUDU_LOG(ERROR) << "usage: " << argv[0] << " <master host> ...";
return -1;
}
vector<string> master_addrs;
for (int i = 1; i < argc; i++) {
master_addrs.push_back(argv[i]);
}
const string kTableName = "non_unique_primary_key_test_table";
shared_ptr<KuduClient> client;
KUDU_CHECK_OK(CreateClient(master_addrs, &client));
KUDU_LOG(INFO) << "Created a client connection";
// Create a schema with non-unique primary key.
KuduSchema schema(CreateSchema());
KUDU_LOG(INFO) << "Created the schema:";
// The schema stringification shows the presence of the auto-incrementing column,
// and the resulting composite primary key.
KUDU_LOG(INFO) << schema.ToString();
KUDU_CHECK_OK(CreateTable(client, kTableName, schema));
KUDU_LOG(INFO) << "Created the table";
// Insert some rows into the table.
shared_ptr<KuduTable> table;
KUDU_CHECK_OK(client->OpenTable(kTableName, &table));
int divisor = 3;
int num_rows = 10;
KUDU_CHECK_OK(InsertStaleCounterRows(table, num_rows, divisor));
KUDU_LOG(INFO) << "Inserted some row(s) into the table:";
KUDU_CHECK_OK(ScanRows(table, {}));
KUDU_LOG(INFO) << "Demonstrating scanning ...";
{
int non_unique_key_equals = 1;
KUDU_LOG(INFO) << "Scanned some row(s) WHERE non_unique_key = "
<< non_unique_key_equals << ":";
vector<KuduPredicate*> predicates;
KuduPredicate* p = table->NewComparisonPredicate(
"non_unique_key", KuduPredicate::EQUAL, KuduValue::FromInt(non_unique_key_equals));
predicates.emplace_back(p);
KUDU_CHECK_OK(ScanRows(table, predicates));
}
KUDU_LOG(INFO) << "Demonstrating UPDATE ...";
// Updating based upon a predicate on a non-unique PK and on a non-PK column
{
int non_unique_key_equals = 1;
int int_val_equals = 2;
int new_val = 98;
vector<KuduPredicate*> predicates;
KuduPredicate* p = table->NewComparisonPredicate(
"non_unique_key", KuduPredicate::EQUAL, KuduValue::FromInt(non_unique_key_equals));
predicates.emplace_back(p);
p = table->NewComparisonPredicate(
"int_val", KuduPredicate::EQUAL, KuduValue::FromInt(int_val_equals));
predicates.emplace_back(p);
// Update row(s)
KUDU_CHECK_OK(UpdateRows(table, predicates, new_val));
KUDU_LOG(INFO) << "Updated row(s) WHERE non_unique_key = " << non_unique_key_equals
<< " AND int_val = " << int_val_equals << " to int_val = " << new_val;
KUDU_CHECK_OK(ScanRows(table, {}));
}
// Updating based upon a predicate on a non-unique PK
{
int non_unique_key_equals = 2;
int new_val = 99;
vector<KuduPredicate*> predicates;
KuduPredicate* p = table->NewComparisonPredicate(
"non_unique_key", KuduPredicate::EQUAL, KuduValue::FromInt(non_unique_key_equals));
predicates.emplace_back(p);
// Update row(s)
KUDU_CHECK_OK(UpdateRows(table, predicates, new_val));
KUDU_LOG(INFO) << "Updated row(s) WHERE non_unique_key = " << non_unique_key_equals
<< " to int_val = " << new_val;
KUDU_CHECK_OK(ScanRows(table, {}));
}
// Updating based upon a predicate on a non-unique PK and on the auto-incrementing column
{
int non_unique_key_equals = 2;
int auto_incrementin_counter_val = 5;
int new_val = 100;
vector<KuduPredicate*> predicates;
KuduPredicate* p = table->NewComparisonPredicate(
"non_unique_key", KuduPredicate::EQUAL, KuduValue::FromInt(non_unique_key_equals));
predicates.emplace_back(p);
p = table->NewComparisonPredicate(
KuduSchema::GetAutoIncrementingColumnName(), KuduPredicate::EQUAL,
KuduValue::FromInt(auto_incrementin_counter_val));
predicates.emplace_back(p);
// Update row(s)
KUDU_CHECK_OK(UpdateRows(table, predicates, new_val));
KUDU_LOG(INFO) << "Updated row(s) WHERE non_unique_key = " << non_unique_key_equals <<
" AND " << KuduSchema::GetAutoIncrementingColumnName() << " = " <<
auto_incrementin_counter_val << " to int_val = " << new_val;
KUDU_CHECK_OK(ScanRows(table, {}));
}
KUDU_LOG(INFO) << "Demonstrating DELETE ...";
// Deleting based upon a predicate on a non-unique PK and on a non-PK column
{
int non_unique_key_equals = 3;
int int_val_equals = 1;
vector<KuduPredicate*> predicates;
KuduPredicate* p = table->NewComparisonPredicate(
"non_unique_key", KuduPredicate::EQUAL, KuduValue::FromInt(non_unique_key_equals));
predicates.emplace_back(p);
p = table->NewComparisonPredicate(
"int_val", KuduPredicate::EQUAL, KuduValue::FromInt(int_val_equals));
predicates.emplace_back(p);
// Delete row(s)
KUDU_CHECK_OK(DeleteRows(table, predicates));
KUDU_LOG(INFO) << "Deleted row(s) WHERE non_unique_key = " << non_unique_key_equals
<< " AND int_val = " << int_val_equals;
KUDU_CHECK_OK(ScanRows(table, {}));
}
// Deleting based upon a predicate on a non-unique PK
{
int non_unique_key_equals = 2;
vector<KuduPredicate*> predicates;
KuduPredicate* p = table->NewComparisonPredicate(
"non_unique_key", KuduPredicate::EQUAL, KuduValue::FromInt(non_unique_key_equals));
predicates.emplace_back(p);
// Delete row(s)
KUDU_CHECK_OK(DeleteRows(table, predicates));
KUDU_LOG(INFO) << "Deleted row(s) WHERE non_unique_key = " << non_unique_key_equals;
KUDU_CHECK_OK(ScanRows(table, {}));
}
// Deleting based upon a predicate on a non-unique PK and on the auto-incrementing column
{
int non_unique_key_equals = 3;
int auto_incrementin_counter_val = 3;
vector<KuduPredicate*> predicates;
KuduPredicate* p = table->NewComparisonPredicate(
"non_unique_key", KuduPredicate::EQUAL, KuduValue::FromInt(non_unique_key_equals));
predicates.emplace_back(p);
p = table->NewComparisonPredicate(
KuduSchema::GetAutoIncrementingColumnName(), KuduPredicate::EQUAL,
KuduValue::FromInt(auto_incrementin_counter_val));
predicates.emplace_back(p);
// Delete row(s)
KUDU_CHECK_OK(DeleteRows(table, predicates));
KUDU_LOG(INFO) << "Deleted row(s) WHERE non_unique_key = " << non_unique_key_equals <<
" AND " << KuduSchema::GetAutoIncrementingColumnName() << " = " <<
auto_incrementin_counter_val;
KUDU_CHECK_OK(ScanRows(table, {}));
}
KUDU_CHECK_OK(client->DeleteTable(kTableName));
KUDU_LOG(INFO) << "Deleted the table";
KUDU_LOG(INFO) << "Done";
return 0;
}