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

Gabriel39 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 7191837ece2 [fix](parquet) Restore column ownership after interrupted 
reads (#66780)
7191837ece2 is described below

commit 7191837ece228a32fcb4913054df94f55fc9657d
Author: daidai <[email protected]>
AuthorDate: Mon Aug 24 16:05:22 2026 +0800

    [fix](parquet) Restore column ownership after interrupted reads (#66780)
    
    ### What problem does this PR solve?
    
    Issue Number: None
    
    Related PR: None
    
    Problem Summary: The direct Parquet conversion path temporarily moves
    the caller-owned column into the physical read column. If an interrupted
    read returns before conversion, the caller is left with a null column
    pointer. Complex-column cleanup can then dereference that null child and
    crash the BE. Restore the transferred column on every early return, and
    disarm the restoration only after conversion moves ownership back
    successfully. Add a focused test that triggers the IO stop path and
    verifies the caller retains a valid column.
    
    coredump:
    ```
    04:09:40   
/mnt/disk3/pipeline/repo/selectdb-core_branch-selectdb-doris-4.1/selectdb-core/be/src/core/column/column_struct.h:98:23:
 runtime error: member call on null pointer of type 'doris::IColumn'
    04:09:40   *** Query id: 346501e80b2a4276-b032f92f6e9b6c3c ***
    04:09:40   *** is nereids: 1 ***
    04:09:40   *** tablet id: 0 ***
    04:09:40   *** Aborted at 1786478874 (unix time) try "date -d @1786478874" 
if you are using GNU date ***
    04:09:40   *** Current BE git commitID: 206d71cd3c ***
    04:09:40   *** SIGSEGV address not mapped to object (@0x0) received by PID 
12549 (TID 37198 OR 0x7af300fc4640) from PID 0; stack trace: ***
    04:09:40    0# doris::signal::(anonymous 
namespace)::FailureSignalHandler(int, siginfo_t*, void*) at 
/mnt/disk3/pipeline/repo/selectdb-core_branch-selectdb-doris-4.1/selectdb-core/be/src/common/signal_handler.h:420
    04:09:40    1# PosixSignals::chained_handler(int, siginfo*, void*) [clone 
.part.0] in /usr/lib/jvm/java-17-openjdk-amd64/lib/server/libjvm.so
    04:09:40    2# JVM_handle_linux_signal in 
/usr/lib/jvm/java-17-openjdk-amd64/lib/server/libjvm.so
    04:09:40    3# 0x00007F007F83D520 in /lib/x86_64-linux-gnu/libc.so.6
    04:09:40    4# doris::ColumnArray::is_exclusive() const at 
/mnt/disk3/pipeline/repo/selectdb-core_branch-selectdb-doris-4.1/selectdb-core/be/src/core/column/column_array.h:141
    04:09:40    5# doris::ColumnNullable::is_exclusive() const at 
/mnt/disk3/pipeline/repo/selectdb-core_branch-selectdb-doris-4.1/selectdb-core/be/src/core/column/column_nullable.h:268
    04:09:40    6# doris::ColumnStruct::is_exclusive() const at 
/mnt/disk3/pipeline/repo/selectdb-core_branch-selectdb-doris-4.1/selectdb-core/be/src/core/column/column_struct.h:98
    04:09:40    7# doris::ColumnNullable::is_exclusive() const at 
/mnt/disk3/pipeline/repo/selectdb-core_branch-selectdb-doris-4.1/selectdb-core/be/src/core/column/column_nullable.h:268
    04:09:40    8# doris::ColumnArray::is_exclusive() const at 
/mnt/disk3/pipeline/repo/selectdb-core_branch-selectdb-doris-4.1/selectdb-core/be/src/core/column/column_array.h:141
    04:09:40    9# doris::ColumnNullable::is_exclusive() const at 
/mnt/disk3/pipeline/repo/selectdb-core_branch-selectdb-doris-4.1/selectdb-core/be/src/core/column/column_nullable.h:268
    04:09:40   10# doris::ColumnStruct::is_exclusive() const at 
/mnt/disk3/pipeline/repo/selectdb-core_branch-selectdb-doris-4.1/selectdb-core/be/src/core/column/column_struct.h:98
    04:09:40   11# doris::ColumnNullable::is_exclusive() const at 
/mnt/disk3/pipeline/repo/selectdb-core_branch-selectdb-doris-4.1/selectdb-core/be/src/core/column/column_nullable.h:268
    04:09:40   12# doris::ColumnArray::is_exclusive() const at 
/mnt/disk3/pipeline/repo/selectdb-core_branch-selectdb-doris-4.1/selectdb-core/be/src/core/column/column_array.h:141
    04:09:40   13# doris::ColumnNullable::is_exclusive() const at 
/mnt/disk3/pipeline/repo/selectdb-core_branch-selectdb-doris-4.1/selectdb-core/be/src/core/column/column_nullable.h:268
    04:09:40   14# doris::ColumnStruct::is_exclusive() const at 
/mnt/disk3/pipeline/repo/selectdb-core_branch-selectdb-doris-4.1/selectdb-core/be/src/core/column/column_struct.h:98
    04:09:40   15# doris::ColumnNullable::is_exclusive() const at 
/mnt/disk3/pipeline/repo/selectdb-core_branch-selectdb-doris-4.1/selectdb-core/be/src/core/column/column_nullable.h:268
    04:09:40   16# doris::ColumnArray::is_exclusive() const at 
/mnt/disk3/pipeline/repo/selectdb-core_branch-selectdb-doris-4.1/selectdb-core/be/src/core/column/column_array.h:141
    04:09:40   17# doris::ColumnNullable::is_exclusive() const at 
/mnt/disk3/pipeline/repo/selectdb-core_branch-selectdb-doris-4.1/selectdb-core/be/src/core/column/column_nullable.h:268
    04:09:40   18# doris::Block::clear_column_data(long) at 
/mnt/disk3/pipeline/repo/selectdb-core_branch-selectdb-doris-4.1/selectdb-core/be/src/core/block/block.cpp:816
    04:09:40   19# doris::ParquetReader::get_next_block(doris::Block*, unsigned 
long*, bool*) at 
/mnt/disk3/pipeline/repo/selectdb-core_branch-selectdb-doris-4.1/selectdb-core/be/src/format/parquet/vparquet_reader.cpp:734
    ```
    
    ### Release note
    
    None
    
    ### Check List (For Author)
    
    - Test <!-- At least one of them must be included. -->
        - [ ] Regression test
        - [x] Unit Test
        - [ ] Manual test (add detailed scripts or steps below)
        - [ ] No need to test or manual test. Explain why:
    - [ ] This is a refactor/code format and no logic has been changed.
            - [ ] Previous test can cover this change.
            - [ ] No code files have been changed.
            - [ ] Other reason <!-- Add your reason?  -->
    
    - Behavior changed:
        - [ ] No.
        - [x] Yes. <!-- Explain the behavior change -->
    
    - Does this need documentation?
        - [x] No.
    - [ ] Yes. <!-- Add document PR link here. eg:
    https://github.com/apache/doris-website/pull/1214 -->
    
    ### Check List (For Reviewer who merge this PR)
    
    - [ ] Confirm the release note
    - [ ] Confirm test cases
    - [ ] Confirm document
    - [ ] Add branch pick label <!-- Add branch pick label that this PR
    should merge into -->
---
 be/src/format/parquet/vparquet_column_reader.cpp   | 22 ++++++++++---
 .../parquet/parquet_column_chunk_reader_test.cpp   | 37 ++++++++++++++++++++++
 2 files changed, 55 insertions(+), 4 deletions(-)

diff --git a/be/src/format/parquet/vparquet_column_reader.cpp 
b/be/src/format/parquet/vparquet_column_reader.cpp
index 5fd9adcff06..8797b54c0a6 100644
--- a/be/src/format/parquet/vparquet_column_reader.cpp
+++ b/be/src/format/parquet/vparquet_column_reader.cpp
@@ -41,6 +41,7 @@
 #include "format/table/iceberg_default_value.h"
 #include "io/fs/tracing_file_reader.h"
 #include "runtime/runtime_profile.h"
+#include "util/defer_op.h"
 
 namespace doris {
 static void fill_struct_null_map(FieldSchema* field, NullMap& null_map,
@@ -539,11 +540,26 @@ Status ScalarColumnReader<IN_COLLECTION, 
OFFSET_INDEX>::read_column_data(
     ColumnPtr resolved_column =
             _converter->get_physical_column(_field_schema->physical_type, 
_field_schema->data_type,
                                             doris_column, type, 
is_dict_filter);
+    // Direct reads transfer the caller's only ColumnPtr so mutate() can avoid 
cloning it. Restore
+    // that ownership if any read step returns before convert() moves the 
column back.
+    bool restore_doris_column = false;
+    Defer restore_column([&]() {
+        if (restore_doris_column) {
+            doris_column = std::move(resolved_column);
+        }
+    });
     if (_converter->read_directly_into_dst_logical_column()) {
         DCHECK_EQ(resolved_column.get(), doris_column.get());
         resolved_column = std::move(doris_column);
+        restore_doris_column = true;
     }
     DataTypePtr& resolved_type = _converter->get_physical_type();
+    auto convert_column = [&]() -> Status {
+        RETURN_IF_ERROR(_converter->convert(resolved_column, 
_field_schema->data_type, type,
+                                            doris_column, is_dict_filter));
+        restore_doris_column = false;
+        return Status::OK();
+    };
 
     _def_levels.clear();
     _rep_levels.clear();
@@ -552,8 +568,7 @@ Status ScalarColumnReader<IN_COLLECTION, 
OFFSET_INDEX>::read_column_data(
     if (_in_nested) {
         RETURN_IF_ERROR(_read_nested_column(resolved_column, resolved_type, 
filter_map, batch_size,
                                             read_rows, eof, is_dict_filter));
-        return _converter->convert(resolved_column, _field_schema->data_type, 
type, doris_column,
-                                   is_dict_filter);
+        return convert_column();
     }
 
     int64_t right_row = 0;
@@ -631,8 +646,7 @@ Status ScalarColumnReader<IN_COLLECTION, 
OFFSET_INDEX>::read_column_data(
 
     {
         SCOPED_RAW_TIMER(&_convert_time);
-        RETURN_IF_ERROR(_converter->convert(resolved_column, 
_field_schema->data_type, type,
-                                            doris_column, is_dict_filter));
+        RETURN_IF_ERROR(convert_column());
     }
     return Status::OK();
 }
diff --git a/be/test/format/parquet/parquet_column_chunk_reader_test.cpp 
b/be/test/format/parquet/parquet_column_chunk_reader_test.cpp
index be9616c523f..d4327ee05d5 100644
--- a/be/test/format/parquet/parquet_column_chunk_reader_test.cpp
+++ b/be/test/format/parquet/parquet_column_chunk_reader_test.cpp
@@ -27,11 +27,13 @@
 
 #include "core/assert_cast.h"
 #include "core/column/column_string.h"
+#include "core/data_type/data_type_string.h"
 #include "format/parquet/schema_desc.h"
 #include "format/parquet/vparquet_column_chunk_reader.h"
 #include "format/parquet/vparquet_column_reader.h"
 #include "io/fs/buffered_reader.h"
 #include "io/fs/file_reader.h"
+#include "io/io_common.h"
 #include "runtime/runtime_state.h"
 #include "util/coding.h"
 #include "util/thrift_util.h"
@@ -418,6 +420,41 @@ TEST(ParquetColumnChunkReaderTest, 
ScalarDictionaryReadUsesExplicitProbe) {
     EXPECT_EQ(std::string(strings.get_data_at(2)), "carol");
 }
 
+TEST(ParquetColumnChunkReaderTest, ScalarNestedReadRestoresColumnWhenStopped) {
+    ColumnChunkFixture fixture;
+    ASSERT_TRUE(make_plain_fixture(&fixture).ok());
+    auto file_reader = 
std::make_shared<CountingFileReader>(std::move(fixture.data));
+    auto string_type = std::make_shared<DataTypeString>();
+    fixture.field_schema.data_type = string_type;
+    fixture.field_schema.parquet_schema.__set_type(tparquet::Type::BYTE_ARRAY);
+
+    RowRanges row_ranges;
+    row_ranges.add({0, 1});
+    io::IOContext io_ctx;
+    ScalarColumnReader<true, false> reader(row_ranges, 1, fixture.chunk, 
nullptr, nullptr, &io_ctx);
+    reader.set_column_in_nested();
+
+    TQueryOptions query_options;
+    query_options.__set_enable_parquet_file_page_cache(false);
+    RuntimeState runtime_state(query_options, TQueryGlobals());
+    ASSERT_TRUE(reader.init(file_reader, &fixture.field_schema,
+                            /*max_buf_size=*/1024 * 1024, &runtime_state)
+                        .ok());
+
+    ColumnPtr column = ColumnString::create();
+    FilterMap filter_map;
+    ASSERT_TRUE(filter_map.init(nullptr, 0, false).ok());
+    size_t read_rows = 0;
+    bool eof = false;
+    io_ctx.should_stop = true;
+
+    Status status = reader.read_column_data(column, string_type, nullptr, 
filter_map, 1, &read_rows,
+                                            &eof, false);
+    EXPECT_TRUE(status.is<ErrorCode::END_OF_FILE>()) << status;
+    ASSERT_TRUE(column);
+    EXPECT_TRUE(column->empty());
+}
+
 void expect_offset_index_skip(ColumnChunkFixture fixture) {
     CountingBufferedReader buffered_reader(std::move(fixture.data));
     ParquetPageReadContext page_read_ctx(false);


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

Reply via email to