| // 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 "storage/predicate/accept_null_predicate.h" |
| |
| #include <gtest/gtest.h> |
| |
| #include <memory> |
| #include <roaring/roaring.hh> |
| |
| #include "core/data_type/define_primitive_type.h" |
| #include "runtime/memory/lru_cache_policy.h" |
| #include "storage/index/index_iterator.h" |
| #include "storage/index/inverted/inverted_index_cache.h" |
| #include "storage/predicate/comparison_predicate.h" |
| |
| namespace doris { |
| namespace { |
| |
| class MockLRUCachePolicy : public LRUCachePolicy { |
| public: |
| MockLRUCachePolicy(std::shared_ptr<roaring::Roaring> bitmap) |
| : LRUCachePolicy(CachePolicy::CacheType::INVERTEDINDEX_QUERY_CACHE, 1024, |
| LRUCacheType::SIZE, 3600, 1, 0, true, false) { |
| _cache_value.bitmap = bitmap; |
| } |
| |
| void* value(Cache::Handle* handle) override { return &_cache_value; } |
| |
| void release(Cache::Handle* handle) override {} |
| |
| segment_v2::InvertedIndexQueryCache::CacheValue _cache_value; |
| }; |
| |
| class MockIndexIterator : public segment_v2::IndexIterator { |
| public: |
| MockIndexIterator(bool has_null, std::shared_ptr<roaring::Roaring> null_bitmap) |
| : _has_null_value(has_null), _null_bitmap(std::move(null_bitmap)) { |
| if (_null_bitmap) { |
| _mock_cache = std::make_unique<MockLRUCachePolicy>(_null_bitmap); |
| } |
| } |
| |
| Result<bool> has_null() override { return _has_null_value; } |
| |
| Status read_null_bitmap(segment_v2::InvertedIndexQueryCacheHandle* cache_handle) override { |
| if (_mock_cache && _null_bitmap) { |
| *cache_handle = segment_v2::InvertedIndexQueryCacheHandle( |
| _mock_cache.get(), reinterpret_cast<Cache::Handle*>(1)); |
| } |
| return Status::OK(); |
| } |
| |
| segment_v2::IndexReaderPtr get_reader(segment_v2::IndexReaderType) const override { |
| return nullptr; |
| } |
| |
| Status read_from_index(const segment_v2::IndexParam&) override { return Status::OK(); } |
| |
| private: |
| bool _has_null_value; |
| std::shared_ptr<roaring::Roaring> _null_bitmap; |
| std::unique_ptr<MockLRUCachePolicy> _mock_cache; |
| }; |
| |
| class MockNestedPredicate : public ColumnPredicate { |
| public: |
| MockNestedPredicate(uint32_t column_id, std::shared_ptr<roaring::Roaring> result_bitmap) |
| : ColumnPredicate(column_id, "mock_col", PrimitiveType::TYPE_INT, false), |
| _result_bitmap(std::move(result_bitmap)) {} |
| |
| PredicateType type() const override { return PredicateType::EQ; } |
| |
| Status evaluate(const IndexFieldNameAndTypePair& name_with_type, |
| segment_v2::IndexIterator* iterator, uint32_t num_rows, |
| roaring::Roaring* bitmap) const override { |
| *bitmap = *_result_bitmap; |
| return Status::OK(); |
| } |
| |
| std::shared_ptr<ColumnPredicate> clone(uint32_t col_id) const override { |
| return std::make_shared<MockNestedPredicate>(col_id, _result_bitmap); |
| } |
| |
| private: |
| uint16_t _evaluate_inner(const IColumn& column, uint16_t* sel, uint16_t size) const override { |
| return size; |
| } |
| |
| std::shared_ptr<roaring::Roaring> _result_bitmap; |
| }; |
| |
| } // anonymous namespace |
| |
| class AcceptNullPredicateTest : public testing::Test { |
| public: |
| AcceptNullPredicateTest() = default; |
| ~AcceptNullPredicateTest() = default; |
| }; |
| |
| TEST_F(AcceptNullPredicateTest, EvaluateWithNullIterator) { |
| auto nested_result = std::make_shared<roaring::Roaring>(); |
| nested_result->add(2); |
| nested_result->add(5); |
| nested_result->add(8); |
| |
| auto nested_pred = std::make_shared<MockNestedPredicate>(0, nested_result); |
| AcceptNullPredicate predicate(nested_pred); |
| |
| roaring::Roaring bitmap; |
| IndexFieldNameAndTypePair name_with_type = {"test_col", nullptr}; |
| |
| Status status = predicate.evaluate(name_with_type, nullptr, 10, &bitmap); |
| |
| EXPECT_TRUE(status.ok()); |
| EXPECT_EQ(bitmap.cardinality(), 3); |
| EXPECT_TRUE(bitmap.contains(2)); |
| EXPECT_TRUE(bitmap.contains(5)); |
| EXPECT_TRUE(bitmap.contains(8)); |
| } |
| |
| TEST_F(AcceptNullPredicateTest, EvaluateWithNoNull) { |
| auto nested_result = std::make_shared<roaring::Roaring>(); |
| nested_result->add(1); |
| nested_result->add(3); |
| nested_result->add(7); |
| |
| auto nested_pred = std::make_shared<MockNestedPredicate>(0, nested_result); |
| AcceptNullPredicate predicate(nested_pred); |
| |
| auto null_bitmap = std::make_shared<roaring::Roaring>(); |
| null_bitmap->add(0); |
| MockIndexIterator iterator(false, null_bitmap); |
| |
| roaring::Roaring bitmap; |
| IndexFieldNameAndTypePair name_with_type = {"test_col", nullptr}; |
| |
| Status status = predicate.evaluate(name_with_type, &iterator, 10, &bitmap); |
| |
| EXPECT_TRUE(status.ok()); |
| EXPECT_EQ(bitmap.cardinality(), 3); |
| EXPECT_TRUE(bitmap.contains(1)); |
| EXPECT_TRUE(bitmap.contains(3)); |
| EXPECT_TRUE(bitmap.contains(7)); |
| EXPECT_FALSE(bitmap.contains(0)); |
| } |
| |
| TEST_F(AcceptNullPredicateTest, EvaluateWithNullRows) { |
| auto nested_result = std::make_shared<roaring::Roaring>(); |
| nested_result->add(2); |
| nested_result->add(5); |
| nested_result->add(8); |
| |
| auto nested_pred = std::make_shared<MockNestedPredicate>(0, nested_result); |
| AcceptNullPredicate predicate(nested_pred); |
| |
| auto null_bitmap = std::make_shared<roaring::Roaring>(); |
| null_bitmap->add(0); |
| null_bitmap->add(3); |
| null_bitmap->add(9); |
| MockIndexIterator iterator(true, null_bitmap); |
| |
| roaring::Roaring bitmap; |
| bitmap.addRange(0, 10); |
| IndexFieldNameAndTypePair name_with_type = {"test_col", nullptr}; |
| |
| Status status = predicate.evaluate(name_with_type, &iterator, 10, &bitmap); |
| |
| EXPECT_TRUE(status.ok()); |
| EXPECT_EQ(bitmap.cardinality(), 6); |
| EXPECT_TRUE(bitmap.contains(0)); |
| EXPECT_TRUE(bitmap.contains(2)); |
| EXPECT_TRUE(bitmap.contains(3)); |
| EXPECT_TRUE(bitmap.contains(5)); |
| EXPECT_TRUE(bitmap.contains(8)); |
| EXPECT_TRUE(bitmap.contains(9)); |
| } |
| |
| TEST_F(AcceptNullPredicateTest, DebugString) { |
| auto nested_result = std::make_shared<roaring::Roaring>(); |
| auto nested_pred = std::make_shared<MockNestedPredicate>(0, nested_result); |
| AcceptNullPredicate predicate(nested_pred); |
| |
| std::string debug_str = predicate.debug_string(); |
| EXPECT_TRUE(debug_str.find("AcceptNullPredicate") != std::string::npos); |
| } |
| |
| TEST_F(AcceptNullPredicateTest, TypeDelegation) { |
| auto nested_result = std::make_shared<roaring::Roaring>(); |
| auto nested_pred = std::make_shared<MockNestedPredicate>(0, nested_result); |
| AcceptNullPredicate predicate(nested_pred); |
| |
| EXPECT_EQ(predicate.type(), PredicateType::EQ); |
| } |
| |
| TEST_F(AcceptNullPredicateTest, Clone) { |
| auto nested_result = std::make_shared<roaring::Roaring>(); |
| auto nested_pred = std::make_shared<MockNestedPredicate>(0, nested_result); |
| AcceptNullPredicate predicate(nested_pred); |
| |
| auto cloned = predicate.clone(1); |
| EXPECT_NE(cloned, nullptr); |
| EXPECT_EQ(cloned->column_id(), 1); |
| } |
| |
| TEST_F(AcceptNullPredicateTest, EvaluateWithPreFilteredBitmap) { |
| auto nested_result = std::make_shared<roaring::Roaring>(); |
| nested_result->add(0); |
| nested_result->add(1); |
| nested_result->add(5); |
| |
| auto nested_pred = std::make_shared<MockNestedPredicate>(0, nested_result); |
| AcceptNullPredicate predicate(nested_pred); |
| |
| auto null_bitmap = std::make_shared<roaring::Roaring>(); |
| null_bitmap->add(2); |
| null_bitmap->add(3); |
| null_bitmap->add(7); |
| MockIndexIterator iterator(true, null_bitmap); |
| |
| roaring::Roaring bitmap; |
| bitmap.add(0); |
| bitmap.add(1); |
| bitmap.add(2); |
| bitmap.add(5); |
| bitmap.add(6); |
| |
| IndexFieldNameAndTypePair name_with_type = {"test_col", nullptr}; |
| |
| Status status = predicate.evaluate(name_with_type, &iterator, 10, &bitmap); |
| |
| EXPECT_TRUE(status.ok()); |
| EXPECT_EQ(bitmap.cardinality(), 4); |
| EXPECT_TRUE(bitmap.contains(0)); |
| EXPECT_TRUE(bitmap.contains(1)); |
| EXPECT_TRUE(bitmap.contains(2)); |
| EXPECT_TRUE(bitmap.contains(5)); |
| EXPECT_FALSE(bitmap.contains(3)); |
| EXPECT_FALSE(bitmap.contains(6)); |
| EXPECT_FALSE(bitmap.contains(7)); |
| } |
| |
| } // namespace doris |