| /* |
| * 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/format/blob/blob_stats_extractor.h" |
| |
| #include <cstdint> |
| #include <vector> |
| |
| #include "arrow/api.h" |
| #include "gtest/gtest.h" |
| #include "paimon/common/data/blob_utils.h" |
| #include "paimon/defs.h" |
| #include "paimon/format/column_stats.h" |
| #include "paimon/fs/local/local_file_system.h" |
| #include "paimon/memory/memory_pool.h" |
| #include "paimon/status.h" |
| #include "paimon/testing/utils/testharness.h" |
| |
| namespace paimon::blob::test { |
| |
| class BlobStatsExtractorTest : public testing::Test { |
| public: |
| void SetUp() override { |
| pool_ = GetDefaultPool(); |
| // Create a blob schema with one blob field |
| blob_field_ = BlobUtils::ToArrowField("blob_field", false); |
| blob_schema_ = arrow::schema({blob_field_}); |
| fs_ = std::make_shared<LocalFileSystem>(); |
| } |
| |
| private: |
| std::shared_ptr<MemoryPool> pool_; |
| std::shared_ptr<arrow::Field> blob_field_; |
| std::shared_ptr<arrow::Schema> blob_schema_; |
| std::shared_ptr<FileSystem> fs_; |
| }; |
| |
| TEST_F(BlobStatsExtractorTest, TestDifferentBlobFiles) { |
| BlobStatsExtractor extractor(blob_schema_); |
| |
| std::vector<std::pair<std::string, int64_t>> test_files = { |
| {"data-d7816e8e-6c6d-4e28-9137-837cdf706350-1.blob", 3}, |
| {"data-d7816e8e-6c6d-4e28-9137-837cdf706350-2.blob", 2}, |
| {"data-d7816e8e-6c6d-4e28-9137-837cdf706350-3.blob", 4}, |
| {"data-d7816e8e-6c6d-4e28-9137-837cdf706350-4.blob", 1}}; |
| |
| for (const auto& [filename, expected_rows] : test_files) { |
| std::string blob_file_path = |
| paimon::test::GetDataDir() + "/db_with_blob.db/table_with_blob/bucket-0/" + filename; |
| |
| ASSERT_OK_AND_ASSIGN(auto stats_with_info, |
| extractor.ExtractWithFileInfo(fs_, blob_file_path, pool_)); |
| |
| // Check stats structure |
| ASSERT_EQ(1u, stats_with_info.first.size()); |
| ASSERT_TRUE(stats_with_info.first[0]); |
| ASSERT_EQ(FieldType::STRING, stats_with_info.first[0]->GetFieldType()); |
| ASSERT_EQ("min null, max null, null count null", stats_with_info.first[0]->ToString()); |
| |
| // Check row count matches expected |
| ASSERT_EQ(expected_rows, stats_with_info.second.GetRowCount()) |
| << "Row count mismatch for file: " << filename; |
| } |
| } |
| |
| TEST_F(BlobStatsExtractorTest, TestInvalidCase) { |
| std::string blob_file_path = paimon::test::GetDataDir() + |
| "/db_with_blob.db/table_with_blob/bucket-0/" |
| "data-d7816e8e-6c6d-4e28-9137-837cdf706350-1.blob"; |
| |
| // Should fail because schema has more than 1 field |
| { |
| auto int_field = arrow::field("int_field", arrow::int32()); |
| auto multi_field_schema = arrow::schema({blob_field_, int_field}); |
| BlobStatsExtractor extractor(multi_field_schema); |
| ASSERT_NOK_WITH_MSG(extractor.ExtractWithFileInfo(fs_, blob_file_path, pool_), |
| "schema field number 2 is not 1"); |
| } |
| // Should fail because field is not a blob field |
| { |
| auto string_field = arrow::field("string_field", arrow::utf8()); |
| auto non_blob_schema = arrow::schema({string_field}); |
| BlobStatsExtractor extractor(non_blob_schema); |
| ASSERT_NOK_WITH_MSG(extractor.ExtractWithFileInfo(fs_, blob_file_path, pool_), |
| "field string_field: string is not BLOB"); |
| } |
| // Should fail because file doesn't exist |
| { |
| BlobStatsExtractor extractor(blob_schema_); |
| std::string non_existent_path = "/path/that/does/not/exist.blob"; |
| ASSERT_NOK_WITH_MSG(extractor.ExtractWithFileInfo(fs_, non_existent_path, pool_), |
| "File '/path/that/does/not/exist.blob' not exists"); |
| } |
| } |
| |
| } // namespace paimon::blob::test |