blob: e316d1090c0f4375e6ac7edec4d457722652be72 [file] [log] [blame]
// 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 <cstdint>
#include <limits>
#include <memory>
#include <string>
#include <vector>
#include <glog/logging.h>
#include <gtest/gtest.h>
#include "kudu/client/client.h"
#include "kudu/client/shared_ptr.h"
#include "kudu/client/scan_batch.h"
#include "kudu/client/scan_predicate.h"
#include "kudu/client/schema.h"
#include "kudu/client/value.h"
#include "kudu/client/write_op.h"
#include "kudu/common/partial_row.h"
#include "kudu/gutil/gscoped_ptr.h"
#include "kudu/gutil/strings/escaping.h"
#include "kudu/gutil/strings/substitute.h"
#include "kudu/mini-cluster/internal_mini_cluster.h"
#include "kudu/util/decimal_util.h"
#include "kudu/util/int128.h"
#include "kudu/util/status.h"
#include "kudu/util/test_macros.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 cluster::InternalMiniCluster;
using cluster::InternalMiniClusterOptions;
using sp::shared_ptr;
class PredicateTest : public KuduTest {
protected:
void SetUp() override {
// Set up the mini cluster
cluster_.reset(new InternalMiniCluster(env_, InternalMiniClusterOptions()));
ASSERT_OK(cluster_->Start());
ASSERT_OK(cluster_->CreateClient(nullptr, &client_));
}
// 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)
.set_range_partition_columns({ "key" })
.num_replicas(1)
.Create());
shared_ptr<KuduTable> table;
CHECK_OK(client_->OpenTable("table", &table));
return table;
}
// Creates a new session in automatic background flush mode.
shared_ptr<KuduSession> CreateSession() {
shared_ptr<KuduSession> session = client_->NewSession();
session->SetTimeoutMillis(10000);
CHECK_OK(session->SetFlushMode(KuduSession::AUTO_FLUSH_BACKGROUND));
return session;
}
// Count the rows in a table which satisfy the specified predicates.
int DoCountRows(const shared_ptr<KuduTable>& table,
const vector<KuduPredicate*>& predicates) {
KuduScanner scanner(table.get());
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;
}
// Count the rows in a table which satisfy the specified predicates. This
// also does a separate scan with cloned predicates, in order to test that
// cloned predicates behave exactly as the original.
int CountRows(const shared_ptr<KuduTable>& table,
const vector<KuduPredicate*>& predicates) {
vector<KuduPredicate*> cloned_predicates;
for (KuduPredicate* pred : predicates) {
cloned_predicates.push_back(pred->Clone());
}
int cloned_count = DoCountRows(table, cloned_predicates);
int count = DoCountRows(table, predicates);
CHECK_EQ(count, cloned_count);
return count;
}
template <typename T>
int CountMatchedRows(const vector<T>& values, const vector<T>& test_values) {
int count = 0;
for (const T& v : values) {
if (std::any_of(test_values.begin(), test_values.end(),
[&] (const T& t) { return t == v; })) {
count += 1;
}
}
return count;
}
// 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 negative 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 decimal(4, 2) numbers from -50.50 (inclusive) to 50.50
// (exclusive) (100 values) and boundary values.
vector<int128_t> CreateDecimalValues() {
vector<int128_t> values;
for (int i = -50; i < 50; i++) {
values.push_back(i * 100 + i);
}
values.push_back(-9999);
values.push_back(-9998);
values.push_back(9998);
values.push_back(9999);
return values;
}
/// Returns a vector of decimal numbers for creating test predicates.
vector<int128_t> CreateDecimalTestValues() {
return {
-9999,
-9998,
-5100,
-5000,
0,
4900,
5000,
9998,
9999,
};
}
// Returns a vector of string values.
vector<string> CreateStringValues() {
return {
string(),
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 one null value.
template <typename T>
void CheckIntPredicates(const 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 > v
int count = count_if(values.begin(), values.end(),
[&] (T value) { return value > v; });
ASSERT_EQ(count, CountRows(table, {
table->NewComparisonPredicate("value",
KuduPredicate::GREATER,
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,
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)),
}));
}
}
// IN list predicates
std::random_shuffle(test_values.begin(), test_values.end());
for (auto end = test_values.begin(); end <= test_values.end(); end++) {
vector<KuduValue*> vals;
for (auto itr = test_values.begin(); itr != end; itr++) {
vals.push_back(KuduValue::FromInt(*itr));
}
int count = CountMatchedRows<T>(values, vector<T>(test_values.begin(), end));
ASSERT_EQ(count, CountRows(table, { table->NewInListPredicate("value", &vals) }));
}
// IS NOT NULL predicate
ASSERT_EQ(CountRows(table, {}) - 1,
CountRows(table, { table->NewIsNotNullPredicate("value") }));
// IS NULL predicate
ASSERT_EQ(1, CountRows(table, { table->NewIsNullPredicate("value") }));
}
// 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.emplace_back("aa");
test_values.emplace_back("\1", 1);
test_values.emplace_back("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 > 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,
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,
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")),
}));
}
}
// IN list predicates
std::random_shuffle(test_values.begin(), test_values.end());
for (auto end = test_values.begin(); end <= test_values.end(); end++) {
vector<KuduValue*> vals;
for (auto itr = test_values.begin(); itr != end; itr++) {
vals.push_back(KuduValue::CopyString(*itr));
}
int count = CountMatchedRows<string>(values, vector<string>(test_values.begin(), end));
ASSERT_EQ(count, CountRows(table, { table->NewInListPredicate("value", &vals) }));
}
// IS NOT NULL predicate
ASSERT_EQ(CountRows(table, {}) - 1,
CountRows(table, { table->NewIsNotNullPredicate("value") }));
// IS NULL predicate
ASSERT_EQ(1, CountRows(table, { table->NewIsNullPredicate("value") }));
}
shared_ptr<KuduClient> client_;
gscoped_ptr<InternalMiniCluster> 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 }));
}
{ // value > true
KuduPredicate* pred = table->NewComparisonPredicate("value",
KuduPredicate::GREATER,
KuduValue::FromBool(true));
ASSERT_EQ(0, CountRows(table, { pred }));
}
{ // value > false
KuduPredicate* pred = table->NewComparisonPredicate("value",
KuduPredicate::GREATER,
KuduValue::FromBool(false));
ASSERT_EQ(1, CountRows(table, { pred }));
}
{ // value < false
KuduPredicate* pred = table->NewComparisonPredicate("value",
KuduPredicate::LESS,
KuduValue::FromBool(false));
ASSERT_EQ(0, CountRows(table, { pred }));
}
{ // value < true
KuduPredicate* pred = table->NewComparisonPredicate("value",
KuduPredicate::LESS,
KuduValue::FromBool(true));
ASSERT_EQ(1, CountRows(table, { pred }));
}
{ // value IN ()
vector<KuduValue*> values = { };
KuduPredicate* pred = table->NewInListPredicate("value", &values);
ASSERT_EQ(0, CountRows(table, { pred }));
}
{ // value IN (true)
vector<KuduValue*> values = { KuduValue::FromBool(true) };
KuduPredicate* pred = table->NewInListPredicate("value", &values);
ASSERT_EQ(1, CountRows(table, { pred }));
}
{ // value IN (false)
vector<KuduValue*> values = { KuduValue::FromBool(false) };
KuduPredicate* pred = table->NewInListPredicate("value", &values);
ASSERT_EQ(1, CountRows(table, { pred }));
}
{ // value IN (true, false)
vector<KuduValue*> values = { KuduValue::FromBool(false), KuduValue::FromBool(true) };
KuduPredicate* pred = table->NewInListPredicate("value", &values);
ASSERT_EQ(2, CountRows(table, { pred }));
}
// IS NOT NULL predicate
ASSERT_EQ(CountRows(table, {}) - 1,
CountRows(table, { table->NewIsNotNullPredicate("value") }));
// IS NULL predicate
ASSERT_EQ(1, CountRows(table, { table->NewIsNullPredicate("value") }));
}
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::UNIXTIME_MICROS);
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()->SetUnixTimeMicros("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 > v
int count = count_if(values.begin(), values.end(),
[&] (float value) { return value > v; });
ASSERT_EQ(count, CountRows(table, {
table->NewComparisonPredicate("value",
KuduPredicate::GREATER,
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,
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)),
}));
}
}
// IN list predicates
std::random_shuffle(test_values.begin(), test_values.end());
for (auto end = test_values.begin(); end <= test_values.end(); end++) {
vector<KuduValue*> vals;
for (auto itr = test_values.begin(); itr != end; itr++) {
vals.push_back(KuduValue::FromFloat(*itr));
}
int count = CountMatchedRows<float>(values, vector<float>(test_values.begin(), end));
ASSERT_EQ(count, CountRows(table, { table->NewInListPredicate("value", &vals) }));
}
// IS NOT NULL predicate
ASSERT_EQ(values.size(),
CountRows(table, { table->NewIsNotNullPredicate("value") }));
// IS NULL predicate
ASSERT_EQ(1, CountRows(table, { table->NewIsNullPredicate("value") }));
}
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 > v
int count = count_if(values.begin(), values.end(),
[&] (double value) { return value > v; });
ASSERT_EQ(count, CountRows(table, {
table->NewComparisonPredicate("value",
KuduPredicate::GREATER,
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,
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)),
}));
}
}
// IN list predicates
std::random_shuffle(test_values.begin(), test_values.end());
for (auto end = test_values.begin(); end <= test_values.end(); end++) {
vector<KuduValue*> vals;
for (auto itr = test_values.begin(); itr != end; itr++) {
vals.push_back(KuduValue::FromDouble(*itr));
}
int count = CountMatchedRows<double>(values, vector<double>(test_values.begin(), end));
ASSERT_EQ(count, CountRows(table, { table->NewInListPredicate("value", &vals) }));
}
// IS NOT NULL predicate
ASSERT_EQ(values.size(),
CountRows(table, { table->NewIsNotNullPredicate("value") }));
// IS NULL predicate
ASSERT_EQ(1, CountRows(table, { table->NewIsNullPredicate("value") }));
}
TEST_F(PredicateTest, TestDecimalPredicates) {
KuduSchema schema;
{
KuduSchemaBuilder builder;
builder.AddColumn("key")->NotNull()->Type(KuduColumnSchema::INT64)->PrimaryKey();
builder.AddColumn("value")->Type(KuduColumnSchema::DECIMAL)
->Precision(kMaxDecimal128Precision)->Scale(2);
CHECK_OK(builder.Build(&schema));
}
unique_ptr<client::KuduTableCreator> table_creator(client_->NewTableCreator());
CHECK_OK(table_creator->table_name("table")
.schema(&schema)
.set_range_partition_columns({ "key" })
.num_replicas(1)
.Create());
shared_ptr<KuduTable> table;
CHECK_OK(client_->OpenTable("table", &table));
shared_ptr<KuduSession> session = CreateSession();
vector<int128_t> values = CreateDecimalValues();
vector<int128_t> test_values = CreateDecimalTestValues();
int i = 0;
for (int128_t value : values) {
unique_ptr<KuduInsert> insert(table->NewInsert());
ASSERT_OK(insert->mutable_row()->SetInt64("key", i++));
ASSERT_OK(insert->mutable_row()->SetUnscaledDecimal("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 (int128_t v : test_values) {
SCOPED_TRACE(strings::Substitute("test value: $0", v));
{ // value = v
int count = count_if(values.begin(), values.end(),
[&] (int128_t value) { return value == v; });
ASSERT_EQ(count, CountRows(table, {
table->NewComparisonPredicate("value", KuduPredicate::EQUAL,
KuduValue::FromDecimal(v, 2)),
}));
}
{ // value >= v
int count = count_if(values.begin(), values.end(),
[&] (int128_t value) { return value >= v; });
ASSERT_EQ(count, CountRows(table, {
table->NewComparisonPredicate("value",
KuduPredicate::GREATER_EQUAL,
KuduValue::FromDecimal(v, 2)),
}));
}
{ // value <= v
int count = count_if(values.begin(), values.end(),
[&] (int128_t value) { return value <= v; });
ASSERT_EQ(count, CountRows(table, {
table->NewComparisonPredicate("value",
KuduPredicate::LESS_EQUAL,
KuduValue::FromDecimal(v, 2)),
}));
}
{ // value > v
int count = count_if(values.begin(), values.end(),
[&] (int128_t value) { return value > v; });
ASSERT_EQ(count, CountRows(table, {
table->NewComparisonPredicate("value",
KuduPredicate::GREATER,
KuduValue::FromDecimal(v, 2)),
}));
}
{ // value < v
int count = count_if(values.begin(), values.end(),
[&] (int128_t value) { return value < v; });
ASSERT_EQ(count, CountRows(table, {
table->NewComparisonPredicate("value",
KuduPredicate::LESS,
KuduValue::FromDecimal(v, 2)),
}));
}
{ // value >= 0
// value <= v
int count = count_if(values.begin(), values.end(),
[&] (int128_t value) { return value >= 0 && value <= v; });
ASSERT_EQ(count, CountRows(table, {
table->NewComparisonPredicate("value",
KuduPredicate::GREATER_EQUAL,
KuduValue::FromDecimal(0, 2)),
table->NewComparisonPredicate("value",
KuduPredicate::LESS_EQUAL,
KuduValue::FromDecimal(v, 2)),
}));
}
{ // value >= v
// value <= 0.0
int count = count_if(values.begin(), values.end(),
[&] (int128_t value) { return value >= v && value <= 0; });
ASSERT_EQ(count, CountRows(table, {
table->NewComparisonPredicate("value",
KuduPredicate::GREATER_EQUAL,
KuduValue::FromDecimal(v, 2)),
table->NewComparisonPredicate("value",
KuduPredicate::LESS_EQUAL,
KuduValue::FromDecimal(0, 2)),
}));
}
}
// IN list predicates
std::random_shuffle(test_values.begin(), test_values.end());
for (auto end = test_values.begin(); end <= test_values.end(); end++) {
vector<KuduValue*> vals;
for (auto itr = test_values.begin(); itr != end; itr++) {
vals.push_back(KuduValue::FromDecimal(*itr, 2));
}
int count = CountMatchedRows<int128_t>(values, vector<int128_t>(test_values.begin(), end));
ASSERT_EQ(count, CountRows(table, { table->NewInListPredicate("value", &vals) }));
}
// IS NOT NULL predicate
ASSERT_EQ(values.size(),
CountRows(table, { table->NewIsNotNullPredicate("value") }));
// IS NULL predicate
ASSERT_EQ(1, CountRows(table, { table->NewIsNullPredicate("value") }));
}
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()->SetStringNoCopy("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()->SetBinaryNoCopy("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