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

lxy-9602 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/paimon-cpp.git


The following commit(s) were added to refs/heads/main by this push:
     new d76c2a6f fix(compaction): persist keys in lookup SST files while 
first-row merge function (#250)
d76c2a6f is described below

commit d76c2a6f9ae93ee26cd5ebbecc9adaaf451a4f94
Author: lszskye <[email protected]>
AuthorDate: Wed Aug 26 06:08:42 2026 -0700

    fix(compaction): persist keys in lookup SST files while first-row merge 
function (#250)
---
 .../lookup_merge_tree_compact_rewriter_test.cpp    | 39 ++++++++++++++++++++++
 src/paimon/core/mergetree/lookup_levels.cpp        | 14 ++------
 2 files changed, 41 insertions(+), 12 deletions(-)

diff --git 
a/src/paimon/core/mergetree/compact/lookup_merge_tree_compact_rewriter_test.cpp 
b/src/paimon/core/mergetree/compact/lookup_merge_tree_compact_rewriter_test.cpp
index bbc1608c..eeb7e0ce 100644
--- 
a/src/paimon/core/mergetree/compact/lookup_merge_tree_compact_rewriter_test.cpp
+++ 
b/src/paimon/core/mergetree/compact/lookup_merge_tree_compact_rewriter_test.cpp
@@ -484,6 +484,45 @@ TEST_F(LookupMergeTreeCompactRewriterTest, 
TestFirstRowRewrite) {
     CheckResult(compact_file_name, table_schema, "orc", expected_array);
 }
 
+TEST_F(LookupMergeTreeCompactRewriterTest, TestFirstRowLooksUpExistingKeys) {
+    std::map<std::string, std::string> options = {{Options::MERGE_ENGINE, 
"first-row"},
+                                                  {Options::FILE_FORMAT, 
"orc"}};
+    ASSERT_OK_AND_ASSIGN(CoreOptions core_options, 
CoreOptions::FromMap(options));
+    ASSERT_OK_AND_ASSIGN(auto table_path, CreateTable(options));
+    auto schema_manager = std::make_shared<SchemaManager>(fs_, table_path);
+    ASSERT_OK_AND_ASSIGN(auto table_schema, schema_manager->ReadSchema(0));
+
+    ASSERT_OK_AND_ASSIGN(auto level0_file,
+                         NewFiles(/*level=*/0, /*last_sequence_number=*/0, 
table_path, core_options,
+                                  "[[1, 111], [2, 22]]"));
+    ASSERT_OK_AND_ASSIGN(auto high_level_file, NewFiles(/*level=*/2, 
/*last_sequence_number=*/-1,
+                                                        table_path, 
core_options, "[[1, 11]]"));
+    auto processor_factory = 
std::make_shared<PersistEmptyProcessor::Factory>();
+    ASSERT_OK_AND_ASSIGN(auto lookup_levels,
+                         CreateLookupLevels<bool>(table_path, table_schema, 
processor_factory,
+                                                  
std::vector<std::shared_ptr<DataFileMeta>>{
+                                                      level0_file, 
high_level_file}));
+    ASSERT_OK_AND_ASSIGN(auto rewriter,
+                         CreateCompactRewriterForFirstRow(table_path, 
table_schema, core_options,
+                                                          
std::move(lookup_levels)));
+    ASSERT_OK_AND_ASSIGN(
+        auto runs, 
GenerateSortedRuns(std::vector<std::shared_ptr<DataFileMeta>>{level0_file}));
+    ASSERT_OK_AND_ASSIGN(auto compact_result, rewriter->Rewrite(
+                                                  /*output_level=*/1, 
/*drop_delete=*/true, runs));
+
+    ASSERT_EQ(1, compact_result.After().size());
+    ASSERT_EQ(1, compact_result.After()[0]->row_count);
+
+    auto type_with_special_fields =
+        
arrow::struct_(SpecialFields::CompleteSequenceAndValueKindField(arrow_schema_)->fields());
+    std::shared_ptr<arrow::ChunkedArray> expected;
+    
ASSERT_TRUE(arrow::ipc::internal::json::ChunkedArrayFromJSON(type_with_special_fields,
+                                                                 {"[[2, 0, 2, 
22]]"}, &expected)
+                    .ok());
+    CheckResult(table_path + "/bucket-0/" + 
compact_result.After()[0]->file_name, table_schema,
+                "orc", expected);
+}
+
 TEST_F(LookupMergeTreeCompactRewriterTest, TestFirstRowUpgrade) {
     std::map<std::string, std::string> options = {{Options::MERGE_ENGINE, 
"first-row"},
                                                   {Options::FILE_FORMAT, 
"orc"}};
diff --git a/src/paimon/core/mergetree/lookup_levels.cpp 
b/src/paimon/core/mergetree/lookup_levels.cpp
index 8b5f69e7..a931fea1 100644
--- a/src/paimon/core/mergetree/lookup_levels.cpp
+++ b/src/paimon/core/mergetree/lookup_levels.cpp
@@ -162,8 +162,8 @@ LookupLevels<T>::LookupLevels(
       lookup_store_factory_(lookup_store_factory),
       lookup_file_cache_(lookup_file_cache),
       remote_lookup_file_manager_(remote_lookup_file_manager) {
-    if constexpr (std::is_same_v<T, FilePosition>) {
-        // if T is FilePosition, only read key fields to create sst file is 
enough
+    if constexpr (std::is_same_v<T, FilePosition> || std::is_same_v<T, bool>) {
+        // FilePosition and first-row lookup do not persist values, so reading 
key fields is enough.
         value_schema_ = key_schema_;
     } else {
         value_schema_ = 
DataField::ConvertDataFieldsToArrowSchema(table_schema->Fields());
@@ -332,16 +332,6 @@ std::optional<std::string> 
LookupLevels<T>::TryToDownloadRemoteSst(
 template <typename T>
 Status LookupLevels<T>::CreateSstFileFromDataFile(const 
std::shared_ptr<DataFileMeta>& file,
                                                   const std::string& 
kv_file_path) {
-    if constexpr (std::is_same_v<T, bool>) {
-        // Short-circuit logic: if T is bool, just write empty lookup file.
-        PAIMON_ASSIGN_OR_RAISE(
-            std::shared_ptr<BloomFilter> bloom_filter,
-            LookupStoreFactory::BfGenerator(file->row_count, options_, 
pool_.get()));
-        PAIMON_ASSIGN_OR_RAISE(
-            std::unique_ptr<LookupStoreWriter> kv_writer,
-            lookup_store_factory_->CreateWriter(fs_, kv_file_path, 
bloom_filter, pool_));
-        return kv_writer->Close();
-    }
     // Prepare reader to iterate KeyValue
     PAIMON_ASSIGN_OR_RAISE(
         std::vector<std::unique_ptr<FileBatchReader>> raw_readers,

Reply via email to