You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by al...@apache.org on 2023/04/13 16:35:30 UTC

[kudu] branch master updated: KUDU-1945 Add C++ example for non-unique PK

This is an automated email from the ASF dual-hosted git repository.

alexey pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git


The following commit(s) were added to refs/heads/master by this push:
     new 3019848d0 KUDU-1945 Add C++ example for non-unique PK
3019848d0 is described below

commit 3019848d00ac721b30d98a1aeb77bc205352b7b5
Author: Marton Greber <gr...@gmail.com>
AuthorDate: Thu Mar 30 17:32:52 2023 +0000

    KUDU-1945 Add C++ example for non-unique PK
    
    This patch adds an example based on Kudu C++ client API to showcase the
    main operations performed on a table having a non-unique primary key.
    
    STDOUT of the example:
    
    Created a client connection
    Created the schema:
    (
        non_unique_key INT32 NOT NULL,
        auto_incrementing_id INT64 NOT NULL,
        int_val INT32 NOT NULL,
        PRIMARY KEY (non_unique_key, auto_incrementing_id)
    )
    Created the table
    Inserted some row(s) into the table:
    (int32 non_unique_key=3, int64 auto_incrementing_id=1, int32 int_val=0)
    (int32 non_unique_key=3, int64 auto_incrementing_id=2, int32 int_val=1)
    (int32 non_unique_key=3, int64 auto_incrementing_id=3, int32 int_val=2)
    (int32 non_unique_key=4, int64 auto_incrementing_id=4, int32 int_val=0)
    (int32 non_unique_key=1, int64 auto_incrementing_id=1, int32 int_val=0)
    (int32 non_unique_key=1, int64 auto_incrementing_id=2, int32 int_val=1)
    (int32 non_unique_key=1, int64 auto_incrementing_id=3, int32 int_val=2)
    (int32 non_unique_key=2, int64 auto_incrementing_id=4, int32 int_val=0)
    (int32 non_unique_key=2, int64 auto_incrementing_id=5, int32 int_val=1)
    (int32 non_unique_key=2, int64 auto_incrementing_id=6, int32 int_val=2)
    Demonstrating scanning ...
    Scanned some row(s) WHERE non_unique_key = 1:
    (int32 non_unique_key=1, int64 auto_incrementing_id=1, int32 int_val=0)
    (int32 non_unique_key=1, int64 auto_incrementing_id=2, int32 int_val=1)
    (int32 non_unique_key=1, int64 auto_incrementing_id=3, int32 int_val=2)
    Demonstrating UPDATE ...
    Updated row(s) WHERE non_unique_key = 1 AND int_val = 2 to int_val = 98
    (int32 non_unique_key=3, int64 auto_incrementing_id=1, int32 int_val=0)
    (int32 non_unique_key=3, int64 auto_incrementing_id=2, int32 int_val=1)
    (int32 non_unique_key=3, int64 auto_incrementing_id=3, int32 int_val=2)
    (int32 non_unique_key=4, int64 auto_incrementing_id=4, int32 int_val=0)
    (int32 non_unique_key=1, int64 auto_incrementing_id=1, int32 int_val=0)
    (int32 non_unique_key=1, int64 auto_incrementing_id=2, int32 int_val=1)
    (int32 non_unique_key=1, int64 auto_incrementing_id=3, int32 int_val=98)
    (int32 non_unique_key=2, int64 auto_incrementing_id=4, int32 int_val=0)
    (int32 non_unique_key=2, int64 auto_incrementing_id=5, int32 int_val=1)
    (int32 non_unique_key=2, int64 auto_incrementing_id=6, int32 int_val=2)
    Updated row(s) WHERE non_unique_key = 2 to int_val = 99
    (int32 non_unique_key=3, int64 auto_incrementing_id=1, int32 int_val=0)
    (int32 non_unique_key=3, int64 auto_incrementing_id=2, int32 int_val=1)
    (int32 non_unique_key=3, int64 auto_incrementing_id=3, int32 int_val=2)
    (int32 non_unique_key=4, int64 auto_incrementing_id=4, int32 int_val=0)
    (int32 non_unique_key=1, int64 auto_incrementing_id=1, int32 int_val=0)
    (int32 non_unique_key=1, int64 auto_incrementing_id=2, int32 int_val=1)
    (int32 non_unique_key=1, int64 auto_incrementing_id=3, int32 int_val=98)
    (int32 non_unique_key=2, int64 auto_incrementing_id=4, int32 int_val=99)
    (int32 non_unique_key=2, int64 auto_incrementing_id=5, int32 int_val=99)
    (int32 non_unique_key=2, int64 auto_incrementing_id=6, int32 int_val=99)
    Updated row(s) WHERE non_unique_key = 2 AND auto_incrementing_id = 5 to\
    int_val = 100
    (int32 non_unique_key=3, int64 auto_incrementing_id=1, int32 int_val=0)
    (int32 non_unique_key=3, int64 auto_incrementing_id=2, int32 int_val=1)
    (int32 non_unique_key=3, int64 auto_incrementing_id=3, int32 int_val=2)
    (int32 non_unique_key=4, int64 auto_incrementing_id=4, int32 int_val=0)
    (int32 non_unique_key=1, int64 auto_incrementing_id=1, int32 int_val=0)
    (int32 non_unique_key=1, int64 auto_incrementing_id=2, int32 int_val=1)
    (int32 non_unique_key=1, int64 auto_incrementing_id=3, int32 int_val=98)
    (int32 non_unique_key=2, int64 auto_incrementing_id=4, int32 int_val=99)
    (int32 non_unique_key=2, int64 auto_incrementing_id=5, int32 int_val=\
    100)
    (int32 non_unique_key=2, int64 auto_incrementing_id=6, int32 int_val=99)
    Demonstrating DELETE ...
    Deleted row(s) WHERE non_unique_key = 3 AND int_val = 1
    (int32 non_unique_key=3, int64 auto_incrementing_id=1, int32 int_val=0)
    (int32 non_unique_key=3, int64 auto_incrementing_id=3, int32 int_val=2)
    (int32 non_unique_key=4, int64 auto_incrementing_id=4, int32 int_val=0)
    (int32 non_unique_key=1, int64 auto_incrementing_id=1, int32 int_val=0)
    (int32 non_unique_key=1, int64 auto_incrementing_id=2, int32 int_val=1)
    (int32 non_unique_key=1, int64 auto_incrementing_id=3, int32 int_val=98)
    (int32 non_unique_key=2, int64 auto_incrementing_id=4, int32 int_val=99)
    (int32 non_unique_key=2, int64 auto_incrementing_id=5, int32 int_val=\
    100)
    (int32 non_unique_key=2, int64 auto_incrementing_id=6, int32 int_val=99)
    Deleted row(s) WHERE non_unique_key = 2
    (int32 non_unique_key=3, int64 auto_incrementing_id=1, int32 int_val=0)
    (int32 non_unique_key=3, int64 auto_incrementing_id=3, int32 int_val=2)
    (int32 non_unique_key=4, int64 auto_incrementing_id=4, int32 int_val=0)
    (int32 non_unique_key=1, int64 auto_incrementing_id=1, int32 int_val=0)
    (int32 non_unique_key=1, int64 auto_incrementing_id=2, int32 int_val=1)
    (int32 non_unique_key=1, int64 auto_incrementing_id=3, int32 int_val=98)
    Deleted row(s) WHERE non_unique_key = 3 AND auto_incrementing_id = 3
    (int32 non_unique_key=3, int64 auto_incrementing_id=1, int32 int_val=0)
    (int32 non_unique_key=4, int64 auto_incrementing_id=4, int32 int_val=0)
    (int32 non_unique_key=1, int64 auto_incrementing_id=1, int32 int_val=0)
    (int32 non_unique_key=1, int64 auto_incrementing_id=2, int32 int_val=1)
    (int32 non_unique_key=1, int64 auto_incrementing_id=3, int32 int_val=98)
    Deleted the table
    Done
    
    Change-Id: I8c6be6bece56788dc858468d1fcccff6955836ec
    Reviewed-on: http://gerrit.cloudera.org:8080/19690
    Tested-by: Alexey Serbin <al...@apache.org>
    Reviewed-by: Alexey Serbin <al...@apache.org>
