blob: fa4733cbaf781f002764aa6828918d0d5b9c564b [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 "paimon/predicate/predicate.h"
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "arrow/api.h"
#include "arrow/array/array_nested.h"
#include "arrow/ipc/json_simple.h"
#include "gtest/gtest.h"
#include "paimon/common/data/binary_array.h"
#include "paimon/common/data/binary_row.h"
#include "paimon/common/data/binary_row_writer.h"
#include "paimon/common/predicate/predicate_filter.h"
#include "paimon/defs.h"
#include "paimon/memory/memory_pool.h"
#include "paimon/predicate/function.h"
#include "paimon/predicate/literal.h"
#include "paimon/predicate/predicate_builder.h"
#include "paimon/result.h"
#include "paimon/status.h"
#include "paimon/testing/utils/testharness.h"
namespace arrow {
class Array;
} // namespace arrow
namespace paimon::test {
class PredicateTest : public ::testing::Test {
public:
void SetUp() override {}
void TearDown() override {}
struct FieldStats {
FieldStats(const std::optional<int64_t>& _min_value,
const std::optional<int64_t>& _max_value, int64_t _null_count)
: min_value(_min_value), max_value(_max_value), null_count(_null_count) {}
std::optional<int64_t> min_value;
std::optional<int64_t> max_value;
int64_t null_count;
};
bool StatsCheck(const PredicateFilter& predicate, int64_t row_count,
const std::vector<FieldStats>& field_stats) const {
auto pool = GetDefaultPool();
BinaryRow min_row(/*arity=*/field_stats.size());
BinaryRowWriter min_row_writer(&min_row, 0, pool.get());
BinaryRow max_row(/*arity=*/field_stats.size());
BinaryRowWriter max_row_writer(&max_row, 0, pool.get());
std::vector<int64_t> nulls;
arrow::FieldVector fields;
for (uint32_t i = 0; i < field_stats.size(); i++) {
const auto& stats = field_stats[i];
if (stats.min_value == std::nullopt) {
min_row_writer.SetNullAt(i);
} else {
min_row_writer.WriteLong(i, stats.min_value.value());
}
if (stats.max_value == std::nullopt) {
max_row_writer.SetNullAt(i);
} else {
max_row_writer.WriteLong(i, stats.max_value.value());
}
nulls.emplace_back(stats.null_count);
fields.emplace_back(arrow::field("f" + std::to_string(i), arrow::int64()));
}
min_row_writer.Complete();
max_row_writer.Complete();
auto null_counts = BinaryArray::FromLongArray(nulls, pool.get());
auto arrow_schema = arrow::schema(fields);
EXPECT_OK_AND_ASSIGN(
auto ret, predicate.Test(arrow_schema, row_count, min_row, max_row, null_counts));
return ret;
}
struct StringFieldStats {
StringFieldStats(const std::optional<std::string>& _min_value,
const std::optional<std::string>& _max_value, int64_t _null_count)
: min_value(_min_value), max_value(_max_value), null_count(_null_count) {}
std::optional<std::string> min_value;
std::optional<std::string> max_value;
int64_t null_count;
};
bool StringStatsCheck(const PredicateFilter& predicate, int64_t row_count,
const std::vector<StringFieldStats>& field_stats) const {
auto pool = GetDefaultPool();
BinaryRow min_row(/*arity=*/field_stats.size());
BinaryRowWriter min_row_writer(&min_row, 0, pool.get());
BinaryRow max_row(/*arity=*/field_stats.size());
BinaryRowWriter max_row_writer(&max_row, 0, pool.get());
std::vector<int64_t> nulls;
arrow::FieldVector fields;
for (uint32_t i = 0; i < field_stats.size(); i++) {
const auto& stats = field_stats[i];
if (stats.min_value == std::nullopt) {
min_row_writer.SetNullAt(i);
} else {
min_row_writer.WriteString(
i, BinaryString::FromString(stats.min_value.value(), pool.get()));
}
if (stats.max_value == std::nullopt) {
max_row_writer.SetNullAt(i);
} else {
max_row_writer.WriteString(
i, BinaryString::FromString(stats.max_value.value(), pool.get()));
}
nulls.emplace_back(stats.null_count);
fields.emplace_back(arrow::field("f" + std::to_string(i), arrow::utf8()));
}
min_row_writer.Complete();
max_row_writer.Complete();
auto null_counts = BinaryArray::FromLongArray(nulls, pool.get());
auto arrow_schema = arrow::schema(fields);
EXPECT_OK_AND_ASSIGN(
auto ret, predicate.Test(arrow_schema, row_count, min_row, max_row, null_counts));
return ret;
}
BinaryRow CreateBigIntRow(const std::vector<std::optional<int64_t>>& value) const {
auto pool = GetDefaultPool();
BinaryRow row(/*arity=*/value.size());
BinaryRowWriter row_writer(&row, 0, pool.get());
for (size_t i = 0; i < value.size(); ++i) {
if (value[i] == std::nullopt) {
row_writer.SetNullAt(i);
} else {
row_writer.WriteLong(i, value[i].value());
}
}
row_writer.Complete();
return row;
}
BinaryRow CreateStringRow(const std::vector<std::optional<std::string>>& value) const {
auto pool = GetDefaultPool();
BinaryRow row(/*arity=*/value.size());
BinaryRowWriter row_writer(&row, 0, pool.get());
for (size_t i = 0; i < value.size(); ++i) {
if (value[i] == std::nullopt) {
row_writer.SetNullAt(i);
} else {
row_writer.WriteString(i, BinaryString::FromString(value[i].value(), pool.get()));
}
}
row_writer.Complete();
return row;
}
};
TEST_F(PredicateTest, TestInvalidFieldIndex) {
auto bigint_type = arrow::int64();
auto predicate_base = PredicateBuilder::Equal(/*field_index=*/2, /*field_name=*/"f0",
FieldType::BIGINT, Literal(5l));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([4, 5, null])").ValueOrDie();
auto f1 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2, 1])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
// with array
ASSERT_NOK_WITH_MSG(predicate->Test(*struct_array),
"field index 2 exceed field count 2 in struct array");
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_NOK_WITH_MSG(predicate->Test(arrow_schema, CreateBigIntRow({4})),
"field index 2 exceed field count 1 in row");
}
TEST_F(PredicateTest, TestEqual) {
auto bigint_type = arrow::int64();
auto predicate_base = PredicateBuilder::Equal(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(5l));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([4, 5, null])").ValueOrDie();
auto f1 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2, 1])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 1, 0}));
ASSERT_EQ(*predicate->Negate(),
*PredicateBuilder::NotEqual(/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT,
Literal(5l)));
ASSERT_FALSE(*predicate->Negate() ==
*PredicateBuilder::NotEqual(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(10l)));
ASSERT_FALSE(*predicate->Negate() == *PredicateBuilder::Equal(/*field_index=*/0,
/*field_name=*/"f0",
FieldType::BIGINT, Literal(10l)));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({4})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({5})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 5ll, 0ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 6ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestEqualNull) {
auto bigint_type = arrow::int64();
auto predicate_base = PredicateBuilder::Equal(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(FieldType::BIGINT));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([4, null])").ValueOrDie();
auto f1 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 0}));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({4})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 5ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestNotEqual) {
auto bigint_type = arrow::int64();
auto predicate_base = PredicateBuilder::NotEqual(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(5l));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([4, 5, null])").ValueOrDie();
auto f1 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2, 1])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({1, 0, 0}));
auto predicate_negate = std::dynamic_pointer_cast<PredicateFilter>(predicate->Negate());
ASSERT_EQ(*predicate_negate, *PredicateBuilder::Equal(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(5l)));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({4})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({5})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
ASSERT_TRUE(predicate_negate->Test(arrow_schema, CreateBigIntRow({5})).value());
// with stats
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 5ll, 0ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 6ll, 0ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(5ll, 5ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestNotEqualNull) {
auto bigint_type = arrow::int64();
auto predicate_base = PredicateBuilder::NotEqual(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(FieldType::BIGINT));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([4, null])").ValueOrDie();
auto f1 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 0}));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({4})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 5ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestGreater) {
auto bigint_type = arrow::int64();
auto predicate_base = PredicateBuilder::GreaterThan(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(5l));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([4, 5, 6, null])").ValueOrDie();
auto f1 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2, 1, 0])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 0, 1, 0}));
ASSERT_EQ(*predicate->Negate(),
*PredicateBuilder::LessOrEqual(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(5l)));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({4})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({5})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({6})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 4ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 5ll, 0ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 6ll, 0ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestGreaterNull) {
auto bigint_type = arrow::int64();
auto predicate_base = PredicateBuilder::GreaterThan(
/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT, Literal(FieldType::BIGINT));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([4, null])").ValueOrDie();
auto f1 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 0}));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({4})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 4ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestGreaterOrEqual) {
auto bigint_type = arrow::int64();
auto predicate_base = PredicateBuilder::GreaterOrEqual(
/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT, Literal(5l));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
ASSERT_EQ(predicate->GetFunction().ToString(), "GreaterOrEqual");
auto f0 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([4, 5, 6, null])").ValueOrDie();
auto f1 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2, 1, 0])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 1, 1, 0}));
ASSERT_EQ(*predicate->Negate(),
*PredicateBuilder::LessThan(/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT,
Literal(5l)));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({4})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({5})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({6})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 4ll, 0ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 5ll, 0ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 6ll, 0ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestGreaterOrEqualNull) {
auto bigint_type = arrow::int64();
auto predicate_base = PredicateBuilder::GreaterOrEqual(
/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT, Literal(FieldType::BIGINT));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([4, null])").ValueOrDie();
auto f1 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 0}));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({4})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 4ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestLess) {
auto bigint_type = arrow::int64();
auto predicate_base = PredicateBuilder::LessThan(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(5l));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([4, 5, 6, null])").ValueOrDie();
auto f1 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2, 1, 0])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({1, 0, 0, 0}));
ASSERT_EQ(*predicate->Negate(),
*PredicateBuilder::GreaterOrEqual(
/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT, Literal(5l)));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({4})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({5})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({6})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(5ll, 7ll, 0ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(4ll, 7ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestLessNull) {
auto bigint_type = arrow::int64();
auto predicate_base = PredicateBuilder::LessThan(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(FieldType::BIGINT));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([4, null])").ValueOrDie();
auto f1 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 0}));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({4})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestLessOrEqual) {
auto bigint_type = arrow::int64();
auto predicate_base = PredicateBuilder::LessOrEqual(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(5l));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([4, 5, 6, null])").ValueOrDie();
auto f1 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2, 1, 0])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({1, 1, 0, 0}));
ASSERT_EQ(*predicate->Negate(),
*PredicateBuilder::GreaterThan(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(5l)));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({4})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({5})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({6})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(5ll, 7ll, 0ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(4ll, 7ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestLessOrEqualNull) {
auto bigint_type = arrow::int64();
auto predicate_base = PredicateBuilder::LessOrEqual(
/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT, Literal(FieldType::BIGINT));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([4, null])").ValueOrDie();
auto f1 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 0}));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({4})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestIsNull) {
auto bigint_type = arrow::int64();
auto predicate_base =
PredicateBuilder::IsNull(/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT);
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([4, null])").ValueOrDie();
auto f1 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 1}));
ASSERT_EQ(*predicate->Negate(), *PredicateBuilder::IsNotNull(
/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({4})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(5ll, 7ll, 1ll)}));
}
TEST_F(PredicateTest, TestIsNotNull) {
auto bigint_type = arrow::int64();
auto predicate_base =
PredicateBuilder::IsNotNull(/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT);
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([4, null])").ValueOrDie();
auto f1 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({1, 0}));
ASSERT_EQ(*predicate->Negate(),
*PredicateBuilder::IsNull(/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({4})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(5ll, 7ll, 1ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(std::nullopt, std::nullopt, 3ll)}));
}
TEST_F(PredicateTest, TestIn) {
auto bigint_type = arrow::int64();
auto predicate_base = PredicateBuilder::In(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, {Literal(1l), Literal(3l)});
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([1, 2, 3, null])").ValueOrDie();
auto f1 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2, 1, 0])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({1, 0, 1, 0}));
ASSERT_EQ(*predicate->Negate(),
*PredicateBuilder::NotIn(/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT,
{Literal(1l), Literal(3l)}));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({1})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({2})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({3})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 5ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestInNull) {
auto bigint_type = arrow::int64();
auto predicate_base = PredicateBuilder::In(
/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT,
{Literal(1l), Literal(FieldType::BIGINT), Literal(3l)});
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([1, 2, 3, null])").ValueOrDie();
auto f1 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2, 1, 0])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({1, 0, 1, 0}));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({1})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({2})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({3})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 5ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestNotIn) {
auto bigint_type = arrow::int64();
auto predicate_base = PredicateBuilder::NotIn(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, {Literal(1l), Literal(3l)});
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([1, 2, 3, null])").ValueOrDie();
auto f1 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2, 1, 0])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 1, 0, 0}));
ASSERT_EQ(*predicate->Negate(),
*PredicateBuilder::In(/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT,
{Literal(1l), Literal(3l)}));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({1})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({2})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({3})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(1ll, 1ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(3ll, 3ll, 0ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(1ll, 3ll, 0ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 5ll, 0ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestNotInNull) {
auto bigint_type = arrow::int64();
auto predicate_base = PredicateBuilder::NotIn(
/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT,
{Literal(1l), Literal(FieldType::BIGINT), Literal(3l)});
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([1, 2, 3, null])").ValueOrDie();
auto f1 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2, 1, 0])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 0, 0, 0}));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({1})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({2})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({3})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(1ll, 1ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(3ll, 3ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(1ll, 3ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 5ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestLargeIn) {
auto bigint_type = arrow::int64();
std::vector<Literal> literals;
literals.reserve(30);
literals.emplace_back(1l);
literals.emplace_back(3l);
for (int64_t i = 10; i < 30; i++) {
literals.emplace_back(i);
}
auto predicate_base =
PredicateBuilder::In(/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT, literals);
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([1, 2, 3, null])").ValueOrDie();
auto f1 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2, 1, 0])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({1, 0, 1, 0}));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({1})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({2})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({3})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 5ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(29ll, 32ll, 0ll)}));
}
TEST_F(PredicateTest, TestLargeInNull) {
auto bigint_type = arrow::int64();
std::vector<Literal> literals;
literals.reserve(30);
literals.emplace_back(1l);
literals.emplace_back(FieldType::BIGINT);
literals.emplace_back(3l);
for (int64_t i = 10; i < 30; i++) {
literals.emplace_back(i);
}
auto predicate_base =
PredicateBuilder::In(/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT, literals);
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([1, 2, 3, null])").ValueOrDie();
auto f1 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2, 1, 0])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({1, 0, 1, 0}));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({1})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({2})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({3})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 5ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(29ll, 32ll, 0ll)}));
}
TEST_F(PredicateTest, TestLargeNotIn) {
auto bigint_type = arrow::int64();
std::vector<Literal> literals;
literals.reserve(30);
literals.emplace_back(1l);
literals.emplace_back(3l);
for (int64_t i = 10; i < 30; i++) {
literals.emplace_back(i);
}
auto predicate_base = PredicateBuilder::NotIn(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, literals);
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([1, 2, 3, null])").ValueOrDie();
auto f1 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2, 1, 0])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 1, 0, 0}));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({1})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({2})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({3})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(1ll, 1ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(3ll, 3ll, 0ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(1ll, 3ll, 0ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 5ll, 0ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(29ll, 32ll, 0ll)}));
}
TEST_F(PredicateTest, TestLargeNotInNull) {
auto bigint_type = arrow::int64();
std::vector<Literal> literals;
literals.reserve(30);
literals.emplace_back(1l);
literals.emplace_back(FieldType::BIGINT);
literals.emplace_back(3l);
for (int64_t i = 10; i < 30; i++) {
literals.emplace_back(i);
}
auto predicate_base = PredicateBuilder::NotIn(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, literals);
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([1, 2, 3, null])").ValueOrDie();
auto f1 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 2, 1, 0])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 0, 0, 0}));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({1})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({2})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({3})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(1ll, 1ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(3ll, 3ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(1ll, 3ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(0ll, 5ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(29ll, 32ll, 0ll)}));
}
TEST_F(PredicateTest, TestAnd) {
auto bigint_type = arrow::int64();
ASSERT_OK_AND_ASSIGN(
auto predicate_base,
PredicateBuilder::And({PredicateBuilder::Equal(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(3l)),
PredicateBuilder::Equal(/*field_index=*/1, /*field_name=*/"f1",
FieldType::BIGINT, Literal(5l))}));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
auto f0 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([4, 3, 3, null])").ValueOrDie();
auto f1 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([5, 6, 5, 5])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 0, 1, 0}));
ASSERT_OK_AND_ASSIGN(
auto negate_predicate,
PredicateBuilder::Or({PredicateBuilder::NotEqual(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(3l)),
PredicateBuilder::NotEqual(/*field_index=*/1, /*field_name=*/"f1",
FieldType::BIGINT, Literal(5l))}));
ASSERT_EQ(*predicate->Negate(), *negate_predicate);
// with internal row
auto arrow_schema = arrow::schema(
arrow::FieldVector({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({4, 5})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({3, 6})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({3, 5})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt, 5})).value());
// with stats
ASSERT_TRUE(
StatsCheck(*predicate, 3ll, {FieldStats(3ll, 6ll, 0ll), FieldStats(4ll, 6ll, 0ll)}));
ASSERT_FALSE(
StatsCheck(*predicate, 3ll, {FieldStats(3ll, 6ll, 0ll), FieldStats(6ll, 8ll, 0ll)}));
ASSERT_FALSE(
StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll), FieldStats(4ll, 6ll, 0ll)}));
}
TEST_F(PredicateTest, TestOr) {
auto bigint_type = arrow::int64();
ASSERT_OK_AND_ASSIGN(
auto predicate_base,
PredicateBuilder::Or({PredicateBuilder::Equal(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(3l)),
PredicateBuilder::Equal(/*field_index=*/1, /*field_name=*/"f1",
FieldType::BIGINT, Literal(5l))}));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
auto f0 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([4, 3, 3, null])").ValueOrDie();
auto f1 =
arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([6, 6, 5, 5])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 1, 1, 1}));
ASSERT_OK_AND_ASSIGN(
auto negate_predicate,
PredicateBuilder::And({PredicateBuilder::NotEqual(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(3l)),
PredicateBuilder::NotEqual(/*field_index=*/1, /*field_name=*/"f1",
FieldType::BIGINT, Literal(5l))}));
ASSERT_EQ(*predicate->Negate(), *negate_predicate);
// with internal row
auto arrow_schema = arrow::schema(
arrow::FieldVector({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({4, 6})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({3, 6})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({3, 5})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt, 5})).value());
// with stats
ASSERT_TRUE(
StatsCheck(*predicate, 3ll, {FieldStats(3ll, 6ll, 0ll), FieldStats(4ll, 6ll, 0ll)}));
ASSERT_TRUE(
StatsCheck(*predicate, 3ll, {FieldStats(3ll, 6ll, 0ll), FieldStats(6ll, 8ll, 0ll)}));
ASSERT_FALSE(
StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll), FieldStats(8ll, 10ll, 0ll)}));
}
TEST_F(PredicateTest, TestBetween) {
auto bigint_type = arrow::int64();
auto predicate_base = PredicateBuilder::Between(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(3l), Literal(5l));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([3, 4, 5, 100, 1, null])")
.ValueOrDie();
auto f1 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([1, 2, 3, 4, 5, 6])")
.ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({1, 1, 1, 0, 0, 0}));
auto less_than = PredicateBuilder::LessThan(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(3l));
auto greater_than = PredicateBuilder::GreaterThan(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(5l));
ASSERT_OK_AND_ASSIGN(auto or_predicate, PredicateBuilder::Or({less_than, greater_than}));
auto predicate_negate = std::dynamic_pointer_cast<PredicateFilter>(predicate->Negate());
ASSERT_EQ(*predicate_negate, *or_predicate);
ASSERT_FALSE(*predicate_negate == *predicate_base);
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({1})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateBigIntRow({4})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
ASSERT_TRUE(predicate_negate->Test(arrow_schema, CreateBigIntRow({1})).value());
// with stats
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(1ll, 10ll, 0ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(3ll, 4ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(6ll, 7ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
ASSERT_TRUE(StatsCheck(*predicate, 3ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestBetweenNull) {
auto bigint_type = arrow::int64();
auto predicate_base =
PredicateBuilder::Between(/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT,
Literal(FieldType::BIGINT), Literal(5l));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([4, null])").ValueOrDie();
auto f1 = arrow::ipc::internal::json::ArrayFromJSON(bigint_type, R"([1, 2])").ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", bigint_type), arrow::field("f1", bigint_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 0}));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", bigint_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({4})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateBigIntRow({std::nullopt})).value());
// with stats
ASSERT_FALSE(StatsCheck(*predicate, 3ll, {FieldStats(1ll, 10ll, 0ll)}));
ASSERT_FALSE(StatsCheck(*predicate, 1ll, {FieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestStartsWith) {
auto string_type = arrow::utf8();
ASSERT_OK_AND_ASSIGN(
const auto predicate_base,
PredicateBuilder::StartsWith(/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "aab", 3)));
const auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 = arrow::ipc::internal::json::ArrayFromJSON(string_type,
R"(["ccddee", "bbccdd", "aabbcc", null])")
.ValueOrDie();
auto f1 = arrow::ipc::internal::json::ArrayFromJSON(
string_type, R"(["gghhii", "ffgghh", "eeffgg", "ddeeff"])")
.ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", string_type), arrow::field("f1", string_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 0, 1, 0}));
ASSERT_EQ(predicate->Negate(), nullptr);
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", string_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"ccddee"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"bbccdd"})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"aabbcc"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({std::nullopt})).value());
// with stats
// min="aaa", max="aaz" covers prefix "aab"
ASSERT_TRUE(StringStatsCheck(*predicate, 3ll, {StringFieldStats("aaa", "aaz", 0ll)}));
// min="aab", max="aab" exact match prefix
ASSERT_TRUE(StringStatsCheck(*predicate, 3ll, {StringFieldStats("aab", "aab", 0ll)}));
// min="aabxxx", max="aabzzz" both start with "aab"
ASSERT_TRUE(StringStatsCheck(*predicate, 3ll, {StringFieldStats("aabxxx", "aabzzz", 0ll)}));
// min="bbb", max="ccc" entirely above prefix "aab"
ASSERT_FALSE(StringStatsCheck(*predicate, 3ll, {StringFieldStats("bbb", "ccc", 0ll)}));
// min="aaa", max="aaa" entirely below prefix "aab"
ASSERT_FALSE(StringStatsCheck(*predicate, 3ll, {StringFieldStats("aaa", "aaa", 0ll)}));
// all nulls
ASSERT_FALSE(
StringStatsCheck(*predicate, 1ll, {StringFieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestStartsWithNull) {
const auto string_type = arrow::utf8();
ASSERT_OK_AND_ASSIGN(
const auto predicate_base,
PredicateBuilder::StartsWith(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING, Literal(FieldType::STRING)));
const auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 =
arrow::ipc::internal::json::ArrayFromJSON(string_type, R"(["bbccdd", null])").ValueOrDie();
auto f1 = arrow::ipc::internal::json::ArrayFromJSON(string_type, R"(["ffgghh", "ccddee"])")
.ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", string_type), arrow::field("f1", string_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 0}));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", string_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"bbccdd"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({std::nullopt})).value());
// with stats: null literal always returns false
ASSERT_FALSE(StringStatsCheck(*predicate, 3ll, {StringFieldStats("aaa", "zzz", 0ll)}));
ASSERT_FALSE(
StringStatsCheck(*predicate, 1ll, {StringFieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestEndsWith) {
auto string_type = arrow::utf8();
ASSERT_OK_AND_ASSIGN(
const auto predicate_base,
PredicateBuilder::EndsWith(/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "bcc", 3)));
const auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 = arrow::ipc::internal::json::ArrayFromJSON(string_type,
R"(["ccddee", "bbccdd", "aabbcc", null])")
.ValueOrDie();
auto f1 = arrow::ipc::internal::json::ArrayFromJSON(
string_type, R"(["gghhii", "ffgghh", "eeffgg", "ddeeff"])")
.ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", string_type), arrow::field("f1", string_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 0, 1, 0}));
ASSERT_EQ(predicate->Negate(), nullptr);
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", string_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"ccddee"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"bbccdd"})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"aabbcc"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({std::nullopt})).value());
// with stats: EndsWith base class always returns true for non-null stats
ASSERT_TRUE(StringStatsCheck(*predicate, 3ll, {StringFieldStats("aaa", "zzz", 0ll)}));
ASSERT_TRUE(StringStatsCheck(*predicate, 3ll, {StringFieldStats("xxx", "yyy", 0ll)}));
// all nulls
ASSERT_FALSE(
StringStatsCheck(*predicate, 1ll, {StringFieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestEndsWithNull) {
const auto string_type = arrow::utf8();
ASSERT_OK_AND_ASSIGN(
const auto predicate_base,
PredicateBuilder::EndsWith(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING, Literal(FieldType::STRING)));
const auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 =
arrow::ipc::internal::json::ArrayFromJSON(string_type, R"(["bbccdd", null])").ValueOrDie();
auto f1 = arrow::ipc::internal::json::ArrayFromJSON(string_type, R"(["ffgghh", "ccddee"])")
.ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", string_type), arrow::field("f1", string_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 0}));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", string_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"bbccdd"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({std::nullopt})).value());
// with stats: null literal always returns false
ASSERT_FALSE(StringStatsCheck(*predicate, 3ll, {StringFieldStats("aaa", "zzz", 0ll)}));
ASSERT_FALSE(
StringStatsCheck(*predicate, 1ll, {StringFieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestContains) {
auto string_type = arrow::utf8();
ASSERT_OK_AND_ASSIGN(
const auto predicate_base,
PredicateBuilder::Contains(/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "cde", 3)));
const auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 = arrow::ipc::internal::json::ArrayFromJSON(string_type,
R"(["ghijkl", "defghi", "abcdef", null])")
.ValueOrDie();
auto f1 = arrow::ipc::internal::json::ArrayFromJSON(
string_type, R"(["stuvwx", "pqrstu", "mnopqr", "jklmno"])")
.ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", string_type), arrow::field("f1", string_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 0, 1, 0}));
ASSERT_EQ(predicate->Negate(), nullptr);
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", string_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"ghijkl"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"defghi"})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"abcdef"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({std::nullopt})).value());
// with stats: Contains base class always returns true for non-null stats
ASSERT_TRUE(StringStatsCheck(*predicate, 3ll, {StringFieldStats("aaa", "zzz", 0ll)}));
ASSERT_TRUE(StringStatsCheck(*predicate, 3ll, {StringFieldStats("xxx", "yyy", 0ll)}));
// all nulls
ASSERT_FALSE(
StringStatsCheck(*predicate, 1ll, {StringFieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestContainsNull) {
const auto string_type = arrow::utf8();
ASSERT_OK_AND_ASSIGN(
const auto predicate_base,
PredicateBuilder::Contains(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING, Literal(FieldType::STRING)));
const auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
auto f0 =
arrow::ipc::internal::json::ArrayFromJSON(string_type, R"(["defghi", null])").ValueOrDie();
auto f1 = arrow::ipc::internal::json::ArrayFromJSON(string_type, R"(["pqrstu", "jklmno"])")
.ValueOrDie();
std::shared_ptr<arrow::DataType> src_type =
arrow::struct_({arrow::field("f0", string_type), arrow::field("f1", string_type)});
std::shared_ptr<arrow::Array> struct_array =
arrow::StructArray::Make({f0, f1}, src_type->fields()).ValueOrDie();
ASSERT_OK_AND_ASSIGN(auto is_valid, predicate->Test(*struct_array));
ASSERT_EQ(is_valid, std::vector<char>({0, 0}));
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", string_type)}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"defghi"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({std::nullopt})).value());
// with stats: null literal always returns false
ASSERT_FALSE(StringStatsCheck(*predicate, 3ll, {StringFieldStats("aaa", "zzz", 0ll)}));
ASSERT_FALSE(
StringStatsCheck(*predicate, 1ll, {StringFieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestLike) {
ASSERT_OK_AND_ASSIGN(auto predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "a.c", 3)));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate);
ASSERT_EQ(predicate->Negate(), nullptr);
// with internal row
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", arrow::utf8())}));
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"abc"})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"a.c"})).value());
ASSERT_OK_AND_ASSIGN(predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "a.*d", 4)));
predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"abcd"})).value());
ASSERT_OK_AND_ASSIGN(predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "%c.e", 4)));
predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"abcde"})).value());
ASSERT_OK_AND_ASSIGN(predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "a\\_c", 4)));
predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"a-c"})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"a_c"})).value());
ASSERT_OK_AND_ASSIGN(predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "start%", 6)));
predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"startX"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"not_startX"})).value());
ASSERT_OK_AND_ASSIGN(predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "%middle%", 8)));
predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"xxmiddleyy"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"xxmidxdleyy"})).value());
ASSERT_OK_AND_ASSIGN(predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "%end", 4)));
predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"xxend"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"xxendyy"})).value());
ASSERT_OK_AND_ASSIGN(predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "equal", 5)));
predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"equal"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"equalxx"})).value());
ASSERT_OK_AND_ASSIGN(predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "st_rt%", 6)));
predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"startxx"})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"stbrtxx"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"xxstbrtxx"})).value());
ASSERT_OK_AND_ASSIGN(predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "abc%def%", 8)));
predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"abchahadefxx"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"abchahadafxx"})).value());
// with stats: Like base class always returns true for non-null stats
ASSERT_TRUE(StringStatsCheck(*predicate, 3ll, {StringFieldStats("aaa", "zzz", 0ll)}));
// all nulls
ASSERT_FALSE(
StringStatsCheck(*predicate, 1ll, {StringFieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestLikeConsecutivePercent) {
// consecutive '%' should be merged into one '%'
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", arrow::utf8())}));
// "%%" should behave the same as "%"
ASSERT_OK_AND_ASSIGN(auto predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "%%", 2)));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"anything"})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({""})).value());
// "a%%%b" should behave the same as "a%b"
ASSERT_OK_AND_ASSIGN(predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "a%%%b", 5)));
predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"axyzb"})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"ab"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"axyzc"})).value());
}
TEST_F(PredicateTest, TestLikeEmptyField) {
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", arrow::utf8())}));
// empty field should match "%"
ASSERT_OK_AND_ASSIGN(auto predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "%", 1)));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({""})).value());
// empty field should NOT match "_"
ASSERT_OK_AND_ASSIGN(predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "_", 1)));
predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({""})).value());
// empty field should NOT match a fixed pattern "abc"
ASSERT_OK_AND_ASSIGN(predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "abc", 3)));
predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({""})).value());
}
TEST_F(PredicateTest, TestLikeMinLenExceedsField) {
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", arrow::utf8())}));
// pattern "abcdef" requires 6 literal chars, field "ab" has only 2
ASSERT_OK_AND_ASSIGN(auto predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "abcdef", 6)));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"ab"})).value());
// pattern "a_b_c" requires 3 literal chars, wildcards = min_len 3, field "ab" has 2
ASSERT_OK_AND_ASSIGN(predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "a_b_c", 5)));
predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"ab"})).value());
// field length equals min_len should still be possible to match
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"axbxc"})).value());
}
TEST_F(PredicateTest, TestLikeLongPatternHeapAlloc) {
// pattern length > STACK_LIMIT(128) uses heap allocation
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", arrow::utf8())}));
// Build a pattern with > 128 characters: "a" + 130 * "_" + "z"
std::string long_pattern = "a";
for (int i = 0; i < 130; ++i) {
long_pattern += '_';
}
long_pattern += 'z';
ASSERT_OK_AND_ASSIGN(auto predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, long_pattern.data(), long_pattern.size())));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
// Build a matching field: "a" + 130 * "x" + "z"
std::string matching_field = "a";
for (int i = 0; i < 130; ++i) {
matching_field += 'x';
}
matching_field += 'z';
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({matching_field})).value());
// Non-matching: wrong ending
std::string non_matching_field = "a";
for (int i = 0; i < 130; ++i) {
non_matching_field += 'x';
}
non_matching_field += 'y';
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({non_matching_field})).value());
}
TEST_F(PredicateTest, TestLikeInvalidEscapeSequence) {
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", arrow::utf8())}));
// Trailing backslash is invalid (Java throws "Invalid escape sequence")
ASSERT_OK_AND_ASSIGN(auto predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "abc\\", 4)));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_NOK_WITH_MSG(predicate->Test(arrow_schema, CreateStringRow({"abc"})),
"Invalid escape sequence");
// Backslash followed by non-special char is invalid (only \_, \%, \\ are legal)
ASSERT_OK_AND_ASSIGN(predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "a\\bc", 4)));
predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_NOK_WITH_MSG(predicate->Test(arrow_schema, CreateStringRow({"abc"})),
"Invalid escape sequence");
// \n is not a valid escape
ASSERT_OK_AND_ASSIGN(predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "a\\nf", 4)));
predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_NOK_WITH_MSG(predicate->Test(arrow_schema, CreateStringRow({"anf"})),
"Invalid escape sequence");
}
TEST_F(PredicateTest, TestLikeEscapeBackslash) {
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", arrow::utf8())}));
// \\\\ in C++ string literal = "\\" in the pattern = escaped backslash
ASSERT_OK_AND_ASSIGN(auto predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "a\\\\b", 4)));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
// Field "a\b" should match pattern "a\\b" (escaped backslash)
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"a\\b"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"axb"})).value());
// Escaped percent: "a\%b" matches literal "a%b"
ASSERT_OK_AND_ASSIGN(predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "a\\%b", 4)));
predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"a%b"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"axb"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"axxb"})).value());
}
TEST_F(PredicateTest, TestLikeUtf8MultibyteUnderscore) {
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", arrow::utf8())}));
// Single '_' should match one Unicode character, not one byte.
ASSERT_OK_AND_ASSIGN(auto predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "_", 1)));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"中"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"中文"})).value());
// "a_c" where _ matches one Chinese character
ASSERT_OK_AND_ASSIGN(predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "a_c", 3)));
predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"a中c"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"a中文c"})).value());
// "___" should match exactly 3 Unicode characters
ASSERT_OK_AND_ASSIGN(predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "___", 3)));
predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"中文字"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"中文"})).value());
// '%' should still work with multi-byte characters
std::string pattern_contains = std::string("%") + "中" + "%";
ASSERT_OK_AND_ASSIGN(
predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, pattern_contains.data(), pattern_contains.size())));
predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"hello中world"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"helloworld"})).value());
}
TEST_F(PredicateTest, TestLikeJavaRegexLineTerminatorSemantics) {
auto arrow_schema = arrow::schema(arrow::FieldVector({arrow::field("f0", arrow::utf8())}));
// Java regex '.' does not match line terminators, so '_' should not match them either.
ASSERT_OK_AND_ASSIGN(auto predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "_", 1)));
auto predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"\n"})).value());
ASSERT_FALSE(predicate->Test(arrow_schema, CreateStringRow({"\r"})).value());
// Java LIKE '%' uses (?s:.*), so it should still match line terminators.
ASSERT_OK_AND_ASSIGN(predicate_base,
PredicateBuilder::Like(
/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "%", 1)));
predicate = std::dynamic_pointer_cast<PredicateFilter>(predicate_base);
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"\n"})).value());
ASSERT_TRUE(predicate->Test(arrow_schema, CreateStringRow({"\r"})).value());
}
TEST_F(PredicateTest, TestCompound) {
ASSERT_OK_AND_ASSIGN(
const auto startswith_predicate,
PredicateBuilder::StartsWith(/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "aab", 3)));
ASSERT_OK_AND_ASSIGN(
const auto endswith_predicate,
PredicateBuilder::EndsWith(/*field_index=*/0, /*field_name=*/"f0", FieldType::STRING,
Literal(FieldType::STRING, "bcc", 3)));
ASSERT_OK_AND_ASSIGN(const auto compound_predicate,
PredicateBuilder::And({startswith_predicate, endswith_predicate}));
ASSERT_NOK_WITH_MSG(
PredicateBuilder::Not(compound_predicate),
"Could not construct A NOT predicate from And([StartsWith(f0, aab), EndsWith(f0, bcc)])");
// with stats: And of StartsWith + EndsWith
auto compound_filter = std::dynamic_pointer_cast<PredicateFilter>(compound_predicate);
ASSERT_TRUE(compound_filter);
// min="aab", max="aaz" covers StartsWith("aab"), EndsWith("bcc") returns true by default
ASSERT_TRUE(StringStatsCheck(*compound_filter, 3ll, {StringFieldStats("aab", "aaz", 0ll)}));
// min="bbb", max="ccc" does not cover StartsWith("aab")
ASSERT_FALSE(StringStatsCheck(*compound_filter, 3ll, {StringFieldStats("bbb", "ccc", 0ll)}));
// all nulls
ASSERT_FALSE(StringStatsCheck(*compound_filter, 1ll,
{StringFieldStats(std::nullopt, std::nullopt, 1ll)}));
}
TEST_F(PredicateTest, TestPredicateToString) {
{
auto predicate = PredicateBuilder::Equal(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(5l));
ASSERT_EQ(predicate->ToString(), "Equal(f0, 5)");
}
{
auto predicate = PredicateBuilder::GreaterThan(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(5l));
ASSERT_EQ(predicate->ToString(), "GreaterThan(f0, 5)");
}
{
auto predicate =
PredicateBuilder::IsNotNull(/*field_index=*/0, /*field_name=*/"f0", FieldType::BIGINT);
ASSERT_EQ(predicate->ToString(), "IsNotNull(f0)");
}
{
std::vector<Literal> literals;
literals.reserve(30);
for (int64_t i = 1; i <= 21; i++) {
literals.emplace_back(i);
}
auto predicate = PredicateBuilder::In(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, literals);
ASSERT_TRUE(predicate);
ASSERT_EQ(
predicate->ToString(),
"In(f0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21])");
}
{
std::vector<Literal> literals;
literals.reserve(30);
for (int64_t i = 1; i <= 21; i++) {
literals.emplace_back(i);
}
auto predicate = PredicateBuilder::NotIn(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, literals);
ASSERT_TRUE(predicate);
ASSERT_EQ(predicate->ToString(),
"NotIn(f0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, "
"20, 21])");
}
{
ASSERT_OK_AND_ASSIGN(
auto predicate,
PredicateBuilder::And({PredicateBuilder::Equal(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(3l)),
PredicateBuilder::Equal(/*field_index=*/1, /*field_name=*/"f1",
FieldType::BIGINT, Literal(5l))}));
ASSERT_EQ(predicate->ToString(), "And([Equal(f0, 3), Equal(f1, 5)])");
}
{
ASSERT_OK_AND_ASSIGN(
auto predicate,
PredicateBuilder::Or({PredicateBuilder::Equal(/*field_index=*/0, /*field_name=*/"f0",
FieldType::BIGINT, Literal(3l)),
PredicateBuilder::Equal(/*field_index=*/1, /*field_name=*/"f1",
FieldType::BIGINT, Literal(5l))}));
ASSERT_EQ(predicate->ToString(), "Or([Equal(f0, 3), Equal(f1, 5)])");
}
}
TEST_F(PredicateTest, TestBuildAndOr) {
{
// literals cannot be empty
ASSERT_NOK(PredicateBuilder::Or({}));
}
{
// literals cannot be empty
ASSERT_NOK(PredicateBuilder::And({}));
}
}
} // namespace paimon::test