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

csun5285 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new b8dc27e263b [refactor](storage) Replace record_rowids with a 
compaction rowid column (#69195)
b8dc27e263b is described below

commit b8dc27e263bf3ab791eae3310cec8d76a599e62d
Author: Chenyang Sun <[email protected]>
AuthorDate: Wed Oct 21 10:15:44 2026 +0800

    [refactor](storage) Replace record_rowids with a compaction rowid column 
(#69195)
    
    Replace `record_rowids` and separate row-location buffers with a
    temporary compaction rowid column.
    
    The temporary column keeps source row locations aligned with data rows
    during compaction and is removed before writing output segments.
---
 be/src/common/consts.h                             |   2 +
 be/src/storage/iterator/block_reader.cpp           |  47 +-------
 be/src/storage/iterator/block_reader.h             |   5 -
 be/src/storage/iterator/vcollect_iterator.cpp      |  51 --------
 be/src/storage/iterator/vcollect_iterator.h        |  22 ----
 be/src/storage/iterator/vertical_block_reader.cpp  |  45 ++-----
 be/src/storage/iterator/vertical_block_reader.h    |  10 +-
 .../storage/iterator/vertical_merge_iterator.cpp   |  64 +++-------
 be/src/storage/iterator/vertical_merge_iterator.h  |  45 ++-----
 be/src/storage/iterator/vgeneric_iterators.cpp     |  16 ---
 be/src/storage/iterator/vgeneric_iterators.h       |  28 -----
 be/src/storage/iterators.h                         |   6 -
 be/src/storage/merger.cpp                          | 131 ++++++++++++++++-----
 be/src/storage/rowset/beta_rowset_reader.cpp       |   4 +-
 be/src/storage/rowset/beta_rowset_reader.h         |   5 -
 be/src/storage/rowset/rowset_reader.h              |   3 -
 be/src/storage/rowset/rowset_reader_context.h      |   1 -
 be/src/storage/rowset/segcompaction.cpp            |   9 +-
 be/src/storage/schema.cpp                          |  14 +++
 be/src/storage/schema.h                            |   9 +-
 be/src/storage/segment/column_reader.h             |  43 +++++++
 be/src/storage/segment/count_on_index_fastpath.h   |  16 +--
 .../storage/segment/lazy_init_segment_iterator.h   |   4 -
 be/src/storage/segment/segment_iterator.cpp        |  26 ++--
 be/src/storage/segment/segment_iterator.h          |  10 +-
 be/src/storage/tablet/tablet_reader.cpp            |   1 -
 be/src/storage/tablet/tablet_reader.h              |   3 +-
 be/test/exec/scan/vgeneric_iterators_test.cpp      |  12 ++
 .../compaction/vertical_compaction_test.cpp        |   2 -
 .../block_reader_change_next_block_test.cpp        |   4 -
 be/test/storage/read_schema_test.cpp               |  18 +++
 .../segment/count_on_index_fastpath_test.cpp       |  24 +---
 .../segment_iterator_count_emit_shortcut_test.cpp  |   8 --
 33 files changed, 258 insertions(+), 430 deletions(-)

diff --git a/be/src/common/consts.h b/be/src/common/consts.h
index b6fe2c8788b..8cb9e42d6db 100644
--- a/be/src/common/consts.h
+++ b/be/src/common/consts.h
@@ -25,6 +25,8 @@ const std::string CSV = "csv";
 const std::string CSV_WITH_NAMES = "csv_with_names";
 const std::string CSV_WITH_NAMES_AND_TYPES = "csv_with_names_and_types";
 const std::string GLOBAL_ROWID_COL = "__DORIS_GLOBAL_ROWID_COL__";
+// Synthetic row-id column for compaction reads, containing each source row's 
physical RowLocation.
+const std::string COMPACTION_ROWID_COL = "__DORIS_COMPACTION_ROWID_COL__";
 const std::string ROW_STORE_COL = "__DORIS_ROW_STORE_COL__";
 const std::string DYNAMIC_COLUMN_NAME = "__DORIS_DYNAMIC_COL__";
 const std::string PARTIAL_UPDATE_AUTO_INC_COL = 
"__PARTIAL_UPDATE_AUTO_INC_COLUMN__";
diff --git a/be/src/storage/iterator/block_reader.cpp 
b/be/src/storage/iterator/block_reader.cpp
index 0012fea97c7..285cee951f2 100644
--- a/be/src/storage/iterator/block_reader.cpp
+++ b/be/src/storage/iterator/block_reader.cpp
@@ -639,13 +639,6 @@ Status BlockReader::_direct_next_block(Block* block, bool* 
eof) {
     }
     *eof = res.is<END_OF_FILE>();
     _eof = *eof;
-    if (UNLIKELY(_reader_context.record_rowids)) {
-        res = 
_vcollect_iter.current_block_row_locations(&_block_row_locations);
-        if (UNLIKELY(!res.ok() && res != Status::Error<END_OF_FILE>(""))) {
-            return res;
-        }
-        DCHECK_EQ(_block_row_locations.size(), block->rows());
-    }
     return Status::OK();
 }
 
@@ -658,17 +651,9 @@ Status BlockReader::_replace_key_next_block(Block* block, 
bool* eof) {
     auto target_block_row = 0;
     auto target_columns_guard = block->mutate_columns_scoped();
     auto& target_columns = target_columns_guard.mutable_columns();
-    // currently seq mapping only support mor table
-    // so this will not be executed for the time being
-    if (UNLIKELY(_reader_context.record_rowids)) {
-        _block_row_locations.resize(batch_max_rows());
-    }
     auto merged_row = 0;
     while (target_block_row < batch_max_rows() && !_eof) {
         RETURN_IF_ERROR(_insert_data_normal(target_columns));
-        if (UNLIKELY(_reader_context.record_rowids)) {
-            _block_row_locations[target_block_row] = 
_vcollect_iter.current_row_location();
-        }
         target_block_row++;
 
         while (!_eof) {
@@ -678,9 +663,6 @@ Status BlockReader::_replace_key_next_block(Block* block, 
bool* eof) {
             if (UNLIKELY(res.is<END_OF_FILE>())) {
                 _eof = true;
                 *eof = true;
-                if (UNLIKELY(_reader_context.record_rowids)) {
-                    _block_row_locations.resize(target_block_row);
-                }
                 break;
             }
 
@@ -700,9 +682,6 @@ Status BlockReader::_replace_key_next_block(Block* block, 
bool* eof) {
         // key, so it is safe to stop accumulating here without repeating any 
row.
         if (target_block_row % BLOCK_SIZE_CHECK_INTERVAL_ROWS == 0 &&
             _reached_byte_budget(target_columns)) {
-            if (UNLIKELY(_reader_context.record_rowids)) {
-                _block_row_locations.resize(target_block_row);
-            }
             break;
         }
     }
@@ -808,16 +787,9 @@ Status BlockReader::_unique_key_next_block(Block* block, 
bool* eof) {
     auto target_block_row = 0;
     auto target_columns_guard = block->mutate_columns_scoped();
     auto& target_columns = target_columns_guard.mutable_columns();
-    if (UNLIKELY(_reader_context.record_rowids)) {
-        _block_row_locations.resize(batch_max_rows());
-    }
 
     do {
         RETURN_IF_ERROR(_insert_data_normal(target_columns));
-
-        if (UNLIKELY(_reader_context.record_rowids)) {
-            _block_row_locations[target_block_row] = 
_vcollect_iter.current_row_location();
-        }
         target_block_row++;
 
         // the version is in reverse order, the first row is the highest 
version,
@@ -827,9 +799,6 @@ Status BlockReader::_unique_key_next_block(Block* block, 
bool* eof) {
         if (UNLIKELY(res.is<END_OF_FILE>())) {
             _eof = true;
             *eof = true;
-            if (UNLIKELY(_reader_context.record_rowids)) {
-                _block_row_locations.resize(target_block_row);
-            }
             break;
         }
 
@@ -840,9 +809,6 @@ Status BlockReader::_unique_key_next_block(Block* block, 
bool* eof) {
         // Byte-budget check: _next_row is already saved so stopping here is 
safe.
         if (target_block_row % BLOCK_SIZE_CHECK_INTERVAL_ROWS == 0 &&
             _reached_byte_budget(target_columns)) {
-            if (UNLIKELY(_reader_context.record_rowids)) {
-                _block_row_locations.resize(target_block_row);
-            }
             break;
         }
     } while (target_block_row < batch_max_rows());
@@ -860,16 +826,8 @@ Status BlockReader::_unique_key_next_block(Block* block, 
bool* eof) {
                 
reinterpret_cast<ColumnInt8*>(target_columns[delete_sign_idx].get())
                         ->get_data()
                         .data();
-        int delete_count = 0;
         for (int i = 0; i < target_block_row; ++i) {
-            bool sign = (delete_data[i] == 0);
-            filter_data[i] = sign;
-            if (UNLIKELY(!sign)) {
-                if (UNLIKELY(_reader_context.record_rowids)) {
-                    _block_row_locations[i].row_id = -1;
-                    delete_count++;
-                }
-            }
+            filter_data[i] = delete_data[i] == 0;
         }
         auto target_columns_size = target_columns.size();
         _delete_filter_column = std::move(delete_filter_column);
@@ -880,9 +838,6 @@ Status BlockReader::_unique_key_next_block(Block* block, 
bool* eof) {
         block->insert(column_with_type_and_name);
         RETURN_IF_ERROR(Block::filter_block(block, target_columns_size, 
target_columns_size));
         _stats.rows_del_filtered += target_block_row - block->rows();
-        if (UNLIKELY(_reader_context.record_rowids)) {
-            DCHECK_EQ(_block_row_locations.size(), block->rows() + 
delete_count);
-        }
     }
     return Status::OK();
 }
diff --git a/be/src/storage/iterator/block_reader.h 
b/be/src/storage/iterator/block_reader.h
index de1ad68fdcd..d3615369454 100644
--- a/be/src/storage/iterator/block_reader.h
+++ b/be/src/storage/iterator/block_reader.h
@@ -34,7 +34,6 @@
 #include "storage/iterator/vcollect_iterator.h"
 #include "storage/rowset/rowset_reader.h"
 #include "storage/tablet/tablet_reader.h"
-#include "storage/utils.h"
 
 namespace doris {
 class ColumnPredicate;
@@ -49,8 +48,6 @@ public:
 
     Status next_block_with_aggregation(Block* block, bool* eof) override;
 
-    std::vector<RowLocation> current_block_row_locations() { return 
_block_row_locations; }
-
     void update_profile(RuntimeProfile* profile) override {
         return _vcollect_iter.update_profile(profile);
     }
@@ -149,8 +146,6 @@ private:
 
     Status (BlockReader::*_next_block_func)(Block* block, bool* eof) = nullptr;
 
-    std::vector<RowLocation> _block_row_locations;
-
     ColumnPtr _delete_filter_column;
 
     bool _is_rowsets_overlapping = true;
diff --git a/be/src/storage/iterator/vcollect_iterator.cpp 
b/be/src/storage/iterator/vcollect_iterator.cpp
index 2ccc4ff06d2..f761fe84639 100644
--- a/be/src/storage/iterator/vcollect_iterator.cpp
+++ b/be/src/storage/iterator/vcollect_iterator.cpp
@@ -542,10 +542,6 @@ Status 
VCollectIterator::Level0Iterator::refresh_current_row() {
             if (res.is<END_OF_FILE>() && _is_empty()) {
                 break;
             }
-
-            if (UNLIKELY(_reader->_reader_context.record_rowids)) {
-                
RETURN_IF_ERROR(_rs_reader->current_block_row_locations(&_block_row_locations));
-            }
         }
     } while (!_is_empty());
     _ref.row_pos = -1;
@@ -591,30 +587,10 @@ Status VCollectIterator::Level0Iterator::next(Block* 
block) {
         if (res.is<END_OF_FILE>() && block->rows() == 0) {
             return Status::Error<END_OF_FILE>("");
         }
-        if (UNLIKELY(_reader->_reader_context.record_rowids)) {
-            
RETURN_IF_ERROR(_rs_reader->current_block_row_locations(&_block_row_locations));
-        }
         return Status::OK();
     }
 }
 
-RowLocation VCollectIterator::Level0Iterator::current_row_location() {
-    RowLocation& segment_row_id = _block_row_locations[_get_data_by_ref ? 
_current : _ref.row_pos];
-    return RowLocation(_rs_reader->rowset()->rowset_id(), 
segment_row_id.segment_id,
-                       segment_row_id.row_id);
-}
-
-Status VCollectIterator::Level0Iterator::current_block_row_locations(
-        std::vector<RowLocation>* block_row_locations) {
-    block_row_locations->resize(_block_row_locations.size());
-    for (auto i = 0; i < _block_row_locations.size(); i++) {
-        RowLocation& row_location = _block_row_locations[i];
-        (*block_row_locations)[i] = 
RowLocation(_rs_reader->rowset()->rowset_id(),
-                                                row_location.segment_id, 
row_location.row_id);
-    }
-    return Status::OK();
-}
-
 VCollectIterator::Level1Iterator::Level1Iterator(
         std::list<std::unique_ptr<VCollectIterator::LevelIterator>> children, 
TabletReader* reader,
         bool merge, bool is_reverse, bool skip_same)
@@ -915,14 +891,8 @@ Status 
VCollectIterator::Level1Iterator::_merge_next(Block* block) {
     size_t column_count = target_columns.size();
 
     auto batch_size = _reader->batch_max_rows();
-    if (UNLIKELY(_reader->_reader_context.record_rowids)) {
-        _block_row_locations.resize(batch_size);
-    }
     int continuous_row_in_block = 0;
     do {
-        if (UNLIKELY(_reader->_reader_context.record_rowids)) {
-            _block_row_locations[target_block_row] = 
_cur_child->current_row_location();
-        }
         ++target_block_row;
         ++continuous_row_in_block;
         // cur block finished, copy before merge_next cause merge_next will
@@ -941,9 +911,6 @@ Status VCollectIterator::Level1Iterator::_merge_next(Block* 
block) {
         }
         auto res = _merge_next(&cur_row);
         if (UNLIKELY(res.is<END_OF_FILE>())) {
-            if (UNLIKELY(_reader->_reader_context.record_rowids)) {
-                _block_row_locations.resize(target_block_row);
-            }
             return res;
         }
 
@@ -988,9 +955,6 @@ Status VCollectIterator::Level1Iterator::_merge_next(Block* 
block) {
                                                          
continuous_row_in_block);
                 }
             }
-            if (UNLIKELY(_reader->_reader_context.record_rowids)) {
-                _block_row_locations.resize(target_block_row);
-            }
             return Status::OK();
         }
     } while (true);
@@ -1018,19 +982,4 @@ Status 
VCollectIterator::Level1Iterator::_normal_next(Block* block) {
     }
 }
 
-Status VCollectIterator::Level1Iterator::current_block_row_locations(
-        std::vector<RowLocation>* block_row_locations) {
-    if (!_merge) {
-        if (UNLIKELY(_cur_child == nullptr)) {
-            block_row_locations->clear();
-            return Status::Error<END_OF_FILE>("");
-        }
-        return _cur_child->current_block_row_locations(block_row_locations);
-    } else {
-        DCHECK(_reader->_reader_context.record_rowids);
-        *block_row_locations = _block_row_locations;
-        return Status::OK();
-    }
-}
-
 } // namespace doris
diff --git a/be/src/storage/iterator/vcollect_iterator.h 
b/be/src/storage/iterator/vcollect_iterator.h
index 8518aa5bc2d..6f0f9d1a6f4 100644
--- a/be/src/storage/iterator/vcollect_iterator.h
+++ b/be/src/storage/iterator/vcollect_iterator.h
@@ -27,7 +27,6 @@
 #include "common/status.h"
 #include "storage/iterators.h"
 #include "storage/rowset/rowset_reader_context.h"
-#include "storage/utils.h"
 #ifdef USE_LIBCPP
 #include <queue>
 #else
@@ -76,12 +75,6 @@ public:
 
     bool is_merge() const { return _merge; }
 
-    RowLocation current_row_location() { return 
_inner_iter->current_row_location(); }
-
-    Status current_block_row_locations(std::vector<RowLocation>* 
block_row_locations) {
-        return _inner_iter->current_block_row_locations(block_row_locations);
-    }
-
     void update_profile(RuntimeProfile* profile) {
         if (_inner_iter != nullptr) {
             _inner_iter->update_profile(profile);
@@ -147,10 +140,6 @@ private:
 
         const inline std::vector<uint32_t>* compare_columns() const { return 
_compare_columns; }
 
-        virtual RowLocation current_row_location() = 0;
-
-        virtual Status current_block_row_locations(std::vector<RowLocation>* 
row_location) = 0;
-
         [[nodiscard]] virtual Status ensure_first_row_ref() = 0;
 
         virtual void update_profile(RuntimeProfile* profile) = 0;
@@ -221,10 +210,6 @@ private:
 
         Status next(Block* block) override;
 
-        RowLocation current_row_location() override;
-
-        Status current_block_row_locations(std::vector<RowLocation>* 
block_row_locations) override;
-
         void update_profile(RuntimeProfile* profile) override {
             if (_rs_reader != nullptr) {
                 _rs_reader->update_profile(profile);
@@ -286,7 +271,6 @@ private:
 
         int _current;
         BlockView _block_view;
-        std::vector<RowLocation> _block_row_locations;
         std::vector<bool> _row_is_same;
         bool _is_merge_iterator = false;
         bool _get_data_by_ref = false;
@@ -306,10 +290,6 @@ private:
 
         Status next(Block* block) override;
 
-        RowLocation current_row_location() override { return 
_cur_child->current_row_location(); }
-
-        Status current_block_row_locations(std::vector<RowLocation>* 
block_row_locations) override;
-
         [[nodiscard]] Status ensure_first_row_ref() override;
 
         ~Level1Iterator() override;
@@ -356,8 +336,6 @@ private:
         bool _skip_same;
         // used when `_merge == true`
         std::unique_ptr<MergeHeap> _heap;
-
-        std::vector<RowLocation> _block_row_locations;
     };
 
     std::unique_ptr<LevelIterator> _inner_iter;
diff --git a/be/src/storage/iterator/vertical_block_reader.cpp 
b/be/src/storage/iterator/vertical_block_reader.cpp
index e966ad34042..659e9494db5 100644
--- a/be/src/storage/iterator/vertical_block_reader.cpp
+++ b/be/src/storage/iterator/vertical_block_reader.cpp
@@ -102,8 +102,7 @@ Status 
VerticalBlockReader::next_block_with_aggregation(Block* block, bool* eof)
 
 Status VerticalBlockReader::_get_segment_iterators(const ReaderParams& 
read_params,
                                                    
std::vector<RowwiseIteratorUPtr>* segment_iters,
-                                                   std::vector<bool>* 
iterator_init_flag,
-                                                   std::vector<RowsetId>* 
rowset_ids) {
+                                                   std::vector<bool>* 
iterator_init_flag) {
     auto res = _capture_rs_readers(read_params);
     if (!res.ok()) {
         LOG(WARNING) << "fail to init reader when _capture_rs_readers. res:" 
<< res
@@ -154,9 +153,6 @@ Status VerticalBlockReader::_get_segment_iterators(const 
ReaderParams& read_para
                 iterator_init_flag->push_back(false);
             }
         }
-        for (size_t i = 0; i < added_iterators; ++i) {
-            rowset_ids->push_back(rowset->rowset_id());
-        }
         rs_split.rs_reader->reset_read_options();
     }
     return Status::OK();
@@ -165,27 +161,22 @@ Status VerticalBlockReader::_get_segment_iterators(const 
ReaderParams& read_para
 Status VerticalBlockReader::_init_collect_iter(const ReaderParams& read_params,
                                                CompactionSampleInfo* 
sample_info) {
     std::vector<bool> iterator_init_flag;
-    std::vector<RowsetId> rowset_ids;
     std::vector<RowwiseIteratorUPtr>* segment_iters_ptr = 
read_params.segment_iters_ptr;
     std::vector<RowwiseIteratorUPtr> iter_ptr_vector;
 
     if (!segment_iters_ptr) {
-        RETURN_IF_ERROR(_get_segment_iterators(read_params, &iter_ptr_vector, 
&iterator_init_flag,
-                                               &rowset_ids));
+        RETURN_IF_ERROR(_get_segment_iterators(read_params, &iter_ptr_vector, 
&iterator_init_flag));
         CHECK(iter_ptr_vector.size() == iterator_init_flag.size());
         segment_iters_ptr = &iter_ptr_vector;
     } else {
         for (int i = 0; i < segment_iters_ptr->size(); ++i) {
             iterator_init_flag.push_back(true);
-            RowsetId rowsetid;
-            rowset_ids.push_back(rowsetid); // TODO: _record_rowids need it
         }
         // TODO(zhangzhengyu): is it enough for a context?
         _reader_context.reader_type = read_params.reader_type;
         _reader_context.need_ordered_result = true; // TODO: should it be?
         _reader_context.is_unique = _tablet_schema->keys_type() == UNIQUE_KEYS;
         _reader_context.is_key_column_group = read_params.is_key_column_group;
-        _reader_context.record_rowids = read_params.record_rowids;
     }
 
     // build heap if key column iterator or build vertical merge iterator if 
value column
@@ -197,12 +188,12 @@ Status VerticalBlockReader::_init_collect_iter(const 
ReaderParams& read_params,
         }
         if (_tablet_schema->num_key_columns() == 0) {
             _vcollect_iter = new_vertical_fifo_merge_iterator(
-                    std::move(*segment_iters_ptr), iterator_init_flag, 
rowset_ids,
-                    _tablet_schema->keys_type(), seq_col_idx, 
_row_sources_buffer, _context_stats);
+                    std::move(*segment_iters_ptr), iterator_init_flag, 
_tablet_schema->keys_type(),
+                    seq_col_idx, _row_sources_buffer, _context_stats);
         } else {
             _vcollect_iter = new_vertical_heap_merge_iterator(
-                    std::move(*segment_iters_ptr), iterator_init_flag, 
rowset_ids,
-                    _tablet_schema->keys_type(), seq_col_idx, 
_row_sources_buffer, _context_stats,
+                    std::move(*segment_iters_ptr), iterator_init_flag, 
_tablet_schema->keys_type(),
+                    seq_col_idx, _row_sources_buffer, _context_stats,
                     read_params.key_group_cluster_key_idxes);
         }
     } else {
@@ -211,7 +202,7 @@ Status VerticalBlockReader::_init_collect_iter(const 
ReaderParams& read_params,
     }
     // init collect iterator
     StorageReadOptions opts(_stats);
-    opts.record_rowids = read_params.record_rowids;
+    opts.rowset_id = _reader_context.rowset_id;
     opts.use_insert_order_when_same = 
_reader_context.use_insert_order_when_same;
     if (read_params.batch_size > 0) {
         opts.block_row_max = cast_set<int>(read_params.batch_size);
@@ -331,13 +322,6 @@ Status VerticalBlockReader::_direct_next_block(Block* 
block, bool* eof) {
     }
     *eof = (res.is<END_OF_FILE>());
     _eof = *eof;
-    if (_reader_context.is_key_column_group && 
UNLIKELY(_reader_context.record_rowids)) {
-        res = 
_vcollect_iter->current_block_row_locations(&_block_row_locations);
-        if (UNLIKELY(!res.ok() && res != Status::Error<END_OF_FILE>(""))) {
-            return res;
-        }
-        DCHECK_EQ(_block_row_locations.size(), block->rows());
-    }
     return Status::OK();
 }
 
@@ -506,13 +490,6 @@ Status VerticalBlockReader::_unique_key_next_block(Block* 
block, bool* eof) {
         if (UNLIKELY(!res.ok() && !res.is<END_OF_FILE>())) {
             return res;
         }
-        if (UNLIKELY(_reader_context.record_rowids)) {
-            auto ret = 
_vcollect_iter->current_block_row_locations(&_block_row_locations);
-            if (UNLIKELY(!ret.ok() && !ret.is<END_OF_FILE>())) {
-                return res;
-            }
-            DCHECK_EQ(_block_row_locations.size(), block->rows());
-        }
 
         if (_row_sources_buffer->buffered_size() < row_buffer_size_start) {
             row_buffer_size_start = 0;
@@ -549,7 +526,6 @@ Status VerticalBlockReader::_unique_key_next_block(Block* 
block, bool* eof) {
                             .data();
 
             int cur_row = 0;
-            int delete_count = 0;
             while (cur_row < block_rows) {
                 if (_row_sources_buffer->get_agg_flag(row_source_idx)) {
                     row_source_idx++;
@@ -559,10 +535,6 @@ Status VerticalBlockReader::_unique_key_next_block(Block* 
block, bool* eof) {
                 filter_data[cur_row] = sign;
                 if (UNLIKELY(!sign)) {
                     _row_sources_buffer->set_agg_flag(row_source_idx, true);
-                    if (UNLIKELY(_reader_context.record_rowids)) {
-                        _block_row_locations[cur_row].row_id = -1;
-                        delete_count++;
-                    }
                 }
                 cur_row++;
                 row_source_idx++;
@@ -580,9 +552,6 @@ Status VerticalBlockReader::_unique_key_next_block(Block* 
block, bool* eof) {
             block->insert(column_with_type_and_name);
             RETURN_IF_ERROR(Block::filter_block(block, column_to_keep, 
column_to_keep));
             _stats.rows_del_filtered += block_rows - block->rows();
-            if (UNLIKELY(_reader_context.record_rowids)) {
-                DCHECK_EQ(_block_row_locations.size(), block->rows() + 
delete_count);
-            }
         }
 
         size_t filtered_rows_in_rs_buffer = 0;
diff --git a/be/src/storage/iterator/vertical_block_reader.h 
b/be/src/storage/iterator/vertical_block_reader.h
index 543f3f734d2..b849f2bbe24 100644
--- a/be/src/storage/iterator/vertical_block_reader.h
+++ b/be/src/storage/iterator/vertical_block_reader.h
@@ -35,13 +35,10 @@
 #include "storage/iterators.h"
 #include "storage/tablet/tablet.h"
 #include "storage/tablet/tablet_reader.h"
-#include "storage/utils.h"
 
 #pragma once
 
 namespace doris {
-struct RowsetId;
-
 class RowSourcesBuffer;
 class RowsetMeta;
 struct RowBatch;
@@ -69,8 +66,6 @@ public:
         return _vcollect_iter->merged_rows();
     }
 
-    std::vector<RowLocation> current_block_row_locations() { return 
_block_row_locations; }
-
     static uint64_t nextId;
 
 private:
@@ -95,8 +90,7 @@ private:
 
     Status _get_segment_iterators(const ReaderParams& read_params,
                                   std::vector<RowwiseIteratorUPtr>* 
segment_iters,
-                                  std::vector<bool>* iterator_init_flag,
-                                  std::vector<RowsetId>* rowset_ids);
+                                  std::vector<bool>* iterator_init_flag);
 
     void _init_agg_state(const ReaderParams& read_params);
     void _append_agg_data(MutableColumns& columns);
@@ -143,8 +137,6 @@ private:
 
     phmap::flat_hash_map<const Block*, std::vector<std::pair<int, int>>> 
_temp_ref_map;
 
-    std::vector<RowLocation> _block_row_locations;
-
     // For sparse column compaction optimization
     // Set from reader_params.enable_sparse_optimization (calculated in 
Merger::vertical_merge_rowsets)
     bool _enable_sparse_optimization = false;
diff --git a/be/src/storage/iterator/vertical_merge_iterator.cpp 
b/be/src/storage/iterator/vertical_merge_iterator.cpp
index 2adbbb754b4..22faa1adb62 100644
--- a/be/src/storage/iterator/vertical_merge_iterator.cpp
+++ b/be/src/storage/iterator/vertical_merge_iterator.cpp
@@ -417,7 +417,6 @@ Status VerticalMergeIteratorContext::init(const 
StorageReadOptions& opts,
         return Status::OK();
     }
     _block_row_max = opts.block_row_max;
-    _record_rowids = opts.record_rowids;
     RETURN_IF_ERROR(_load_next_block());
     if (sample_info != nullptr) {
         sample_info->bytes += bytes();
@@ -517,14 +516,6 @@ Status VerticalMergeIteratorContext::_load_next_block() {
                 return st;
             }
         }
-        if (UNLIKELY(_record_rowids)) {
-            
RETURN_IF_ERROR(_iter->current_block_row_locations(&_block_row_locations));
-            for (auto i = 0; i < _block_row_locations.size(); i++) {
-                RowLocation& row_location = _block_row_locations[i];
-                _block_row_locations[i] =
-                        RowLocation(_rowset_id, row_location.segment_id, 
row_location.row_id);
-            }
-        }
     } while (_block->rows() == 0);
     _index_in_block = -1;
     _valid = true;
@@ -536,9 +527,6 @@ Status VerticalHeapMergeIterator::next_batch(Block* block) {
     size_t row_idx = 0;
     VerticalMergeIteratorContext* pre_ctx = nullptr;
     std::vector<RowSource> tmp_row_sources;
-    if (UNLIKELY(_record_rowids)) {
-        _block_row_locations.resize(_block_row_max);
-    }
     while (_get_size(block) < _block_row_max) {
         if (_merge_heap.empty()) {
             VLOG_NOTICE << "_merge_heap empty";
@@ -569,9 +557,6 @@ Status VerticalHeapMergeIterator::next_batch(Block* block) {
                 }
                 pre_ctx = ctx;
             }
-            if (UNLIKELY(_record_rowids)) {
-                _block_row_locations[row_idx] = ctx->current_row_location();
-            }
             row_idx++;
             if (ctx->is_cur_block_finished() || row_idx >= _block_row_max) {
                 // current block finished, ctx not advance
@@ -608,9 +593,6 @@ Status VerticalHeapMergeIterator::next_batch(Block* block) {
     if (!_merge_heap.empty()) {
         return Status::OK();
     }
-    if (UNLIKELY(_record_rowids)) {
-        _block_row_locations.resize(row_idx);
-    }
     return Status::EndOfFile("no more data in segment");
 }
 
@@ -618,7 +600,6 @@ Status VerticalHeapMergeIterator::init(const 
StorageReadOptions& opts,
                                        CompactionSampleInfo* sample_info) {
     DCHECK(_origin_iters.size() == _iterator_init_flags.size());
     _opts.emplace(opts);
-    _record_rowids = opts.record_rowids;
     if (_origin_iters.empty()) {
         return Status::OK();
     }
@@ -628,7 +609,7 @@ Status VerticalHeapMergeIterator::init(const 
StorageReadOptions& opts,
     for (size_t seg_order = 0; seg_order < num_iters; ++seg_order) {
         auto& iter = _origin_iters[seg_order];
         auto ctx = std::make_unique<VerticalMergeIteratorContext>(
-                std::move(iter), _rowset_ids[seg_order], seg_order, 
_seq_col_idx, _context_stats,
+                std::move(iter), seg_order, _seq_col_idx, _context_stats,
                 opts.use_insert_order_when_same, _key_group_cluster_key_idxes);
         _ori_iter_ctx.push_back(std::move(ctx));
     }
@@ -662,9 +643,6 @@ Status VerticalHeapMergeIterator::init(const 
StorageReadOptions& opts,
 Status VerticalFifoMergeIterator::next_batch(Block* block) {
     size_t row_idx = 0;
     std::vector<RowSource> tmp_row_sources;
-    if (UNLIKELY(_record_rowids)) {
-        _block_row_locations.resize(_block_row_max);
-    }
     while (_get_size(block) < _block_row_max) {
         if (_cur_iter_ctx == nullptr) {
             VLOG_NOTICE << "_merge_list empty";
@@ -675,9 +653,6 @@ Status VerticalFifoMergeIterator::next_batch(Block* block) {
 
         // Fifo only for duplicate no key
         _cur_iter_ctx->add_cur_batch();
-        if (UNLIKELY(_record_rowids)) {
-            _block_row_locations[row_idx] = 
_cur_iter_ctx->current_row_location();
-        }
         row_idx++;
         if (_cur_iter_ctx->is_cur_block_finished() || row_idx >= 
_block_row_max) {
             // current block finished, ctx not advance
@@ -694,9 +669,9 @@ Status VerticalFifoMergeIterator::next_batch(Block* block) {
                  cur_order++) {
                 auto& next_iter = _origin_iters[cur_order];
                 std::unique_ptr<VerticalMergeIteratorContext> next_ctx(
-                        new VerticalMergeIteratorContext(
-                                std::move(next_iter), _rowset_ids[cur_order], 
cur_order,
-                                _seq_col_idx, _context_stats, 
_opts->use_insert_order_when_same));
+                        new VerticalMergeIteratorContext(std::move(next_iter), 
cur_order,
+                                                         _seq_col_idx, 
_context_stats,
+                                                         
_opts->use_insert_order_when_same));
                 RETURN_IF_ERROR(next_ctx->init(*_opts));
                 if (next_ctx->valid()) {
                     _cur_iter_ctx.swap(next_ctx);
@@ -710,9 +685,6 @@ Status VerticalFifoMergeIterator::next_batch(Block* block) {
     if (_cur_iter_ctx) {
         return Status::OK();
     }
-    if (UNLIKELY(_record_rowids)) {
-        _block_row_locations.resize(row_idx);
-    }
     return Status::EndOfFile("no more data in segment");
 }
 
@@ -721,7 +693,6 @@ Status VerticalFifoMergeIterator::init(const 
StorageReadOptions& opts,
     DCHECK(_origin_iters.size() == _iterator_init_flags.size());
     DCHECK(_keys_type == KeysType::DUP_KEYS);
     _opts.emplace(opts);
-    _record_rowids = opts.record_rowids;
     if (_origin_iters.empty()) {
         return Status::OK();
     }
@@ -735,9 +706,9 @@ Status VerticalFifoMergeIterator::init(const 
StorageReadOptions& opts,
     // will not be pushed into heap, we should init next one util we find a 
valid iter
     // so this rowset can work in heap
     for (auto& iter : _origin_iters) {
-        std::unique_ptr<VerticalMergeIteratorContext> ctx(new 
VerticalMergeIteratorContext(
-                std::move(iter), _rowset_ids[seg_order], seg_order, 
_seq_col_idx, _context_stats,
-                opts.use_insert_order_when_same));
+        std::unique_ptr<VerticalMergeIteratorContext> ctx(
+                new VerticalMergeIteratorContext(std::move(iter), seg_order, 
_seq_col_idx,
+                                                 _context_stats, 
opts.use_insert_order_when_same));
         RETURN_IF_ERROR(ctx->init(opts, sample_info));
         if (!ctx->valid()) {
             ++seg_order;
@@ -993,9 +964,8 @@ Status VerticalMaskMergeIterator::init(const 
StorageReadOptions& opts,
         return Status::OK();
     }
     _schema = &(*_origin_iters.begin())->schema();
-    RowsetId rs_id;
     for (auto& iter : _origin_iters) {
-        auto ctx = 
std::make_unique<VerticalMergeIteratorContext>(std::move(iter), rs_id, -1, -1,
+        auto ctx = 
std::make_unique<VerticalMergeIteratorContext>(std::move(iter), -1, -1,
                                                                   
_context_stats);
         _origin_iter_ctx.push_back(std::move(ctx));
     }
@@ -1009,21 +979,21 @@ Status VerticalMaskMergeIterator::init(const 
StorageReadOptions& opts,
 // interfaces to create vertical merge iterator
 std::shared_ptr<RowwiseIterator> new_vertical_heap_merge_iterator(
         std::vector<RowwiseIteratorUPtr>&& inputs, const std::vector<bool>& 
iterator_init_flag,
-        const std::vector<RowsetId>& rowset_ids, KeysType keys_type, int32_t 
seq_col_idx,
-        RowSourcesBuffer* row_sources, VerticalCompactionContextStats* 
context_stats,
+        KeysType keys_type, int32_t seq_col_idx, RowSourcesBuffer* row_sources,
+        VerticalCompactionContextStats* context_stats,
         std::vector<uint32_t> key_group_cluster_key_idxes) {
-    return std::make_shared<VerticalHeapMergeIterator>(
-            std::move(inputs), iterator_init_flag, rowset_ids, keys_type, 
seq_col_idx, row_sources,
-            context_stats, key_group_cluster_key_idxes);
+    return std::make_shared<VerticalHeapMergeIterator>(std::move(inputs), 
iterator_init_flag,
+                                                       keys_type, seq_col_idx, 
row_sources,
+                                                       context_stats, 
key_group_cluster_key_idxes);
 }
 
 std::shared_ptr<RowwiseIterator> new_vertical_fifo_merge_iterator(
         std::vector<RowwiseIteratorUPtr>&& inputs, const std::vector<bool>& 
iterator_init_flag,
-        const std::vector<RowsetId>& rowset_ids, KeysType keys_type, int32_t 
seq_col_idx,
-        RowSourcesBuffer* row_sources, VerticalCompactionContextStats* 
context_stats) {
+        KeysType keys_type, int32_t seq_col_idx, RowSourcesBuffer* row_sources,
+        VerticalCompactionContextStats* context_stats) {
     return std::make_shared<VerticalFifoMergeIterator>(std::move(inputs), 
iterator_init_flag,
-                                                       rowset_ids, keys_type, 
seq_col_idx,
-                                                       row_sources, 
context_stats);
+                                                       keys_type, seq_col_idx, 
row_sources,
+                                                       context_stats);
 }
 
 std::shared_ptr<RowwiseIterator> new_vertical_mask_merge_iterator(
diff --git a/be/src/storage/iterator/vertical_merge_iterator.h 
b/be/src/storage/iterator/vertical_merge_iterator.h
index c3b378ae3e6..2a8fb8289f7 100644
--- a/be/src/storage/iterator/vertical_merge_iterator.h
+++ b/be/src/storage/iterator/vertical_merge_iterator.h
@@ -35,7 +35,6 @@
 #include "storage/iterators.h"
 #include "storage/olap_common.h"
 #include "storage/schema.h"
-#include "storage/utils.h"
 
 #pragma once
 
@@ -162,12 +161,11 @@ private:
 // takes ownership of rowwise iterator
 class VerticalMergeIteratorContext {
 public:
-    VerticalMergeIteratorContext(RowwiseIteratorUPtr&& iter, RowsetId 
rowset_id, uint32_t order,
-                                 int32_t seq_col_idx, 
VerticalCompactionContextStats* context_stats,
+    VerticalMergeIteratorContext(RowwiseIteratorUPtr&& iter, uint32_t order, 
int32_t seq_col_idx,
+                                 VerticalCompactionContextStats* context_stats,
                                  bool use_insert_order_when_same = false,
                                  std::vector<uint32_t> 
key_group_cluster_key_idxes = {})
             : _iter(std::move(iter)),
-              _rowset_id(rowset_id),
               _order(order),
               _seq_col_idx(seq_col_idx),
               _num_key_columns(_iter->schema().num_key_columns()),
@@ -217,10 +215,6 @@ public:
         ref->row_pos = _index_in_block;
     }
     bool inited() const { return _inited; }
-    RowLocation current_row_location() {
-        DCHECK(_record_rowids);
-        return _block_row_locations[_index_in_block];
-    }
 
     size_t bytes() {
         if (_block) {
@@ -257,7 +251,6 @@ private:
     void _mark_inactive();
 
     RowwiseIteratorUPtr _iter;
-    RowsetId _rowset_id;
 
     // segment order, used to compare key
     const uint32_t _order = 0;
@@ -282,8 +275,6 @@ private:
     // use to identify whether it's first block load from RowwiseIterator
     bool _is_first_row = true;
     bool _is_active_context_counted = false;
-    bool _record_rowids = false;
-    std::vector<RowLocation> _block_row_locations;
 };
 
 // --------------- VerticalHeapMergeIterator ------------- //
@@ -291,14 +282,12 @@ class VerticalHeapMergeIterator : public RowwiseIterator {
 public:
     // VerticalMergeIterator takes the ownership of input iterators
     VerticalHeapMergeIterator(std::vector<RowwiseIteratorUPtr>&& iters,
-                              std::vector<bool> iterator_init_flags,
-                              std::vector<RowsetId> rowset_ids, KeysType 
keys_type,
+                              std::vector<bool> iterator_init_flags, KeysType 
keys_type,
                               int32_t seq_col_idx, RowSourcesBuffer* 
row_sources_buf,
                               VerticalCompactionContextStats* context_stats,
                               std::vector<uint32_t> 
key_group_cluster_key_idxes)
             : _origin_iters(std::move(iters)),
               _iterator_init_flags(std::move(iterator_init_flags)),
-              _rowset_ids(std::move(rowset_ids)),
               _keys_type(keys_type),
               _seq_col_idx(seq_col_idx),
               _row_sources_buf(row_sources_buf),
@@ -313,11 +302,6 @@ public:
     Status next_batch(Block* block) override;
     const ReadSchema& schema() const override { return *_schema; }
     uint64_t merged_rows() const override { return _merged_rows; }
-    Status current_block_row_locations(std::vector<RowLocation>* 
block_row_locations) override {
-        DCHECK(_record_rowids);
-        *block_row_locations = _block_row_locations;
-        return Status::OK();
-    }
 
 private:
     int64_t _get_size(Block* block) { return block->rows(); }
@@ -325,7 +309,6 @@ private:
     // It will be released after '_merge_heap' has been built.
     std::vector<RowwiseIteratorUPtr> _origin_iters;
     std::vector<bool> _iterator_init_flags;
-    std::vector<RowsetId> _rowset_ids;
 
     const ReadSchema* _schema = nullptr;
 
@@ -351,8 +334,6 @@ private:
     // StorageReadOptions is supplied by init(), after this iterator is 
constructed, and must be
     // retained for lazily initialized segment contexts.
     std::optional<StorageReadOptions> _opts;
-    bool _record_rowids = false;
-    std::vector<RowLocation> _block_row_locations;
     std::vector<uint32_t> _key_group_cluster_key_idxes;
 };
 
@@ -361,13 +342,11 @@ class VerticalFifoMergeIterator : public RowwiseIterator {
 public:
     // VerticalFifoMergeIterator takes the ownership of input iterators
     VerticalFifoMergeIterator(std::vector<RowwiseIteratorUPtr>&& iters,
-                              std::vector<bool> iterator_init_flags,
-                              std::vector<RowsetId> rowset_ids, KeysType 
keys_type,
+                              std::vector<bool> iterator_init_flags, KeysType 
keys_type,
                               int32_t seq_col_idx, RowSourcesBuffer* 
row_sources_buf,
                               VerticalCompactionContextStats* context_stats)
             : _origin_iters(std::move(iters)),
               _iterator_init_flags(std::move(iterator_init_flags)),
-              _rowset_ids(std::move(rowset_ids)),
               _keys_type(keys_type),
               _seq_col_idx(seq_col_idx),
               _row_sources_buf(row_sources_buf),
@@ -381,11 +360,6 @@ public:
     Status next_batch(Block* block) override;
     const ReadSchema& schema() const override { return *_schema; }
     uint64_t merged_rows() const override { return _merged_rows; }
-    Status current_block_row_locations(std::vector<RowLocation>* 
block_row_locations) override {
-        DCHECK(_record_rowids);
-        *block_row_locations = _block_row_locations;
-        return Status::OK();
-    }
 
 private:
     int64_t _get_size(Block* block) { return block->rows(); }
@@ -393,7 +367,6 @@ private:
     // It will be released after '_merge_heap' has been built.
     std::vector<RowwiseIteratorUPtr> _origin_iters;
     std::vector<bool> _iterator_init_flags;
-    std::vector<RowsetId> _rowset_ids;
 
     const ReadSchema* _schema = nullptr;
 
@@ -407,8 +380,6 @@ private:
     // StorageReadOptions is supplied by init(), after this iterator is 
constructed, and must be
     // retained for lazily initialized segment contexts.
     std::optional<StorageReadOptions> _opts;
-    bool _record_rowids = false;
-    std::vector<RowLocation> _block_row_locations;
 };
 
 // --------------- RowBatch for batch optimization ------------- //
@@ -481,14 +452,14 @@ private:
 // segment merge iterator
 std::shared_ptr<RowwiseIterator> new_vertical_heap_merge_iterator(
         std::vector<RowwiseIteratorUPtr>&& inputs, const std::vector<bool>& 
iterator_init_flag,
-        const std::vector<RowsetId>& rowset_ids, KeysType key_type, int32_t 
seq_col_idx,
-        RowSourcesBuffer* row_sources_buf, VerticalCompactionContextStats* 
context_stats,
+        KeysType key_type, int32_t seq_col_idx, RowSourcesBuffer* 
row_sources_buf,
+        VerticalCompactionContextStats* context_stats,
         std::vector<uint32_t> key_group_cluster_key_idxes);
 
 std::shared_ptr<RowwiseIterator> new_vertical_fifo_merge_iterator(
         std::vector<RowwiseIteratorUPtr>&& inputs, const std::vector<bool>& 
iterator_init_flag,
-        const std::vector<RowsetId>& rowset_ids, KeysType key_type, int32_t 
seq_col_idx,
-        RowSourcesBuffer* row_sources_buf, VerticalCompactionContextStats* 
context_stats);
+        KeysType key_type, int32_t seq_col_idx, RowSourcesBuffer* 
row_sources_buf,
+        VerticalCompactionContextStats* context_stats);
 
 std::shared_ptr<RowwiseIterator> new_vertical_mask_merge_iterator(
         std::vector<RowwiseIteratorUPtr>&& inputs, RowSourcesBuffer* 
row_sources_buf,
diff --git a/be/src/storage/iterator/vgeneric_iterators.cpp 
b/be/src/storage/iterator/vgeneric_iterators.cpp
index fa75c4bb638..b3c7358f418 100644
--- a/be/src/storage/iterator/vgeneric_iterators.cpp
+++ b/be/src/storage/iterator/vgeneric_iterators.cpp
@@ -277,7 +277,6 @@ Status VAutoIncrementIterator::init(const 
StorageReadOptions& opts) {
 
 Status VMergeIteratorContext::init(const StorageReadOptions& opts) {
     _block_row_max = opts.block_row_max;
-    _record_rowids = opts.record_rowids;
     RETURN_IF_ERROR(_load_next_block());
     if (valid()) {
         RETURN_IF_ERROR(_validate_compare_contract(opts));
@@ -370,9 +369,6 @@ Status VMergeIteratorContext::_load_next_block() {
                 return st;
             }
         }
-        if (UNLIKELY(_record_rowids)) {
-            
RETURN_IF_ERROR(_iter->current_block_row_locations(&_block_row_locations));
-        }
     } while (_block->rows() == 0);
     _index_in_block = -1;
     _valid = true;
@@ -383,8 +379,6 @@ Status VMergeIterator::init(const StorageReadOptions& opts) 
{
     if (_origin_iters.empty()) {
         return Status::OK();
     }
-    _record_rowids = opts.record_rowids;
-
     for (auto& iter : _origin_iters) {
         auto ctx = std::make_shared<VMergeIteratorContext>(
                 std::move(iter), _sequence_id_idx, _is_unique, _is_reverse,
@@ -423,8 +417,6 @@ public:
 
     const ReadSchema& schema() const override { return *_read_schema; }
 
-    Status current_block_row_locations(std::vector<RowLocation>* locations) 
override;
-
     void update_profile(RuntimeProfile* profile) override {
         if (_cur_iter != nullptr) {
             _cur_iter->update_profile(profile);
@@ -471,14 +463,6 @@ Status VUnionIterator::next_batch(Block* block) {
     return Status::EndOfFile("End of VUnionIterator");
 }
 
-Status VUnionIterator::current_block_row_locations(std::vector<RowLocation>* 
locations) {
-    if (!_cur_iter) {
-        locations->clear();
-        return Status::EndOfFile("End of VUnionIterator");
-    }
-    return _cur_iter->current_block_row_locations(locations);
-}
-
 RowwiseIteratorUPtr new_merge_iterator(std::vector<RowwiseIteratorUPtr>&& 
inputs,
                                        int sequence_id_idx, bool is_unique, 
bool is_reverse,
                                        uint64_t* merged_rows, ReadSchemaSPtr 
read_schema,
diff --git a/be/src/storage/iterator/vgeneric_iterators.h 
b/be/src/storage/iterator/vgeneric_iterators.h
index 22012471067..9176f1daf9d 100644
--- a/be/src/storage/iterator/vgeneric_iterators.h
+++ b/be/src/storage/iterator/vgeneric_iterators.h
@@ -34,7 +34,6 @@
 #include "storage/iterators.h"
 #include "storage/schema.h"
 #include "storage/segment/column_reader.h"
-#include "storage/utils.h"
 
 namespace doris {
 class RuntimeProfile;
@@ -123,11 +122,6 @@ public:
     Status copy_rows(BlockWithSameBit* block, bool advanced = true);
     Status copy_rows(BlockView* view, bool advanced = true);
 
-    RowLocation current_row_location() {
-        DCHECK(_record_rowids);
-        return _block_row_locations[_index_in_block];
-    }
-
     // Advance internal row index to next valid row
     // Return error if error happens
     // Don't call this when valid() is false, action is undefined
@@ -189,8 +183,6 @@ private:
     const ReadSchemaSPtr _read_schema;
     int _num_key_columns;
     std::vector<uint32_t>* _compare_columns;
-    std::vector<RowLocation> _block_row_locations;
-    bool _record_rowids = false;
     size_t _cur_batch_num = 0;
 
     // used to store data load from iterator->next_batch(Block*)
@@ -226,12 +218,6 @@ public:
 
     const ReadSchema& schema() const override { return *_read_schema; }
 
-    Status current_block_row_locations(std::vector<RowLocation>* 
block_row_locations) override {
-        DCHECK(_record_rowids);
-        *block_row_locations = _block_row_locations;
-        return Status::OK();
-    }
-
     void update_profile(RuntimeProfile* profile) override {
         if (!_origin_iters.empty()) {
             _origin_iters[0]->update_profile(profile);
@@ -247,9 +233,6 @@ private:
 
     template <typename T>
     Status _next_batch(T* block) {
-        if (UNLIKELY(_record_rowids)) {
-            _block_row_locations.resize(_block_row_max);
-        }
         size_t row_idx = 0;
         std::shared_ptr<VMergeIteratorContext> pre_ctx;
         while (_get_size(block) < _block_row_max) {
@@ -269,9 +252,6 @@ private:
                     pre_ctx = ctx;
                 }
                 pre_ctx->set_pre_ctx_same(ctx.get());
-                if (UNLIKELY(_record_rowids)) {
-                    _block_row_locations[row_idx] = 
ctx->current_row_location();
-                }
                 row_idx++;
                 if (ctx->is_cur_block_finished() || row_idx >= _block_row_max) 
{
                     // current block finished, ctx not advance
@@ -296,12 +276,6 @@ private:
         if (!_merge_heap.empty()) {
             return Status::OK();
         }
-        // Still last batch needs to be processed
-
-        if (UNLIKELY(_record_rowids)) {
-            _block_row_locations.resize(row_idx);
-        }
-
         return Status::EndOfFile("no more data in segment");
     }
 
@@ -331,8 +305,6 @@ private:
     // See VMergeIteratorContext::_small_seq_first; forwarded to every context 
on init().
     bool _small_seq_first = false;
     uint64_t* _merged_rows = nullptr;
-    bool _record_rowids = false;
-    std::vector<RowLocation> _block_row_locations;
 };
 
 // Create a merge iterator for input iterators. Merge iterator will merge
diff --git a/be/src/storage/iterators.h b/be/src/storage/iterators.h
index 3c7cc40a1d0..5c369b685b2 100644
--- a/be/src/storage/iterators.h
+++ b/be/src/storage/iterators.h
@@ -130,7 +130,6 @@ public:
     // The compaction output schema's variant path layout; null outside a 
variant compaction.
     VariantCompactionPathsSPtr variant_compaction_paths;
     bool enable_unique_key_merge_on_write = false;
-    bool record_rowids = false;
     std::vector<int> topn_filter_source_node_ids;
     // used for special optimization for query : ORDER BY key DESC LIMIT n
     bool read_orderby_key_reverse = false;
@@ -243,11 +242,6 @@ public:
 
     virtual bool is_merge_iterator() const { return false; }
 
-    virtual Status current_block_row_locations(std::vector<RowLocation>* 
block_row_locations) {
-        return Status::InternalError("should not reach here, current class: " +
-                                     demangle(typeid(*this).name()));
-    }
-
     // return schema for this Iterator
     virtual const ReadSchema& schema() const = 0;
 
diff --git a/be/src/storage/merger.cpp b/be/src/storage/merger.cpp
index a743e90467e..b89ec47b064 100644
--- a/be/src/storage/merger.cpp
+++ b/be/src/storage/merger.cpp
@@ -23,6 +23,7 @@
 #include <unistd.h>
 
 #include <algorithm>
+#include <cstring>
 #include <iterator>
 #include <memory>
 #include <mutex>
@@ -37,7 +38,9 @@
 #include "common/config.h"
 #include "common/logging.h"
 #include "common/status.h"
+#include "core/assert_cast.h"
 #include "core/block/block.h"
+#include "core/column/column_string.h"
 #include "runtime/runtime_state.h"
 #include "storage/iterator/block_reader.h"
 #include "storage/iterator/vertical_block_reader.h"
@@ -96,6 +99,30 @@ Status set_key_range(TabletReader::ReaderParams* 
reader_params, const TabletSche
     return Status::OK();
 }
 
+// Extract row locations from the rowid column and remove it from the output 
block.
+Block prepare_compaction_output_block(const Block& block, int32_t 
rowid_ordinal,
+                                      std::vector<RowLocation>* row_locations) 
{
+    DORIS_CHECK_GE(rowid_ordinal, 0);
+    auto ordinal = static_cast<size_t>(rowid_ordinal);
+    DORIS_CHECK_LT(ordinal, block.columns());
+    const auto& rowid_column =
+            assert_cast<const 
ColumnString&>(*block.get_by_position(ordinal).column);
+    DORIS_CHECK_EQ(rowid_column.size(), block.rows());
+    row_locations->clear();
+    row_locations->reserve(rowid_column.size());
+    for (size_t i = 0; i < rowid_column.size(); ++i) {
+        const auto rowid = rowid_column.get_data_at(i);
+        DORIS_CHECK_EQ(rowid.size, sizeof(RowLocation));
+        RowLocation location;
+        std::memcpy(&location, rowid.data, sizeof(RowLocation));
+        row_locations->emplace_back(location);
+    }
+
+    Block output_block = block;
+    output_block.erase(ordinal);
+    return output_block;
+}
+
 } // namespace
 
 Status Merger::vmerge_rowsets(BaseTabletSPtr tablet, ReaderType reader_type,
@@ -148,16 +175,24 @@ Status Merger::vmerge_rowsets(BaseTabletSPtr tablet, 
ReaderType reader_type,
         reader_params.delete_bitmap = 
tablet->tablet_meta()->delete_bitmap_ptr();
     }
 
-    if (stats_output && stats_output->rowid_conversion) {
-        reader_params.record_rowids = true;
+    const bool convert_rowids =
+            stats_output != nullptr && stats_output->rowid_conversion != 
nullptr;
+    if (convert_rowids) {
+        DORIS_CHECK_NE(cur_tablet_schema.keys_type(), KeysType::AGG_KEYS);
+        DORIS_CHECK(!cur_tablet_schema.has_seq_map());
         reader_params.rowid_conversion = stats_output->rowid_conversion;
         
stats_output->rowid_conversion->set_dst_rowset_id(dst_rowset_writer->rowset_id());
     }
 
     reader_params.read_schema = 
std::make_shared<ReadSchema>(cur_tablet_schema.columns());
+    if (convert_rowids) {
+        reader_params.read_schema->append_compaction_rowid_column();
+    }
     RETURN_IF_ERROR(reader.init(reader_params));
 
     Block block = reader_params.read_schema->create_read_block();
+    const auto rowid_ordinal = reader_params.read_schema->rowid_ordinal();
+    std::vector<RowLocation> row_locations;
     size_t output_rows = 0;
     bool eof = false;
     while (!eof && !ExecEnv::GetInstance()->storage_engine().stopped()) {
@@ -175,15 +210,23 @@ Status Merger::vmerge_rowsets(BaseTabletSPtr tablet, 
ReaderType reader_type,
         
RETURN_NOT_OK_STATUS_WITH_WARN(reader.next_block_with_aggregation(&block, &eof),
                                        "failed to read next block when merging 
rowsets of tablet " +
                                                
std::to_string(tablet->tablet_id()));
-        RETURN_NOT_OK_STATUS_WITH_WARN(dst_rowset_writer->add_block(&block),
-                                       "failed to write block when merging 
rowsets of tablet " +
-                                               
std::to_string(tablet->tablet_id()));
-
-        if (reader_params.record_rowids && block.rows() > 0) {
-            std::vector<uint32_t> segment_num_rows;
-            
RETURN_IF_ERROR(dst_rowset_writer->get_segment_num_rows(&segment_num_rows));
-            RETURN_IF_ERROR(stats_output->rowid_conversion->add(
-                    reader.current_block_row_locations(), segment_num_rows));
+        if (convert_rowids) {
+            Block output_block =
+                    prepare_compaction_output_block(block, rowid_ordinal, 
&row_locations);
+            
RETURN_NOT_OK_STATUS_WITH_WARN(dst_rowset_writer->add_block(&output_block),
+                                           "failed to write block when merging 
rowsets of tablet " +
+                                                   
std::to_string(tablet->tablet_id()));
+
+            if (block.rows() > 0) {
+                std::vector<uint32_t> segment_num_rows;
+                
RETURN_IF_ERROR(dst_rowset_writer->get_segment_num_rows(&segment_num_rows));
+                RETURN_IF_ERROR(
+                        stats_output->rowid_conversion->add(row_locations, 
segment_num_rows));
+            }
+        } else {
+            
RETURN_NOT_OK_STATUS_WITH_WARN(dst_rowset_writer->add_block(&block),
+                                           "failed to write block when merging 
rowsets of tablet " +
+                                                   
std::to_string(tablet->tablet_id()));
         }
 
         output_rows += block.rows();
@@ -344,18 +387,26 @@ Status Merger::vertical_compact_one_group(
         reader_params.delete_bitmap = 
tablet->tablet_meta()->delete_bitmap_ptr();
     }
 
-    if (is_key && stats_output && stats_output->rowid_conversion) {
-        reader_params.record_rowids = true;
+    const bool convert_rowids =
+            is_key && stats_output != nullptr && 
stats_output->rowid_conversion != nullptr;
+    if (convert_rowids) {
+        DORIS_CHECK_NE(tablet_schema.keys_type(), KeysType::AGG_KEYS);
+        DORIS_CHECK(!tablet_schema.has_seq_map());
         reader_params.rowid_conversion = stats_output->rowid_conversion;
         
stats_output->rowid_conversion->set_dst_rowset_id(dst_rowset_writer->rowset_id());
     }
 
     reader_params.read_schema = std::make_shared<ReadSchema>(
             project_columns_by_ordinal(tablet_schema.columns(), column_group));
+    if (convert_rowids) {
+        reader_params.read_schema->append_compaction_rowid_column();
+    }
     reader_params.batch_size = batch_size;
     RETURN_IF_ERROR(reader.init(reader_params, sample_info));
 
     Block block = reader_params.read_schema->create_read_block();
+    const auto rowid_ordinal = reader_params.read_schema->rowid_ordinal();
+    std::vector<RowLocation> row_locations;
     size_t output_rows = 0;
     bool eof = false;
     while (!eof && !ExecEnv::GetInstance()->storage_engine().stopped()) {
@@ -372,17 +423,27 @@ Status Merger::vertical_compact_one_group(
         
RETURN_NOT_OK_STATUS_WITH_WARN(reader.next_block_with_aggregation(&block, &eof),
                                        "failed to read next block when merging 
rowsets of tablet " +
                                                
std::to_string(tablet->tablet_id()));
-        RETURN_NOT_OK_STATUS_WITH_WARN(
-                dst_rowset_writer->add_columns(&block, column_group, is_key, 
max_rows_per_segment,
-                                               has_cluster_key),
-                "failed to write block when merging rowsets of tablet " +
-                        std::to_string(tablet->tablet_id()));
-
-        if (is_key && reader_params.record_rowids && block.rows() > 0) {
-            std::vector<uint32_t> segment_num_rows;
-            
RETURN_IF_ERROR(dst_rowset_writer->get_segment_num_rows(&segment_num_rows));
-            RETURN_IF_ERROR(stats_output->rowid_conversion->add(
-                    reader.current_block_row_locations(), segment_num_rows));
+        if (convert_rowids) {
+            Block output_block =
+                    prepare_compaction_output_block(block, rowid_ordinal, 
&row_locations);
+            RETURN_NOT_OK_STATUS_WITH_WARN(
+                    dst_rowset_writer->add_columns(&output_block, 
column_group, is_key,
+                                                   max_rows_per_segment, 
has_cluster_key),
+                    "failed to write block when merging rowsets of tablet " +
+                            std::to_string(tablet->tablet_id()));
+
+            if (block.rows() > 0) {
+                std::vector<uint32_t> segment_num_rows;
+                
RETURN_IF_ERROR(dst_rowset_writer->get_segment_num_rows(&segment_num_rows));
+                RETURN_IF_ERROR(
+                        stats_output->rowid_conversion->add(row_locations, 
segment_num_rows));
+            }
+        } else {
+            RETURN_NOT_OK_STATUS_WITH_WARN(
+                    dst_rowset_writer->add_columns(&block, column_group, 
is_key,
+                                                   max_rows_per_segment, 
has_cluster_key),
+                    "failed to write block when merging rowsets of tablet " +
+                            std::to_string(tablet->tablet_id()));
         }
         output_rows += block.rows();
         block.clear_column_data();
@@ -420,8 +481,10 @@ Status Merger::vertical_compact_one_group(
         RowSourcesBuffer* row_source_buf, VerticalBlockReader& 
src_block_reader,
         segment_v2::VerticalSegmentWriter& dst_segment_writer, Statistics* 
stats_output,
         uint64_t* index_size, KeyBoundsPB& key_bounds, SimpleRowIdConversion* 
rowid_conversion) {
-    // TODO: record_rowids
     Block block = read_schema.create_read_block();
+    const auto rowid_ordinal = read_schema.rowid_ordinal();
+    const bool convert_rowids = is_key && rowid_conversion != nullptr;
+    std::vector<RowLocation> row_locations;
     size_t output_rows = 0;
     bool eof = false;
     while (!eof && !ExecEnv::GetInstance()->storage_engine().stopped()) {
@@ -432,12 +495,18 @@ Status Merger::vertical_compact_one_group(
         if (!block.rows()) {
             break;
         }
-        RETURN_NOT_OK_STATUS_WITH_WARN(dst_segment_writer.append_block(&block, 
0, block.rows()),
-                                       "failed to write block when merging 
rowsets of tablet " +
-                                               std::to_string(tablet_id));
-
-        if (is_key && rowid_conversion != nullptr) {
-            
rowid_conversion->add(src_block_reader.current_block_row_locations());
+        if (convert_rowids) {
+            Block output_block =
+                    prepare_compaction_output_block(block, rowid_ordinal, 
&row_locations);
+            RETURN_NOT_OK_STATUS_WITH_WARN(
+                    dst_segment_writer.append_block(&output_block, 0, 
output_block.rows()),
+                    "failed to write block when merging rowsets of tablet " +
+                            std::to_string(tablet_id));
+            rowid_conversion->add(row_locations);
+        } else {
+            
RETURN_NOT_OK_STATUS_WITH_WARN(dst_segment_writer.append_block(&block, 0, 
block.rows()),
+                                           "failed to write block when merging 
rowsets of tablet " +
+                                                   std::to_string(tablet_id));
         }
         output_rows += block.rows();
         block.clear_column_data();
diff --git a/be/src/storage/rowset/beta_rowset_reader.cpp 
b/be/src/storage/rowset/beta_rowset_reader.cpp
index 4bc3bce0b05..bed808d461c 100644
--- a/be/src/storage/rowset/beta_rowset_reader.cpp
+++ b/be/src/storage/rowset/beta_rowset_reader.cpp
@@ -203,7 +203,6 @@ Status 
BetaRowsetReader::get_segment_iterators(RowsetReaderContext* read_context
     _read_options.variant_compaction_paths = 
_read_context->variant_compaction_paths;
     _read_options.enable_unique_key_merge_on_write =
             _read_context->enable_unique_key_merge_on_write;
-    _read_options.record_rowids = _read_context->record_rowids;
     _read_options.topn_filter_source_node_ids = 
_read_context->topn_filter_source_node_ids;
     _read_options.read_orderby_key_reverse = 
_read_context->read_orderby_key_reverse;
     _read_options.use_insert_order_when_same = 
_read_context->use_insert_order_when_same;
@@ -268,7 +267,8 @@ Status 
BetaRowsetReader::get_segment_iterators(RowsetReaderContext* read_context
         seg_start = 0;
         seg_end = segment_count;
     }
-    if (_read_context->record_rowids && _read_context->rowid_conversion) {
+    if (_read_context->rowid_conversion != nullptr) {
+        DORIS_CHECK_GE(read_schema->rowid_ordinal(), 0);
         // init segment rowid map for rowid conversion
         std::vector<uint32_t> segment_rows;
         RETURN_IF_ERROR(_rowset->get_segment_num_rows(&segment_rows, 
should_use_cache, _stats));
diff --git a/be/src/storage/rowset/beta_rowset_reader.h 
b/be/src/storage/rowset/beta_rowset_reader.h
index c223a62089d..cf490f1c4b9 100644
--- a/be/src/storage/rowset/beta_rowset_reader.h
+++ b/be/src/storage/rowset/beta_rowset_reader.h
@@ -38,7 +38,6 @@
 namespace doris {
 class RuntimeProfile;
 class ReadSchema;
-struct RowLocation;
 struct RowsetReaderContext;
 
 class BetaRowsetReader : public RowsetReader {
@@ -85,10 +84,6 @@ public:
 
     RowsetTypePB type() const override { return RowsetTypePB::BETA_ROWSET; }
 
-    Status current_block_row_locations(std::vector<RowLocation>* locations) 
override {
-        return _iterator->current_block_row_locations(locations);
-    }
-
     void update_profile(RuntimeProfile* profile) override;
 
     RowsetReaderSharedPtr clone() override;
diff --git a/be/src/storage/rowset/rowset_reader.h 
b/be/src/storage/rowset/rowset_reader.h
index 559aec4a52b..8d49278d78d 100644
--- a/be/src/storage/rowset/rowset_reader.h
+++ b/be/src/storage/rowset/rowset_reader.h
@@ -80,9 +80,6 @@ public:
     virtual RowsetTypePB type() const = 0;
 
     virtual int64_t newest_write_timestamp() = 0;
-    virtual Status current_block_row_locations(std::vector<RowLocation>* 
locations) {
-        return Status::NotSupported("to be implemented");
-    }
 
     virtual void update_profile(RuntimeProfile* profile) = 0;
 
diff --git a/be/src/storage/rowset/rowset_reader_context.h 
b/be/src/storage/rowset/rowset_reader_context.h
index 8bf7dfc2a0f..17b5938a680 100644
--- a/be/src/storage/rowset/rowset_reader_context.h
+++ b/be/src/storage/rowset/rowset_reader_context.h
@@ -87,7 +87,6 @@ struct RowsetReaderContext {
     // for unique key merge on write
     bool enable_unique_key_merge_on_write = false;
     DeleteBitmapPtr delete_bitmap = nullptr;
-    bool record_rowids = false;
     RowIdConversion* rowid_conversion = nullptr;
     bool is_key_column_group = false;
     // column unique ids referenced by output/orderby exprs (NOT ordinals)
diff --git a/be/src/storage/rowset/segcompaction.cpp 
b/be/src/storage/rowset/segcompaction.cpp
index 6670dd4b34a..116e7b10f2e 100644
--- a/be/src/storage/rowset/segcompaction.cpp
+++ b/be/src/storage/rowset/segcompaction.cpp
@@ -88,10 +88,9 @@ Status SegcompactionWorker::_get_segcompaction_reader(
         bool is_key, std::vector<uint32_t>& key_group_cluster_key_idxes,
         std::unique_ptr<VerticalBlockReader>* reader) {
     const auto& ctx = _writer->_context;
-    bool record_rowids = need_convert_delete_bitmap() && is_key;
     StorageReadOptions read_options(stat);
     read_options.use_page_cache = false;
-    read_options.record_rowids = record_rowids;
+    read_options.rowset_id = ctx.rowset_id;
     if (!tablet->tablet_schema()->cluster_key_uids().empty()) {
         DeleteBitmapPtr delete_bitmap = 
std::make_shared<DeleteBitmap>(tablet->tablet_id());
         RETURN_IF_ERROR(tablet->calc_delete_bitmap_between_segments(
@@ -117,7 +116,7 @@ Status SegcompactionWorker::_get_segcompaction_reader(
         seg_iterators.push_back(std::move(iter));
         segment_rows.emplace(seg_ptr->id(), seg_ptr->num_rows());
     }
-    if (record_rowids && _rowid_conversion != nullptr) {
+    if (is_key && _rowid_conversion != nullptr) {
         _rowid_conversion->reset_segment_map(segment_rows);
     }
 
@@ -132,7 +131,6 @@ Status SegcompactionWorker::_get_segcompaction_reader(
     reader_params.read_schema = std::move(read_schema);
     reader_params.is_key_column_group = is_key;
     reader_params.use_page_cache = false;
-    reader_params.record_rowids = record_rowids;
     reader_params.key_group_cluster_key_idxes = key_group_cluster_key_idxes;
     return (*reader)->init(reader_params, nullptr);
 }
@@ -303,6 +301,9 @@ Status 
SegcompactionWorker::_do_compact_segments(SegCompactionCandidatesSharedPt
         RETURN_IF_ERROR(writer->init(column_ids, is_key));
         auto schema = std::make_shared<ReadSchema>(
                 project_columns_by_ordinal(ctx.tablet_schema->columns(), 
column_ids));
+        if (is_key && _rowid_conversion != nullptr) {
+            schema->append_compaction_rowid_column();
+        }
         OlapReaderStatistics reader_stats;
         std::unique_ptr<VerticalBlockReader> reader;
         auto s = _get_segcompaction_reader(segments, tablet, schema, 
reader_stats, row_sources_buf,
diff --git a/be/src/storage/schema.cpp b/be/src/storage/schema.cpp
index 5c4e0901ff6..642430b0de5 100644
--- a/be/src/storage/schema.cpp
+++ b/be/src/storage/schema.cpp
@@ -17,6 +17,7 @@
 
 #include "storage/schema.h"
 
+#include <limits>
 #include <ranges>
 #include <utility>
 
@@ -181,6 +182,19 @@ void 
ReadSchema::append_dropped_columns(std::vector<TabletColumn> columns) {
     }
 }
 
+void ReadSchema::append_compaction_rowid_column() {
+    DORIS_CHECK_EQ(_num_block_columns, _read_columns.size());
+    DORIS_CHECK_EQ(_rowid_ordinal, -1);
+    auto column = std::make_shared<TabletColumn>(
+            FieldAggregationMethod::OLAP_FIELD_AGGREGATION_NONE, 
FieldType::OLAP_FIELD_TYPE_STRING,
+            false, std::numeric_limits<int32_t>::max(), sizeof(RowLocation));
+    column->set_name(BeConsts::COMPACTION_ROWID_COL);
+    _read_columns.emplace_back(column);
+    _read_types.emplace_back(column->get_vec_type());
+    ++_num_block_columns;
+    _init_descriptors();
+}
+
 void ReadSchema::_init_before_column_ordinals() {
     std::unordered_map<std::string_view, ColumnId> name_to_ordinal;
     name_to_ordinal.reserve(_num_block_columns);
diff --git a/be/src/storage/schema.h b/be/src/storage/schema.h
index e5f9397eb2f..21b6a614b2c 100644
--- a/be/src/storage/schema.h
+++ b/be/src/storage/schema.h
@@ -85,6 +85,10 @@ public:
     // Append private historical delete-predicate columns without extending 
the Block prefix.
     void append_dropped_columns(std::vector<TabletColumn> columns);
 
+    // Append COMPACTION_ROWID_COL to the Block prefix before any historical 
delete columns.
+    // It carries each source row's physical location through compaction 
filtering and merge.
+    void append_compaction_rowid_column();
+
     // Create caller-visible Blocks from the FE-slot prefix.
     Block create_read_block() const;
 
@@ -143,7 +147,7 @@ public:
     int32_t delete_sign_ordinal() const { return _delete_sign_ordinal; }
     // Sequence column used to choose the winning row during merge.
     int32_t sequence_ordinal() const { return _sequence_ordinal; }
-    // Synthetic row identifier returned by rowid-producing scans.
+    // Synthetic row identifier returned by queries or carried by compaction 
reads.
     int32_t rowid_ordinal() const { return _rowid_ordinal; }
     // Rowset version synthesized for single-version reads.
     int32_t version_ordinal() const { return _version_ordinal; }
@@ -199,7 +203,8 @@ private:
             if (col.name() == SEQUENCE_COL) {
                 _sequence_ordinal = i;
             }
-            if (col.name().starts_with(BeConsts::GLOBAL_ROWID_COL)) {
+            if (col.name().starts_with(BeConsts::GLOBAL_ROWID_COL) ||
+                col.name() == BeConsts::COMPACTION_ROWID_COL) {
                 _rowid_ordinal = i;
             }
             if (col.name() == VERSION_COL) {
diff --git a/be/src/storage/segment/column_reader.h 
b/be/src/storage/segment/column_reader.h
index dfbd236c3d3..1b3e0709b39 100644
--- a/be/src/storage/segment/column_reader.h
+++ b/be/src/storage/segment/column_reader.h
@@ -926,6 +926,49 @@ private:
     Status _seek_by_offsets(ordinal_t ord);
 };
 
+// Reads COMPACTION_ROWID_COL by synthesizing the physical source RowLocation 
during compaction.
+// Its payload is RowLocation, distinct from the query-facing 
GlobalRowLoacationV2 format.
+class CompactionRowIdColumnIterator : public ColumnIterator {
+public:
+    CompactionRowIdColumnIterator(RowsetId rowset_id, uint32_t segment_id)
+            : _rowset_id(rowset_id), _segment_id(segment_id) {}
+
+    Status seek_to_ordinal(ordinal_t ord_idx) override {
+        _current_rowid = cast_set<uint32_t>(ord_idx);
+        return Status::OK();
+    }
+
+    Status next_batch(size_t* n, MutableColumnPtr& dst) {
+        bool has_null;
+        return next_batch(n, dst, &has_null);
+    }
+
+    Status next_batch(size_t* n, MutableColumnPtr& dst, bool* has_null) 
override {
+        for (size_t i = 0; i < *n; ++i) {
+            RowLocation location(_rowset_id, _segment_id, 
cast_set<uint32_t>(_current_rowid + i));
+            dst->insert_data(reinterpret_cast<const char*>(&location), 
sizeof(RowLocation));
+        }
+        _current_rowid += *n;
+        return Status::OK();
+    }
+
+    Status read_by_rowids(const rowid_t* rowids, const size_t count,
+                          MutableColumnPtr& dst) override {
+        for (size_t i = 0; i < count; ++i) {
+            RowLocation location(_rowset_id, _segment_id, 
cast_set<uint32_t>(rowids[i]));
+            dst->insert_data(reinterpret_cast<const char*>(&location), 
sizeof(RowLocation));
+        }
+        return Status::OK();
+    }
+
+    ordinal_t get_current_ordinal() const override { return _current_rowid; }
+
+private:
+    uint32_t _current_rowid = 0;
+    RowsetId _rowset_id;
+    uint32_t _segment_id = 0;
+};
+
 class RowIdColumnIteratorV2 : public ColumnIterator {
 public:
     RowIdColumnIteratorV2(uint8_t version, int64_t backend_id, uint32_t 
file_id)
diff --git a/be/src/storage/segment/count_on_index_fastpath.h 
b/be/src/storage/segment/count_on_index_fastpath.h
index 0c370d800c2..4e115aee6c3 100644
--- a/be/src/storage/segment/count_on_index_fastpath.h
+++ b/be/src/storage/segment/count_on_index_fastpath.h
@@ -38,7 +38,7 @@
 //      the per-segment delete bitmap AFTER the index apply, and delete
 //      predicates filter later -- a fabricated id range cannot participate in
 //      either subtraction),
-//   4. nothing consumes REAL row ids (rowid recording, ANN topn, BM25
+//   4. nothing consumes REAL row ids (ANN topn, BM25
 //      scoring, virtual columns), and
 //   5. rows are emitted as defaults without reading column data at the
 //      fabricated ids (the COUNT_ON_INDEX no-read-data contract must be
@@ -66,7 +66,6 @@ struct CountOnIndexFastpathFacts {
     bool has_external_row_ranges = false;
     bool row_bitmap_is_full = false;
     // (4) consumers of real row ids.
-    bool record_rowids = false;
     bool has_ann_topn = false;
     bool has_score_runtime = false;
     // (5) rows must be emitted as defaults (no data read at fabricated ids).
@@ -78,9 +77,8 @@ inline bool count_on_index_fastpath_safe(const 
CountOnIndexFastpathFacts& f) {
     return f.is_count_on_index_agg && !f.has_column_predicates && 
f.common_expr_count == 1 &&
            f.single_expr_is_match_pred && !f.has_virtual_column_exprs && 
!f.has_delete_predicates &&
            f.segment_delete_bitmap_empty && !f.has_col_id_predicates && 
!f.has_topn_filters &&
-           !f.has_external_row_ranges && f.row_bitmap_is_full && 
!f.record_rowids &&
-           !f.has_ann_topn && !f.has_score_runtime && 
f.no_need_read_data_opt_enabled &&
-           f.keys_type_supported;
+           !f.has_external_row_ranges && f.row_bitmap_is_full && 
!f.has_ann_topn &&
+           !f.has_score_runtime && f.no_need_read_data_opt_enabled && 
f.keys_type_supported;
 }
 
 // G03 count-emission shortcut guard (functional core, unit-testable without a
@@ -100,8 +98,7 @@ inline bool count_on_index_fastpath_safe(const 
CountOnIndexFastpathFacts& f) {
 //   2. no evaluation stage survives (vec/short-circuit/expr eval, leftover
 //      column predicates or common exprs, delete predicates, lazy
 //      materialization),
-//   3. nothing consumes real row ids or per-row values (virtual columns,
-//      rowid recording),
+//   3. nothing consumes real row ids or per-row values (virtual columns),
 //   4. batch accounting is a pure countdown (no read limit, no reverse
 //      key-ordered read, no condition-cache writes), and
 //   5. the block is exactly the read schema and EVERY column would take a
@@ -125,7 +122,6 @@ struct CountEmitShortcutFacts {
     bool has_delete_predicates = false;
     // (3) consumers of real row ids / per-row values.
     bool has_virtual_columns = false;
-    bool record_rowids = false;
     // (4) batch accounting must be a pure countdown.
     bool has_read_limit = false;
     bool read_orderby_key_reverse = false;
@@ -138,8 +134,8 @@ struct CountEmitShortcutFacts {
 inline bool count_emit_shortcut_safe(const CountEmitShortcutFacts& f) {
     return f.count_fastpath_hit && !f.needs_vec_eval && !f.needs_short_eval && 
!f.needs_expr_eval &&
            !f.has_remaining_col_predicates && !f.has_remaining_common_exprs &&
-           !f.has_delete_predicates && !f.has_virtual_columns && 
!f.record_rowids &&
-           !f.has_read_limit && !f.read_orderby_key_reverse && 
!f.has_condition_cache_digest &&
+           !f.has_delete_predicates && !f.has_virtual_columns && 
!f.has_read_limit &&
+           !f.read_orderby_key_reverse && !f.has_condition_cache_digest &&
            f.block_shape_matches_schema && f.all_columns_emit_defaults;
 }
 
diff --git a/be/src/storage/segment/lazy_init_segment_iterator.h 
b/be/src/storage/segment/lazy_init_segment_iterator.h
index 9d2a89039b0..381f71162ba 100644
--- a/be/src/storage/segment/lazy_init_segment_iterator.h
+++ b/be/src/storage/segment/lazy_init_segment_iterator.h
@@ -50,10 +50,6 @@ public:
 
     const ReadSchema& schema() const override { return *_schema; }
 
-    Status current_block_row_locations(std::vector<RowLocation>* locations) 
override {
-        return _inner_iterator->current_block_row_locations(locations);
-    }
-
     void update_profile(RuntimeProfile* profile) override {
         if (_inner_iterator != nullptr) {
             _inner_iterator->update_profile(profile);
diff --git a/be/src/storage/segment/segment_iterator.cpp 
b/be/src/storage/segment/segment_iterator.cpp
index e2ec36a0804..f5895aac905 100644
--- a/be/src/storage/segment/segment_iterator.cpp
+++ b/be/src/storage/segment/segment_iterator.cpp
@@ -1551,7 +1551,6 @@ bool SegmentIterator::_count_on_index_fastpath_safe() 
const {
     // ranges): the fabricated [0, df) range only counts correctly against a
     // full [0, num_rows) bitmap.
     facts.row_bitmap_is_full = _row_bitmap.cardinality() == 
uint64_t(num_rows());
-    facts.record_rowids = _opts.record_rowids;
     facts.has_ann_topn = _opts.ann_topn_runtime != nullptr;
     facts.has_score_runtime = _score_runtime != nullptr;
     // Mirror of the _need_read_data preamble: rows must be emitted as 
defaults,
@@ -1611,7 +1610,6 @@ bool 
SegmentIterator::_should_engage_count_emit_shortcut(const Block* block) {
     facts.has_delete_predicates = _opts.delete_condition_predicates != nullptr 
&&
                                   
_opts.delete_condition_predicates->num_of_column_predicate() > 0;
     facts.has_virtual_columns = !_virtual_column_exprs.empty();
-    facts.record_rowids = _opts.record_rowids || _schema->rowid_ordinal() >= 0;
     facts.has_read_limit = _opts.read_limit > 0;
     facts.read_orderby_key_reverse = _opts.read_orderby_key_reverse;
     facts.has_condition_cache_digest = _opts.condition_cache_digest != 0;
@@ -1889,6 +1887,13 @@ Status SegmentIterator::_init_column_iterators() {
         if (cid >= _schema->num_block_columns() && 
!_column_states[cid].has_delete_pred) {
             continue;
         }
+        if (_schema->column(cid)->name() == BeConsts::COMPACTION_ROWID_COL) {
+            // Compaction reads synthesize source locations rather than 
reading a stored column.
+            _column_iterators[cid].reset(
+                    new CompactionRowIdColumnIterator(_opts.rowset_id, 
_segment->id()));
+            continue;
+        }
+
         if 
(_schema->column(cid)->name().starts_with(BeConsts::GLOBAL_ROWID_COL)) {
             auto& id_file_map = _opts.runtime_state->get_id_file_map();
             uint32_t file_id = 
id_file_map->get_file_mapping_id(std::make_shared<FileMapping>(
@@ -3404,23 +3409,6 @@ void 
SegmentIterator::_convert_dict_code_for_predicate_if_necessary_impl(
     }
 }
 
-Status SegmentIterator::current_block_row_locations(std::vector<RowLocation>* 
block_row_locations) {
-    DCHECK(_opts.record_rowids);
-    DCHECK_GE(_block_rowids.size(), _selected_size);
-    block_row_locations->resize(_selected_size);
-    uint32_t sid = segment_id();
-    if (!_is_need_vec_eval && !_is_need_short_eval && !_is_need_expr_eval) {
-        for (auto i = 0; i < _selected_size; i++) {
-            (*block_row_locations)[i] = RowLocation(sid, _block_rowids[i]);
-        }
-    } else {
-        for (auto i = 0; i < _selected_size; i++) {
-            (*block_row_locations)[i] = RowLocation(sid, 
_block_rowids[_sel_rowid_idx[i]]);
-        }
-    }
-    return Status::OK();
-}
-
 Status SegmentIterator::_construct_compound_expr_context() {
     ColumnIteratorOptions iter_opts {
             .use_page_cache = _opts.use_page_cache,
diff --git a/be/src/storage/segment/segment_iterator.h 
b/be/src/storage/segment/segment_iterator.h
index c693c93dee6..1366dbd07a8 100644
--- a/be/src/storage/segment/segment_iterator.h
+++ b/be/src/storage/segment/segment_iterator.h
@@ -61,7 +61,6 @@ namespace doris {
 
 class VExpr;
 class VExprContext;
-struct RowLocation;
 
 namespace segment_v2 {
 
@@ -81,12 +80,6 @@ public:
     [[nodiscard]] Status init(const StorageReadOptions& opts) override;
     [[nodiscard]] Status next_batch(Block* block) override;
 
-    // Get current block row locations. This function should be called
-    // after the `next_batch` function.
-    // Only vectorized version is supported.
-    [[nodiscard]] Status current_block_row_locations(
-            std::vector<RowLocation>* block_row_locations) override;
-
     const ReadSchema& schema() const override { return *_schema; }
     uint64_t data_id() const override { return _segment->id(); }
 
@@ -444,8 +437,9 @@ private:
 
     io::FileReaderSPtr _file_reader;
 
-    // used for compaction, record selectd rowids of current batch
+    // Number of selected rows in the current batch.
     uint16_t _selected_size;
+    // Positions of selected rows within the current batch.
     std::vector<uint16_t> _sel_rowid_idx;
 
     // Rows already produced by this iterator. Used together with
diff --git a/be/src/storage/tablet/tablet_reader.cpp 
b/be/src/storage/tablet/tablet_reader.cpp
index 295c44650d8..1b78fe010e6 100644
--- a/be/src/storage/tablet/tablet_reader.cpp
+++ b/be/src/storage/tablet/tablet_reader.cpp
@@ -184,7 +184,6 @@ Status TabletReader::_capture_rs_readers(const 
ReaderParams& read_params) {
     _reader_context.enable_unique_key_merge_on_write = 
tablet()->enable_unique_key_merge_on_write();
     _reader_context.enable_mor_value_predicate_pushdown =
             read_params.enable_mor_value_predicate_pushdown;
-    _reader_context.record_rowids = read_params.record_rowids;
     _reader_context.rowid_conversion = read_params.rowid_conversion;
     _reader_context.is_key_column_group = read_params.is_key_column_group;
     _reader_context.common_expr_ctxs_push_down = 
read_params.common_expr_ctxs_push_down;
diff --git a/be/src/storage/tablet/tablet_reader.h 
b/be/src/storage/tablet/tablet_reader.h
index 82f3a707fdb..90434934ea2 100644
--- a/be/src/storage/tablet/tablet_reader.h
+++ b/be/src/storage/tablet/tablet_reader.h
@@ -177,8 +177,7 @@ public:
         TPushAggOp::type push_down_agg_type_opt = TPushAggOp::NONE;
         VExprContextSPtrs common_expr_ctxs_push_down;
 
-        // used for compaction to record row ids
-        bool record_rowids = false;
+        // Used by compaction to initialize source segment row-id mappings.
         RowIdConversion* rowid_conversion = nullptr;
         std::vector<int> topn_filter_source_node_ids;
         // used for special optimization for query : ORDER BY key LIMIT n
diff --git a/be/test/exec/scan/vgeneric_iterators_test.cpp 
b/be/test/exec/scan/vgeneric_iterators_test.cpp
index c5d3b819d39..06a59def649 100644
--- a/be/test/exec/scan/vgeneric_iterators_test.cpp
+++ b/be/test/exec/scan/vgeneric_iterators_test.cpp
@@ -20,6 +20,7 @@
 #include <gtest/gtest-message.h>
 #include <gtest/gtest-test-part.h>
 
+#include <cstring>
 #include <memory>
 #include <vector>
 
@@ -569,6 +570,10 @@ public:
                     *(double*)data = j == _seq_col_idx ? 
_seq_col_rows_returned : 1;
                     data_len = sizeof(double);
                     break;
+                case FieldType::OLAP_FIELD_TYPE_STRING:
+                    std::memcpy(data, &_seq_col_rows_returned, 
sizeof(_seq_col_rows_returned));
+                    data_len = sizeof(_seq_col_rows_returned);
+                    break;
                 default:
                     break;
                 }
@@ -597,6 +602,7 @@ public:
 TEST(VGenericIteratorsTest, MergeWithSeqColumn) {
     EXPECT_TRUE(1);
     auto schema = create_schema();
+    schema.append_compaction_rowid_column();
     auto output_schema = std::make_shared<ReadSchema>(schema);
     std::vector<RowwiseIteratorUPtr> inputs;
 
@@ -637,6 +643,12 @@ TEST(VGenericIteratorsTest, MergeWithSeqColumn) {
     auto seq_col = block.get_by_position(seq_column_id).column;
     size_t actual_value = (*seq_col)[0].get<TYPE_BIGINT>();
     EXPECT_EQ(seg_iter_num - 1, actual_value);
+
+    const auto rowid = 
block.get_by_position(schema.rowid_ordinal()).column->get_data_at(0);
+    ASSERT_EQ(sizeof(int), rowid.size);
+    int rowid_value = 0;
+    std::memcpy(&rowid_value, rowid.data, sizeof(rowid_value));
+    EXPECT_EQ(seg_iter_num - 1, rowid_value);
 }
 
 // Mirror of MergeWithSeqColumn but with small_seq_first=true.
diff --git a/be/test/storage/compaction/vertical_compaction_test.cpp 
b/be/test/storage/compaction/vertical_compaction_test.cpp
index 70706ebfa5f..353783958c4 100644
--- a/be/test/storage/compaction/vertical_compaction_test.cpp
+++ b/be/test/storage/compaction/vertical_compaction_test.cpp
@@ -1592,8 +1592,6 @@ TEST_F(VerticalCompactionTest, TestAggKeyVerticalMerge) {
     // merge input rowset
     TabletSharedPtr tablet = create_tablet(*tablet_schema, false);
     Merger::Statistics stats;
-    RowIdConversion rowid_conversion;
-    stats.rowid_conversion = &rowid_conversion;
     ASSERT_EQ("0", 
bvar::Variable::describe_exposed("vertical_compaction_active_segment_contexts"));
     auto s = Merger::vertical_merge_rowsets(tablet, 
ReaderType::READER_BASE_COMPACTION,
                                             *tablet_schema, input_rs_readers,
diff --git a/be/test/storage/iterator/block_reader_change_next_block_test.cpp 
b/be/test/storage/iterator/block_reader_change_next_block_test.cpp
index 1a9fbcbb1ee..8292848b53b 100644
--- a/be/test/storage/iterator/block_reader_change_next_block_test.cpp
+++ b/be/test/storage/iterator/block_reader_change_next_block_test.cpp
@@ -320,10 +320,6 @@ public:
 
     Status next(Block* /*block*/) override { return 
Status::Error<END_OF_FILE>(""); }
 
-    RowLocation current_row_location() override { return {}; }
-    Status current_block_row_locations(std::vector<RowLocation>* /*loc*/) 
override {
-        return Status::OK();
-    }
     Status ensure_first_row_ref() override { return Status::OK(); }
     void update_profile(RuntimeProfile* /*profile*/) override {}
 
diff --git a/be/test/storage/read_schema_test.cpp 
b/be/test/storage/read_schema_test.cpp
index 6cde75ad573..c9ca5428852 100644
--- a/be/test/storage/read_schema_test.cpp
+++ b/be/test/storage/read_schema_test.cpp
@@ -462,4 +462,22 @@ TEST(ReadSchemaTest, TabletColumnFromSlotKeepsTypeDetails) 
{
     EXPECT_TRUE(variant_column.variant_enable_doc_mode());
 }
 
+TEST(ReadSchemaTest, 
AppendedCompactionRowIdExtendsReadBlockBeforeDroppedColumns) {
+    std::vector<TabletColumnPtr> read_columns {create_int_column(10, "k", 
true)};
+    auto dropped_column = create_int_column(11, "dropped");
+    ReadSchema read_schema(std::move(read_columns));
+
+    read_schema.append_compaction_rowid_column();
+    read_schema.append_dropped_columns({*dropped_column});
+
+    EXPECT_EQ(1, read_schema.rowid_ordinal());
+    EXPECT_EQ(2, read_schema.num_block_columns());
+    EXPECT_EQ(3, read_schema.num_read_columns());
+    EXPECT_EQ(2, read_schema.ordinal_by_uid(11));
+    Block block = read_schema.create_read_block();
+    ASSERT_EQ(2, block.columns());
+    EXPECT_EQ(BeConsts::COMPACTION_ROWID_COL, block.get_by_position(1).name);
+    EXPECT_EQ(PrimitiveType::TYPE_STRING, 
block.get_by_position(1).type->get_primitive_type());
+}
+
 } // namespace doris
diff --git a/be/test/storage/segment/count_on_index_fastpath_test.cpp 
b/be/test/storage/segment/count_on_index_fastpath_test.cpp
index 9c87ef32b68..1c8b3a748d9 100644
--- a/be/test/storage/segment/count_on_index_fastpath_test.cpp
+++ b/be/test/storage/segment/count_on_index_fastpath_test.cpp
@@ -45,7 +45,6 @@ CountOnIndexFastpathFacts safe_facts() {
     f.has_topn_filters = false;
     f.has_external_row_ranges = false;
     f.row_bitmap_is_full = true;
-    f.record_rowids = false;
     f.has_ann_topn = false;
     f.has_score_runtime = false;
     f.no_need_read_data_opt_enabled = true;
@@ -143,11 +142,6 @@ TEST(CountOnIndexFastpath, PrunedRowSpaceVetoes) {
 
 // Consumers of REAL row ids veto.
 TEST(CountOnIndexFastpath, RowIdConsumersVeto) {
-    {
-        auto f = safe_facts();
-        f.record_rowids = true;
-        EXPECT_FALSE(count_on_index_fastpath_safe(f));
-    }
     {
         auto f = safe_facts();
         f.has_ann_topn = true;
@@ -195,7 +189,6 @@ CountEmitShortcutFacts safe_emit_facts() {
     f.has_remaining_common_exprs = false;
     f.has_delete_predicates = false;
     f.has_virtual_columns = false;
-    f.record_rowids = false;
     f.has_read_limit = false;
     f.read_orderby_key_reverse = false;
     f.has_condition_cache_digest = false;
@@ -254,18 +247,11 @@ TEST(CountEmitShortcut, SurvivingEvaluationRefuses) {
     }
 }
 
-// Consumers of real row ids or per-row values refuse.
-TEST(CountEmitShortcut, RowIdOrValueConsumersRefuse) {
-    {
-        auto f = safe_emit_facts();
-        f.has_virtual_columns = true;
-        EXPECT_FALSE(count_emit_shortcut_safe(f));
-    }
-    {
-        auto f = safe_emit_facts();
-        f.record_rowids = true;
-        EXPECT_FALSE(count_emit_shortcut_safe(f));
-    }
+// Virtual columns consume real row ids or per-row values.
+TEST(CountEmitShortcut, VirtualColumnsRefuse) {
+    auto f = safe_emit_facts();
+    f.has_virtual_columns = true;
+    EXPECT_FALSE(count_emit_shortcut_safe(f));
 }
 
 // Batch accounting must be a pure countdown: limits, reverse key-ordered
diff --git 
a/be/test/storage/segment/segment_iterator_count_emit_shortcut_test.cpp 
b/be/test/storage/segment/segment_iterator_count_emit_shortcut_test.cpp
index a60cbbdc266..f48e8c98e3d 100644
--- a/be/test/storage/segment/segment_iterator_count_emit_shortcut_test.cpp
+++ b/be/test/storage/segment/segment_iterator_count_emit_shortcut_test.cpp
@@ -360,14 +360,6 @@ TEST(CountEmitShortcut, EngageRefusalTruthTable) {
         Block block = fx.read_schema->create_read_block();
         EXPECT_FALSE(fx.iter->_should_engage_count_emit_shortcut(&block));
     }
-    // Rowid consumers need real row ids.
-    {
-        Fixture fx;
-        fx.set_hit();
-        fx.iter->_opts.record_rowids = true;
-        Block block = fx.read_schema->create_read_block();
-        EXPECT_FALSE(fx.iter->_should_engage_count_emit_shortcut(&block));
-    }
     // Block shape != read schema (e.g. delete-condition column beyond block).
     {
         Fixture fx;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to