---
 examples/cpp/CMakeLists.txt             |   4 +
 examples/cpp/non_unique_primary_key.cc  | 375 ++++++++++++++++++++++++++++++++
 src/kudu/client/CMakeLists.txt          |   1 +
 src/kudu/client/client_examples-test.sh |   1 +
 4 files changed, 381 insertions(+)

diff --git a/examples/cpp/CMakeLists.txt b/examples/cpp/CMakeLists.txt
index 62bc177d6..688aaf149 100644
--- a/examples/cpp/CMakeLists.txt
+++ b/examples/cpp/CMakeLists.txt
@@ -18,9 +18,13 @@
 # Client API example executables
 
 cmake_minimum_required(VERSION 2.8)
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
 
 find_package(kuduClient REQUIRED)
 include_directories(${KUDU_CLIENT_INCLUDE_DIR})
 
 add_executable(example example.cc)
 target_link_libraries(example kudu_client)
+
+add_executable(non_unique_primary_key non_unique_primary_key.cc)
+target_link_libraries(non_unique_primary_key kudu_client)
diff --git a/examples/cpp/non_unique_primary_key.cc b/examples/cpp/non_unique_primary_key.cc
new file mode 100644
index 000000000..8b8271a2f
--- /dev/null
+++ b/examples/cpp/non_unique_primary_key.cc
@@ -0,0 +1,375 @@
+// 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;
+}
diff --git a/src/kudu/client/CMakeLists.txt b/src/kudu/client/CMakeLists.txt
index 65821b3d7..3cc2ab18f 100644
--- a/src/kudu/client/CMakeLists.txt
+++ b/src/kudu/client/CMakeLists.txt
@@ -218,6 +218,7 @@ install(FILES
 install(FILES
   ../../../examples/cpp/CMakeLists.txt
   ../../../examples/cpp/example.cc
+  ../../../examples/cpp/non_unique_primary_key.cc
   DESTINATION ${CMAKE_INSTALL_DATADIR}/doc/kuduClient/examples)
 
 # Exported cmake file for just the library's targets.
diff --git a/src/kudu/client/client_examples-test.sh b/src/kudu/client/client_examples-test.sh
index 562bd1d82..226701fe2 100755
--- a/src/kudu/client/client_examples-test.sh
+++ b/src/kudu/client/client_examples-test.sh
@@ -229,3 +229,4 @@ fi
 
 # Run the examples.
 "$EXAMPLES_DIR/example" $LOCALHOST_IP
+"$EXAMPLES_DIR/non_unique_primary_key" $LOCALHOST_IP