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 51e44133b1d [fix](be) Release deletion vector rows on read errors
(#65351)
51e44133b1d is described below
commit 51e44133b1d8c439369cebf2867dfb99233b1faa
Author: Gabriel <[email protected]>
AuthorDate: Thu Jul 9 17:02:54 2026 +0800
[fix](be) Release deletion vector rows on read errors (#65351)
Problem Summary: Deletion vector and position-delete cache builders
allocated rows/maps with raw `new` and returned `nullptr` on read or
parse failures. Those error paths leaked partially built cache values
because `KVCache::get` only takes ownership when the builder returns a
non-null pointer. This PR uses RAII while building `DeleteRows` and
`DeleteFile`, releases ownership only after successful construction, and
adds fault-injection/regression coverage for V1 Iceberg deletion-vector
and position-delete failures, V1 Paimon Parquet/ORC deletion-vector
failures, the shared Iceberg helper, and the format v2 TableReader path.
---
.../table/iceberg_delete_file_reader_helper.cpp | 7 +-
be/src/format/table/iceberg_reader_mixin.h | 26 ++-
be/src/format/table/paimon_reader.cpp | 13 +-
be/src/format/table/paimon_reader.h | 4 +
be/src/format_v2/table_reader.cpp | 16 +-
.../iceberg_delete_file_reader_helper_test.cpp | 242 +++++++++++++++++++++
.../format/table/iceberg/iceberg_reader_test.cpp | 68 ++++++
be/test/format/table/paimon_cpp_reader_test.cpp | 73 +++++++
be/test/format_v2/table/iceberg_reader_test.cpp | 192 ++++++++++++++++
regression-test/pipeline/external/conf/be.conf | 3 +-
regression-test/pipeline/external/conf/fe.conf | 2 +-
.../iceberg/test_iceberg_deletion_vector.groovy | 29 ++-
12 files changed, 655 insertions(+), 20 deletions(-)
diff --git a/be/src/format/table/iceberg_delete_file_reader_helper.cpp
b/be/src/format/table/iceberg_delete_file_reader_helper.cpp
index 5845e440d95..21bf9865868 100644
--- a/be/src/format/table/iceberg_delete_file_reader_helper.cpp
+++ b/be/src/format/table/iceberg_delete_file_reader_helper.cpp
@@ -44,6 +44,7 @@
#include "io/hdfs_builder.h"
#include "runtime/runtime_state.h"
#include "storage/predicate/column_predicate.h"
+#include "util/debug_points.h"
namespace doris {
@@ -301,6 +302,10 @@ Status read_iceberg_deletion_vector(const
TIcebergDeleteFileDesc& delete_file,
if (!delete_file.__isset.content_offset ||
!delete_file.__isset.content_size_in_bytes) {
return Status::InternalError("Deletion vector is missing content
offset or length");
}
+ DBUG_EXECUTE_IF("IcebergDeleteFileReader.read_deletion_vector.io_error",
+ { return Status::IOError("injected Iceberg deletion vector
read failure"); });
+ DBUG_EXECUTE_IF("IcebergDeleteFileReader.read_deletion_vector.should_stop",
+ { return Status::EndOfFile("stop read."); });
TFileRangeDesc delete_range =
build_iceberg_delete_file_range(delete_file.path);
if (options.fs_name != nullptr && !options.fs_name->empty()) {
@@ -316,7 +321,7 @@ Status read_iceberg_deletion_vector(const
TIcebergDeleteFileDesc& delete_file,
std::vector<char> buf(delete_range.size);
RETURN_IF_ERROR(dv_reader.read_at(delete_range.start_offset,
{buf.data(),
cast_set<size_t>(delete_range.size)}));
- return decode_iceberg_deletion_vector_buffer(buf.data(),
delete_range.size, rows_to_delete);
+ return decode_deletion_vector_buffer(buf.data(), delete_range.size,
rows_to_delete);
}
Status decode_iceberg_deletion_vector_buffer(const char* buf, size_t
buffer_size,
diff --git a/be/src/format/table/iceberg_reader_mixin.h
b/be/src/format/table/iceberg_reader_mixin.h
index f27fa83467c..2e9bd06b6fd 100644
--- a/be/src/format/table/iceberg_reader_mixin.h
+++ b/be/src/format/table/iceberg_reader_mixin.h
@@ -19,6 +19,7 @@
#include <cstddef>
#include <cstdint>
+#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
@@ -108,6 +109,16 @@ public:
_create_topn_row_id_column_iterator = create_func;
}
+ Status TEST_read_deletion_vector(const std::string& data_file_path,
+ const TIcebergDeleteFileDesc&
delete_file_desc) {
+ return read_deletion_vector(data_file_path, delete_file_desc);
+ }
+
+ Status TEST_position_delete_base(const std::string& data_file_path,
+ const
std::vector<TIcebergDeleteFileDesc>& delete_files) {
+ return _position_delete_base(data_file_path, delete_files);
+ }
+
protected:
// ---- Hook implementations ----
@@ -627,18 +638,19 @@ Status
IcebergReaderMixin<BaseReader>::_position_delete_base(
Status create_status = Status::OK();
auto* delete_file_cache = _kv_cache->template get<DeleteFile>(
_delet_file_cache_key(delete_file.path), [&]() -> DeleteFile* {
- auto* position_delete = new DeleteFile;
+ auto position_delete = std::make_unique<DeleteFile>();
TFileRangeDesc delete_file_range;
delete_file_range.__set_fs_name(this->get_scan_range().fs_name);
delete_file_range.path = delete_file.path;
delete_file_range.start_offset = 0;
delete_file_range.size = -1;
delete_file_range.file_size = -1;
- create_status =
_read_position_delete_file(&delete_file_range, position_delete);
+ create_status =
+ _read_position_delete_file(&delete_file_range,
position_delete.get());
if (!create_status) {
return nullptr;
}
- return position_delete;
+ return position_delete.release();
});
if (create_status.is<ErrorCode::END_OF_FILE>()) {
continue;
@@ -660,10 +672,10 @@ Status
IcebergReaderMixin<BaseReader>::_position_delete_base(
SCOPED_TIMER(_iceberg_profile.delete_rows_sort_time);
_iceberg_delete_rows =
_kv_cache->template get<DeleteRows>(data_file_path, [&]() ->
DeleteRows* {
- auto* data_file_position_delete = new DeleteRows;
+ auto data_file_position_delete =
std::make_unique<DeleteRows>();
_sort_delete_rows(delete_rows_array, num_delete_rows,
*data_file_position_delete);
- return data_file_position_delete;
+ return data_file_position_delete.release();
});
set_delete_rows();
COUNTER_UPDATE(_iceberg_profile.num_delete_rows, num_delete_rows);
@@ -808,7 +820,7 @@ Status IcebergReaderMixin<BaseReader>::read_deletion_vector(
_iceberg_delete_rows = _kv_cache->template get<DeleteRows>(
build_iceberg_deletion_vector_cache_key(data_file_path,
delete_file_desc),
[&]() -> DeleteRows* {
- auto* delete_rows = new DeleteRows;
+ auto delete_rows = std::make_unique<DeleteRows>();
roaring::Roaring64Map bitmap;
IcebergDeleteFileReaderOptions options;
@@ -828,7 +840,7 @@ Status IcebergReaderMixin<BaseReader>::read_deletion_vector(
delete_rows->push_back(*it);
}
COUNTER_UPDATE(_iceberg_profile.num_delete_rows,
delete_rows->size());
- return delete_rows;
+ return delete_rows.release();
});
RETURN_IF_ERROR(create_status);
diff --git a/be/src/format/table/paimon_reader.cpp
b/be/src/format/table/paimon_reader.cpp
index 98f85658e32..51a80f3a967 100644
--- a/be/src/format/table/paimon_reader.cpp
+++ b/be/src/format/table/paimon_reader.cpp
@@ -20,6 +20,7 @@
#include <fmt/format.h>
#include <cstring>
+#include <memory>
#include <vector>
#include "common/status.h"
@@ -134,7 +135,7 @@ Status PaimonOrcReader::_init_deletion_vector() {
using DeleteRows = std::vector<int64_t>;
_delete_rows = _kv_cache->get<DeleteRows>(
build_paimon_deletion_vector_cache_key(deletion_file), [&]() ->
DeleteRows* {
- auto* delete_rows = new DeleteRows;
+ auto delete_rows = std::make_unique<DeleteRows>();
TFileRangeDesc delete_range;
delete_range.__set_fs_name(get_scan_range().fs_name);
@@ -160,12 +161,12 @@ Status PaimonOrcReader::_init_deletion_vector() {
SCOPED_TIMER(_paimon_profile.parse_deletion_vector_time);
create_status =
decode_paimon_deletion_vector_buffer(buffer.data(), bytes_read,
-
delete_rows);
+
delete_rows.get());
if (!create_status.ok()) [[unlikely]] {
return nullptr;
}
COUNTER_UPDATE(_paimon_profile.num_delete_rows,
delete_rows->size());
- return delete_rows;
+ return delete_rows.release();
});
RETURN_IF_ERROR(create_status);
if (!_delete_rows->empty()) [[likely]] {
@@ -233,7 +234,7 @@ Status PaimonParquetReader::_init_deletion_vector() {
using DeleteRows = std::vector<int64_t>;
_delete_rows = _kv_cache->get<DeleteRows>(
build_paimon_deletion_vector_cache_key(deletion_file), [&]() ->
DeleteRows* {
- auto* delete_rows = new DeleteRows;
+ auto delete_rows = std::make_unique<DeleteRows>();
TFileRangeDesc delete_range;
delete_range.__set_fs_name(get_scan_range().fs_name);
@@ -259,12 +260,12 @@ Status PaimonParquetReader::_init_deletion_vector() {
SCOPED_TIMER(_paimon_profile.parse_deletion_vector_time);
create_status =
decode_paimon_deletion_vector_buffer(buffer.data(), bytes_read,
-
delete_rows);
+
delete_rows.get());
if (!create_status.ok()) [[unlikely]] {
return nullptr;
}
COUNTER_UPDATE(_paimon_profile.num_delete_rows,
delete_rows->size());
- return delete_rows;
+ return delete_rows.release();
});
RETURN_IF_ERROR(create_status);
if (!_delete_rows->empty()) [[likely]] {
diff --git a/be/src/format/table/paimon_reader.h
b/be/src/format/table/paimon_reader.h
index 10da0b784e2..616c58275b3 100644
--- a/be/src/format/table/paimon_reader.h
+++ b/be/src/format/table/paimon_reader.h
@@ -64,6 +64,8 @@ public:
}
~PaimonOrcReader() final = default;
+ Status TEST_init_deletion_vector() { return _init_deletion_vector(); }
+
protected:
Status on_before_init_reader(ReaderInitContext* ctx) override;
@@ -109,6 +111,8 @@ public:
}
~PaimonParquetReader() final = default;
+ Status TEST_init_deletion_vector() { return _init_deletion_vector(); }
+
protected:
Status on_before_init_reader(ReaderInitContext* ctx) override;
diff --git a/be/src/format_v2/table_reader.cpp
b/be/src/format_v2/table_reader.cpp
index e1bd6b04ad2..99cacbc7a68 100644
--- a/be/src/format_v2/table_reader.cpp
+++ b/be/src/format_v2/table_reader.cpp
@@ -22,6 +22,7 @@
#include <gen_cpp/Types_types.h>
#include <algorithm>
+#include <memory>
#include <ranges>
#include <set>
#include <sstream>
@@ -46,6 +47,7 @@
#include "format_v2/native/native_reader.h"
#include "format_v2/parquet/parquet_reader.h"
#include "storage/segment/condition_cache.h"
+#include "util/debug_points.h"
#include "util/string_util.h"
namespace doris::format {
@@ -765,7 +767,7 @@ Status TableReader::_parse_delete_predicates(const
SplitReadOptions& options) {
Status create_status = Status::OK();
_delete_rows = options.cache->get<DeleteRows>(desc.key, [&]() ->
DeleteRows* {
- auto* delete_rows = new DeleteRows;
+ auto delete_rows = std::make_unique<DeleteRows>();
DeletionVectorReader dv_reader(_runtime_state, _scanner_profile,
*_scan_params, desc,
_io_ctx.get());
@@ -776,6 +778,14 @@ Status TableReader::_parse_delete_predicates(const
SplitReadOptions& options) {
size_t bytes_read = desc.size;
std::vector<char> buffer(bytes_read);
+ DBUG_EXECUTE_IF("TableReader.parse_deletion_vector.io_error", {
+ create_status = Status::IOError("injected format v2 deletion
vector read failure");
+ return nullptr;
+ });
+ DBUG_EXECUTE_IF("TableReader.parse_deletion_vector.should_stop", {
+ create_status = Status::EndOfFile("stop read.");
+ return nullptr;
+ });
create_status = dv_reader.read_at(desc.start_offset,
{buffer.data(), bytes_read});
if (!create_status.ok()) [[unlikely]] {
return nullptr;
@@ -783,12 +793,12 @@ Status TableReader::_parse_delete_predicates(const
SplitReadOptions& options) {
const char* buf = buffer.data();
SCOPED_TIMER(_profile.parse_delete_file_time);
- create_status = parse_deletion_vector(buf, bytes_read,
desc.format, delete_rows);
+ create_status = parse_deletion_vector(buf, bytes_read,
desc.format, delete_rows.get());
if (!create_status.ok()) [[unlikely]] {
return nullptr;
}
COUNTER_UPDATE(_profile.num_delete_rows, delete_rows->size());
- return delete_rows;
+ return delete_rows.release();
});
RETURN_IF_ERROR(create_status);
}
diff --git
a/be/test/format/table/iceberg/iceberg_delete_file_reader_helper_test.cpp
b/be/test/format/table/iceberg/iceberg_delete_file_reader_helper_test.cpp
index 6ce562d6de0..34d2e60f712 100644
--- a/be/test/format/table/iceberg/iceberg_delete_file_reader_helper_test.cpp
+++ b/be/test/format/table/iceberg/iceberg_delete_file_reader_helper_test.cpp
@@ -17,15 +17,24 @@
#include "format/table/iceberg_delete_file_reader_helper.h"
+#include <gen_cpp/PlanNodes_types.h>
#include <gen_cpp/Types_types.h>
#include <gtest/gtest.h>
+#include <cstring>
+#include <filesystem>
+#include <fstream>
+#include <string>
+#include <thread>
#include <unordered_map>
#include <vector>
+#include "exec/common/endian.h"
#include "io/fs/file_meta_cache.h"
+#include "roaring/roaring64map.hh"
#include "runtime/runtime_profile.h"
#include "runtime/runtime_state.h"
+#include "runtime/thread_context.h"
namespace doris {
@@ -49,6 +58,58 @@ public:
size_t total_rows = 0;
};
+TFileScanRangeParams make_local_parquet_scan_params() {
+ TFileScanRangeParams scan_params;
+ scan_params.__set_file_type(TFileType::FILE_LOCAL);
+ scan_params.__set_format_type(TFileFormatType::FORMAT_PARQUET);
+ return scan_params;
+}
+
+TIcebergDeleteFileDesc make_iceberg_deletion_vector(const std::string& path,
int64_t offset,
+ int64_t size) {
+ TIcebergDeleteFileDesc delete_file;
+ delete_file.__set_content(3);
+ delete_file.__set_path(path);
+ delete_file.__set_content_offset(offset);
+ delete_file.__set_content_size_in_bytes(size);
+ return delete_file;
+}
+
+int64_t write_iceberg_deletion_vector_file(const std::string& file_path,
+ const std::vector<uint64_t>&
deleted_positions) {
+ roaring::Roaring64Map rows;
+ for (const auto position : deleted_positions) {
+ rows.add(position);
+ }
+
+ const size_t bitmap_size = rows.getSizeInBytes();
+ std::vector<char> blob(4 + 4 + bitmap_size + 4);
+ rows.write(blob.data() + 8);
+
+ const uint32_t total_length = static_cast<uint32_t>(4 + bitmap_size);
+ BigEndian::Store32(blob.data(), total_length);
+ constexpr char DV_MAGIC[] = {'\xD1', '\xD3', '\x39', '\x64'};
+ memcpy(blob.data() + 4, DV_MAGIC, 4);
+ BigEndian::Store32(blob.data() + 8 + bitmap_size, 0);
+
+ std::ofstream output(file_path, std::ios::binary);
+ EXPECT_TRUE(output.is_open());
+ output.write(blob.data(), static_cast<std::streamsize>(blob.size()));
+ EXPECT_TRUE(output.good());
+ return static_cast<int64_t>(blob.size());
+}
+
+IcebergDeleteFileReaderOptions make_delete_file_reader_options(
+ RuntimeState* state, RuntimeProfile* profile, const
TFileScanRangeParams* scan_params,
+ io::IOContext* io_ctx) {
+ return {
+ .state = state,
+ .profile = profile,
+ .scan_params = scan_params,
+ .io_ctx = io_ctx,
+ };
+}
+
} // namespace
TEST(IcebergDeleteFileReaderHelperTest, BuildDeleteFileRange) {
@@ -119,6 +180,187 @@ TEST(IcebergDeleteFileReaderHelperTest,
DeletionVectorCacheKeyEscapesPathBoundar
build_iceberg_deletion_vector_cache_key(second_data_file_path,
second_delete_file));
}
+TEST(IcebergDeleteFileReaderHelperTest, ReadDeletionVectorReportsMissingFile) {
+ const auto test_dir =
+ std::filesystem::temp_directory_path() /
"doris_iceberg_deletion_vector_missing_test";
+ std::filesystem::remove_all(test_dir);
+ std::filesystem::create_directories(test_dir);
+
+ const auto missing_path = (test_dir /
"missing-delete-vector.bin").string();
+
+ RuntimeProfile profile("test_profile");
+ RuntimeState state {TQueryOptions(), TQueryGlobals()};
+ auto scan_params = make_local_parquet_scan_params();
+ IcebergDeleteFileIOContext io_context(&state);
+ roaring::Roaring64Map rows_to_delete;
+ auto options =
+ make_delete_file_reader_options(&state, &profile, &scan_params,
&io_context.io_ctx);
+
+ auto status =
read_iceberg_deletion_vector(make_iceberg_deletion_vector(missing_path, 0, 16),
+ options, &rows_to_delete);
+
+ EXPECT_FALSE(status.ok());
+ EXPECT_NE(status.to_string().find(missing_path), std::string::npos);
+ EXPECT_EQ(rows_to_delete.cardinality(), 0);
+ std::filesystem::remove_all(test_dir);
+}
+
+TEST(IcebergDeleteFileReaderHelperTest, ReadDeletionVectorReportsShortRead) {
+ const auto test_dir = std::filesystem::temp_directory_path() /
+ "doris_iceberg_deletion_vector_short_read_test";
+ std::filesystem::remove_all(test_dir);
+ std::filesystem::create_directories(test_dir);
+
+ const auto dv_path = (test_dir / "delete-vector.bin").string();
+ const auto dv_size = write_iceberg_deletion_vector_file(dv_path, {1, 3});
+
+ RuntimeProfile profile("test_profile");
+ RuntimeState state {TQueryOptions(), TQueryGlobals()};
+ auto scan_params = make_local_parquet_scan_params();
+ IcebergDeleteFileIOContext io_context(&state);
+ roaring::Roaring64Map rows_to_delete;
+ auto options =
+ make_delete_file_reader_options(&state, &profile, &scan_params,
&io_context.io_ctx);
+
+ auto status = read_iceberg_deletion_vector(
+ make_iceberg_deletion_vector(dv_path, 0, dv_size + 1), options,
&rows_to_delete);
+
+ EXPECT_FALSE(status.ok());
+ EXPECT_EQ(rows_to_delete.cardinality(), 0);
+ std::filesystem::remove_all(test_dir);
+}
+
+TEST(IcebergDeleteFileReaderHelperTest,
ReadDeletionVectorStopsWhenIoContextStops) {
+ const auto test_dir =
+ std::filesystem::temp_directory_path() /
"doris_iceberg_deletion_vector_stop_test";
+ std::filesystem::remove_all(test_dir);
+ std::filesystem::create_directories(test_dir);
+
+ const auto dv_path = (test_dir / "delete-vector.bin").string();
+ const auto dv_size = write_iceberg_deletion_vector_file(dv_path, {0});
+
+ RuntimeProfile profile("test_profile");
+ RuntimeState state {TQueryOptions(), TQueryGlobals()};
+ auto scan_params = make_local_parquet_scan_params();
+ IcebergDeleteFileIOContext io_context(&state);
+ io_context.io_ctx.should_stop = true;
+ roaring::Roaring64Map rows_to_delete;
+ auto options =
+ make_delete_file_reader_options(&state, &profile, &scan_params,
&io_context.io_ctx);
+
+ auto status =
read_iceberg_deletion_vector(make_iceberg_deletion_vector(dv_path, 0, dv_size),
+ options, &rows_to_delete);
+
+ EXPECT_TRUE(status.is<ErrorCode::END_OF_FILE>());
+ EXPECT_NE(status.to_string().find("stop read"), std::string::npos);
+ EXPECT_EQ(rows_to_delete.cardinality(), 0);
+ std::filesystem::remove_all(test_dir);
+}
+
+TEST(IcebergDeleteFileReaderHelperTest,
DecodeDeletionVectorRejectsCorruptPayload) {
+ std::vector<char> corrupted(12, 0);
+ BigEndian::Store32(corrupted.data(), 4);
+ memcpy(corrupted.data() + 4, "BAD!", 4);
+
+ roaring::Roaring64Map rows_to_delete;
+ auto status = decode_iceberg_deletion_vector_buffer(corrupted.data(),
corrupted.size(),
+ &rows_to_delete);
+
+ EXPECT_TRUE(status.is<ErrorCode::DATA_QUALITY_ERROR>());
+ EXPECT_NE(status.to_string().find("magic number mismatch"),
std::string::npos);
+ EXPECT_EQ(rows_to_delete.cardinality(), 0);
+}
+
+TEST(IcebergDeleteFileReaderHelperTest,
ReadDeletionVectorReadsMillionDeletePositions) {
+ const auto test_dir =
+ std::filesystem::temp_directory_path() /
"doris_iceberg_deletion_vector_large_test";
+ std::filesystem::remove_all(test_dir);
+ std::filesystem::create_directories(test_dir);
+
+ constexpr uint64_t delete_position_count = 1'000'000;
+ std::vector<uint64_t> deleted_positions;
+ deleted_positions.reserve(delete_position_count);
+ for (uint64_t position = 0; position < delete_position_count; ++position) {
+ deleted_positions.push_back(position * 2);
+ }
+
+ const auto dv_path = (test_dir / "delete-vector.bin").string();
+ const auto dv_size = write_iceberg_deletion_vector_file(dv_path,
deleted_positions);
+
+ RuntimeProfile profile("test_profile");
+ RuntimeState state {TQueryOptions(), TQueryGlobals()};
+ auto scan_params = make_local_parquet_scan_params();
+ IcebergDeleteFileIOContext io_context(&state);
+ roaring::Roaring64Map rows_to_delete;
+ auto options =
+ make_delete_file_reader_options(&state, &profile, &scan_params,
&io_context.io_ctx);
+
+
ASSERT_TRUE(read_iceberg_deletion_vector(make_iceberg_deletion_vector(dv_path,
0, dv_size),
+ options, &rows_to_delete)
+ .ok());
+
+ EXPECT_EQ(rows_to_delete.cardinality(), delete_position_count);
+ EXPECT_TRUE(rows_to_delete.contains(static_cast<uint64_t>(0)));
+ EXPECT_TRUE(rows_to_delete.contains(static_cast<uint64_t>(999'998)));
+ EXPECT_TRUE(rows_to_delete.contains(static_cast<uint64_t>(1'999'998)));
+ EXPECT_FALSE(rows_to_delete.contains(static_cast<uint64_t>(1'999'999)));
+ std::filesystem::remove_all(test_dir);
+}
+
+TEST(IcebergDeleteFileReaderHelperTest,
ReadDeletionVectorConcurrentReadsAreStable) {
+ const auto test_dir = std::filesystem::temp_directory_path() /
+ "doris_iceberg_deletion_vector_concurrent_test";
+ std::filesystem::remove_all(test_dir);
+ std::filesystem::create_directories(test_dir);
+
+ const auto first_dv_path = (test_dir / "delete-vector-first.bin").string();
+ const auto second_dv_path = (test_dir /
"delete-vector-second.bin").string();
+ const auto first_dv_size =
write_iceberg_deletion_vector_file(first_dv_path, {0, 2, 4});
+ const auto second_dv_size =
write_iceberg_deletion_vector_file(second_dv_path, {1, 3, 5});
+
+ auto read_and_verify = [](const std::string& path, int64_t size,
+ const std::vector<uint64_t>& expected_positions)
-> Status {
+ RuntimeProfile profile("test_profile");
+ RuntimeState state {TQueryOptions(), TQueryGlobals()};
+ auto scan_params = make_local_parquet_scan_params();
+ IcebergDeleteFileIOContext io_context(&state);
+ roaring::Roaring64Map rows_to_delete;
+ auto options =
+ make_delete_file_reader_options(&state, &profile,
&scan_params, &io_context.io_ctx);
+
RETURN_IF_ERROR(read_iceberg_deletion_vector(make_iceberg_deletion_vector(path,
0, size),
+ options,
&rows_to_delete));
+ if (rows_to_delete.cardinality() != expected_positions.size()) {
+ return Status::InternalError("unexpected deletion vector
cardinality");
+ }
+ for (const auto position : expected_positions) {
+ if (!rows_to_delete.contains(position)) {
+ return Status::InternalError("missing deletion vector position
{}", position);
+ }
+ }
+ return Status::OK();
+ };
+
+ std::vector<std::thread> workers;
+ std::vector<Status> statuses(16);
+ for (size_t idx = 0; idx < statuses.size(); ++idx) {
+ workers.emplace_back([&, idx]() {
+ SCOPED_INIT_THREAD_CONTEXT();
+ if (idx % 2 == 0) {
+ statuses[idx] = read_and_verify(first_dv_path, first_dv_size,
{0, 2, 4});
+ } else {
+ statuses[idx] = read_and_verify(second_dv_path,
second_dv_size, {1, 3, 5});
+ }
+ });
+ }
+ for (auto& worker : workers) {
+ worker.join();
+ }
+ for (const auto& status : statuses) {
+ EXPECT_TRUE(status.ok()) << status;
+ }
+ std::filesystem::remove_all(test_dir);
+}
+
TEST(IcebergDeleteFileReaderHelperTest,
ReadMixedEncodingParquetPositionDeleteFile) {
RuntimeProfile profile("test_profile");
RuntimeState runtime_state((TQueryOptions()), TQueryGlobals());
diff --git a/be/test/format/table/iceberg/iceberg_reader_test.cpp
b/be/test/format/table/iceberg/iceberg_reader_test.cpp
index c2618597312..89bf367d06b 100644
--- a/be/test/format/table/iceberg/iceberg_reader_test.cpp
+++ b/be/test/format/table/iceberg/iceberg_reader_test.cpp
@@ -46,6 +46,7 @@
#include "core/data_type/data_type_string.h"
#include "core/data_type/data_type_struct.h"
#include "format/column_descriptor.h"
+#include "format/format_common.h"
#include "format/orc/vorc_reader.h"
#include "format/parquet/vparquet_column_chunk_reader.h"
#include "format/parquet/vparquet_reader.h"
@@ -53,6 +54,7 @@
#include "io/fs/file_reader_writer_fwd.h"
#include "io/fs/file_system.h"
#include "io/fs/local_file_system.h"
+#include "io/io_common.h"
#include "runtime/descriptors.h"
#include "runtime/runtime_state.h"
#include "storage/olap_scan_common.h"
@@ -777,6 +779,72 @@ TEST_F(IcebergReaderTest, read_iceberg_parquet_file) {
verify_test_results(block, read_rows);
}
+TEST_F(IcebergReaderTest, v1_deletion_vector_read_error_releases_cache_entry) {
+ RuntimeState runtime_state = RuntimeState(TQueryOptions(),
TQueryGlobals());
+ TFileScanRangeParams scan_params;
+ scan_params.__set_file_type(TFileType::FILE_LOCAL);
+ scan_params.__set_format_type(TFileFormatType::FORMAT_PARQUET);
+
+ TFileRangeDesc scan_range;
+ scan_range.__set_fs_name("");
+ scan_range.__set_path("data.parquet");
+ scan_range.__set_start_offset(0);
+ scan_range.__set_size(0);
+
+ RuntimeProfile profile("test_profile");
+ cctz::time_zone ctz;
+ TimezoneUtils::find_cctz_time_zone(TimezoneUtils::default_time_zone, ctz);
+ io::IOContext io_ctx;
+ ShardedKVCache kv_cache(8);
+
+ IcebergParquetReader iceberg_reader(&kv_cache, &profile, scan_params,
scan_range, 1024, &ctz,
+ &io_ctx, &runtime_state, cache.get());
+
+ TIcebergDeleteFileDesc delete_file;
+
delete_file.__set_content(IcebergReaderMixin<ParquetReader>::DELETION_VECTOR);
+
delete_file.__set_path("./be/test/exec/test_data/missing_iceberg_v1_delete_vector.bin");
+ delete_file.__set_content_offset(0);
+ delete_file.__set_content_size_in_bytes(16);
+
+ const auto status =
+
iceberg_reader.TEST_read_deletion_vector("file:///tmp/data.parquet",
delete_file);
+
+ ASSERT_FALSE(status.ok());
+ EXPECT_NE(status.to_string().find(delete_file.path), std::string::npos);
+}
+
+TEST_F(IcebergReaderTest, v1_position_delete_read_error_releases_cache_entry) {
+ RuntimeState runtime_state = RuntimeState(TQueryOptions(),
TQueryGlobals());
+ TFileScanRangeParams scan_params;
+ scan_params.__set_file_type(TFileType::FILE_LOCAL);
+ scan_params.__set_format_type(TFileFormatType::FORMAT_PARQUET);
+
+ TFileRangeDesc scan_range;
+ scan_range.__set_fs_name("");
+ scan_range.__set_path("data.parquet");
+ scan_range.__set_start_offset(0);
+ scan_range.__set_size(0);
+
+ RuntimeProfile profile("test_profile");
+ cctz::time_zone ctz;
+ TimezoneUtils::find_cctz_time_zone(TimezoneUtils::default_time_zone, ctz);
+ io::IOContext io_ctx;
+ ShardedKVCache kv_cache(8);
+
+ IcebergParquetReader iceberg_reader(&kv_cache, &profile, scan_params,
scan_range, 1024, &ctz,
+ &io_ctx, &runtime_state, cache.get());
+
+ TIcebergDeleteFileDesc delete_file;
+
delete_file.__set_content(IcebergReaderMixin<ParquetReader>::POSITION_DELETE);
+
delete_file.__set_path("./be/test/exec/test_data/missing_iceberg_v1_position_delete.parquet");
+
+ const auto status =
+
iceberg_reader.TEST_position_delete_base("file:///tmp/data.parquet",
{delete_file});
+
+ ASSERT_FALSE(status.ok());
+ EXPECT_NE(status.to_string().find(delete_file.path), std::string::npos);
+}
+
// Test reading real Iceberg Orc file using IcebergTableReader
TEST_F(IcebergReaderTest, read_iceberg_orc_file) {
// Read only: name, profile.address.coordinates.lat,
profile.address.coordinates.lng, profile.contact.email
diff --git a/be/test/format/table/paimon_cpp_reader_test.cpp
b/be/test/format/table/paimon_cpp_reader_test.cpp
index ecec461d6b1..2858956c8f0 100644
--- a/be/test/format/table/paimon_cpp_reader_test.cpp
+++ b/be/test/format/table/paimon_cpp_reader_test.cpp
@@ -17,6 +17,7 @@
#include "format/table/paimon_cpp_reader.h"
+#include <cctz/time_zone.h>
#include <gtest/gtest.h>
#include <cstring>
@@ -25,10 +26,14 @@
#include "core/block/block.h"
#include "exec/common/endian.h"
+#include "format/format_common.h"
#include "format/table/paimon_reader.h"
+#include "io/fs/file_meta_cache.h"
+#include "io/io_common.h"
#include "roaring/roaring.hh"
#include "runtime/runtime_profile.h"
#include "runtime/runtime_state.h"
+#include "util/timezone_utils.h"
namespace doris {
@@ -50,6 +55,33 @@ std::vector<char> build_paimon_deletion_vector_buffer(const
std::vector<uint32_t
return buffer;
}
+TFileScanRangeParams make_local_paimon_scan_params(TFileFormatType::type
format_type) {
+ TFileScanRangeParams scan_params;
+ scan_params.__set_file_type(TFileType::FILE_LOCAL);
+ scan_params.__set_format_type(format_type);
+ return scan_params;
+}
+
+TFileRangeDesc make_paimon_range_with_deletion_file(const std::string&
deletion_file_path) {
+ TFileRangeDesc range;
+ range.__set_path("data.parquet");
+ range.__set_start_offset(0);
+ range.__set_size(0);
+ range.__set_fs_name("");
+ range.__isset.table_format_params = true;
+ range.table_format_params.__set_table_format_type("paimon");
+
+ TPaimonFileDesc paimon_params;
+ paimon_params.__set_file_format("parquet");
+ TPaimonDeletionFileDesc deletion_file;
+ deletion_file.__set_path(deletion_file_path);
+ deletion_file.__set_offset(0);
+ deletion_file.__set_length(16);
+ paimon_params.__set_deletion_file(deletion_file);
+ range.table_format_params.__set_paimon_params(paimon_params);
+ return range;
+}
+
} // namespace
class PaimonCppReaderTest : public testing::Test {
@@ -198,4 +230,45 @@ TEST(PaimonDeletionVectorTest,
CacheKeyIncludesOffsetAndLength) {
EXPECT_NE(first_key,
build_paimon_deletion_vector_cache_key(different_length));
}
+TEST(PaimonDeletionVectorTest, V1ParquetReaderReadErrorReleasesCacheEntry) {
+ RuntimeState runtime_state {TQueryOptions(), TQueryGlobals()};
+ RuntimeProfile profile("paimon_v1_parquet_dv_test");
+ auto scan_params =
make_local_paimon_scan_params(TFileFormatType::FORMAT_PARQUET);
+ const auto range = make_paimon_range_with_deletion_file(
+ "./be/test/exec/test_data/missing_paimon_v1_delete_vector.bin");
+ cctz::time_zone ctz;
+ TimezoneUtils::find_cctz_time_zone(TimezoneUtils::default_time_zone, ctz);
+ io::IOContext io_ctx;
+ FileMetaCache meta_cache(1024);
+ ShardedKVCache kv_cache(8);
+
+ PaimonParquetReader reader(&profile, scan_params, range, 1024, &ctz,
&kv_cache, &io_ctx,
+ &runtime_state, &meta_cache);
+ const auto status = reader.TEST_init_deletion_vector();
+
+ ASSERT_FALSE(status.ok());
+
EXPECT_NE(status.to_string().find(range.table_format_params.paimon_params.deletion_file.path),
+ std::string::npos);
+}
+
+TEST(PaimonDeletionVectorTest, V1OrcReaderReadErrorReleasesCacheEntry) {
+ RuntimeState runtime_state {TQueryOptions(), TQueryGlobals()};
+ RuntimeProfile profile("paimon_v1_orc_dv_test");
+ auto scan_params =
make_local_paimon_scan_params(TFileFormatType::FORMAT_ORC);
+ auto range = make_paimon_range_with_deletion_file(
+
"./be/test/exec/test_data/missing_paimon_v1_orc_delete_vector.bin");
+ range.table_format_params.paimon_params.__set_file_format("orc");
+ io::IOContext io_ctx;
+ FileMetaCache meta_cache(1024);
+ ShardedKVCache kv_cache(8);
+
+ PaimonOrcReader reader(&profile, &runtime_state, scan_params, range, 1024,
+ TimezoneUtils::default_time_zone, &kv_cache,
&io_ctx, &meta_cache);
+ const auto status = reader.TEST_init_deletion_vector();
+
+ ASSERT_FALSE(status.ok());
+
EXPECT_NE(status.to_string().find(range.table_format_params.paimon_params.deletion_file.path),
+ std::string::npos);
+}
+
} // namespace doris
diff --git a/be/test/format_v2/table/iceberg_reader_test.cpp
b/be/test/format_v2/table/iceberg_reader_test.cpp
index 4d57fcd950b..2d3f74963d9 100644
--- a/be/test/format_v2/table/iceberg_reader_test.cpp
+++ b/be/test/format_v2/table/iceberg_reader_test.cpp
@@ -31,8 +31,10 @@
#include <optional>
#include <string>
#include <typeinfo>
+#include <utility>
#include <vector>
+#include "common/config.h"
#include "common/consts.h"
#include "core/assert_cast.h"
#include "core/block/block.h"
@@ -65,6 +67,7 @@
#include "runtime/runtime_profile.h"
#include "runtime/runtime_state.h"
#include "storage/segment/condition_cache.h"
+#include "util/debug_points.h"
namespace doris::format {
namespace {
@@ -426,6 +429,24 @@ int64_t write_iceberg_deletion_vector_file(const
std::string& file_path,
return static_cast<int64_t>(blob.size());
}
+class ScopedDebugPoint {
+public:
+ explicit ScopedDebugPoint(std::string name)
+ : _name(std::move(name)),
_enable_debug_points(config::enable_debug_points) {
+ config::enable_debug_points = true;
+ DebugPoints::instance()->add(_name);
+ }
+
+ ~ScopedDebugPoint() {
+ DebugPoints::instance()->remove(_name);
+ config::enable_debug_points = _enable_debug_points;
+ }
+
+private:
+ std::string _name;
+ bool _enable_debug_points;
+};
+
Block build_table_block(const std::vector<ColumnDefinition>& columns) {
Block block;
for (const auto& column : columns) {
@@ -1258,6 +1279,28 @@ TEST(IcebergV2ReaderTest,
IcebergDeletionVectorRejectsMultipleDeleteFiles) {
EXPECT_FALSE(status.ok());
}
+TEST(IcebergV2ReaderTest, IcebergDeletionVectorRejectsMissingRange) {
+ TIcebergDeleteFileDesc delete_file;
+ delete_file.__set_content(3);
+ delete_file.__set_path("dv.bin");
+
+ TTableFormatFileDesc table_format_desc;
+ TIcebergFileDesc iceberg_desc;
+ iceberg_desc.__set_format_version(2);
+ iceberg_desc.__set_delete_files({delete_file});
+ table_format_desc.__set_iceberg_params(iceberg_desc);
+
+ IcebergTableReaderDeleteFileTestHelper reader;
+ DeleteFileDesc desc;
+ bool has_delete_file = false;
+ auto status = reader.parse_deletion_vector_file(table_format_desc, &desc,
&has_delete_file);
+
+ EXPECT_FALSE(status.ok());
+ EXPECT_TRUE(status.is<ErrorCode::INTERNAL_ERROR>());
+ EXPECT_NE(status.to_string().find("missing content offset or length"),
std::string::npos);
+ EXPECT_FALSE(has_delete_file);
+}
+
TEST(IcebergV2ReaderTest, IcebergTableReaderAppliesDeletionVectorFile) {
const auto test_dir =
std::filesystem::temp_directory_path() /
"doris_iceberg_deletion_vector_file_test";
@@ -1305,6 +1348,155 @@ TEST(IcebergV2ReaderTest,
IcebergTableReaderAppliesDeletionVectorFile) {
std::filesystem::remove_all(test_dir);
}
+TEST(IcebergV2ReaderTest,
IcebergTableReaderReportsInjectedDeletionVectorReadError) {
+ const auto test_dir = std::filesystem::temp_directory_path() /
+
"doris_iceberg_v2_deletion_vector_injected_read_error_test";
+ std::filesystem::remove_all(test_dir);
+ std::filesystem::create_directories(test_dir);
+
+ const auto file_path = (test_dir / "split.parquet").string();
+ const auto dv_path = (test_dir / "delete-vector.bin").string();
+ write_int_pair_parquet_file(file_path, {1, 2, 3}, {10, 20, 30}, {"one",
"two", "three"});
+ const auto dv_size = write_iceberg_deletion_vector_file(dv_path, {0});
+
+ std::vector<ColumnDefinition> projected_columns;
+ projected_columns.push_back(make_table_column(0, "id",
std::make_shared<DataTypeInt32>()));
+
+ RuntimeProfile profile("test_profile");
+ RuntimeState state {TQueryOptions(), TQueryGlobals()};
+ auto scan_params = make_local_parquet_scan_params();
+ io::FileReaderStats file_reader_stats;
+ io::FileCacheStatistics file_cache_stats;
+ auto io_ctx = make_io_context(&file_reader_stats, &file_cache_stats);
+ ShardedKVCache cache(1);
+ doris::format::iceberg::IcebergTableReader reader;
+ ASSERT_TRUE(reader.init({
+ .projected_columns = projected_columns,
+ .conjuncts = {},
+ .format = FileFormat::PARQUET,
+ .scan_params = &scan_params,
+ .io_ctx = io_ctx,
+ .runtime_state = &state,
+ .scanner_profile = &profile,
+ })
+ .ok());
+
+ auto split_options = build_split_options(file_path);
+ split_options.cache = &cache;
+
split_options.current_range.__set_table_format_params(make_iceberg_table_format_desc(
+ file_path, {make_iceberg_deletion_vector(dv_path, 0, dv_size)}));
+
+ ScopedDebugPoint debug_point("TableReader.parse_deletion_vector.io_error");
+ auto status = reader.prepare_split(split_options);
+
+ EXPECT_FALSE(status.ok());
+ EXPECT_NE(status.to_string().find("injected format v2 deletion vector read
failure"),
+ std::string::npos);
+ ASSERT_TRUE(reader.close().ok());
+ std::filesystem::remove_all(test_dir);
+}
+
+TEST(IcebergV2ReaderTest, IcebergTableReaderStopsDuringDeletionVectorRead) {
+ const auto test_dir = std::filesystem::temp_directory_path() /
+ "doris_iceberg_v2_deletion_vector_should_stop_test";
+ std::filesystem::remove_all(test_dir);
+ std::filesystem::create_directories(test_dir);
+
+ const auto file_path = (test_dir / "split.parquet").string();
+ const auto dv_path = (test_dir / "delete-vector.bin").string();
+ write_int_pair_parquet_file(file_path, {1, 2, 3}, {10, 20, 30}, {"one",
"two", "three"});
+ const auto dv_size = write_iceberg_deletion_vector_file(dv_path, {0});
+
+ std::vector<ColumnDefinition> projected_columns;
+ projected_columns.push_back(make_table_column(0, "id",
std::make_shared<DataTypeInt32>()));
+
+ RuntimeProfile profile("test_profile");
+ RuntimeState state {TQueryOptions(), TQueryGlobals()};
+ auto scan_params = make_local_parquet_scan_params();
+ io::FileReaderStats file_reader_stats;
+ io::FileCacheStatistics file_cache_stats;
+ auto io_ctx = make_io_context(&file_reader_stats, &file_cache_stats);
+ ShardedKVCache cache(1);
+ doris::format::iceberg::IcebergTableReader reader;
+ ASSERT_TRUE(reader.init({
+ .projected_columns = projected_columns,
+ .conjuncts = {},
+ .format = FileFormat::PARQUET,
+ .scan_params = &scan_params,
+ .io_ctx = io_ctx,
+ .runtime_state = &state,
+ .scanner_profile = &profile,
+ })
+ .ok());
+
+ auto split_options = build_split_options(file_path);
+ split_options.cache = &cache;
+
split_options.current_range.__set_table_format_params(make_iceberg_table_format_desc(
+ file_path, {make_iceberg_deletion_vector(dv_path, 0, dv_size)}));
+
+ ScopedDebugPoint
debug_point("TableReader.parse_deletion_vector.should_stop");
+ auto status = reader.prepare_split(split_options);
+
+ EXPECT_TRUE(status.is<ErrorCode::END_OF_FILE>());
+ EXPECT_NE(status.to_string().find("stop read"), std::string::npos);
+ ASSERT_TRUE(reader.close().ok());
+ std::filesystem::remove_all(test_dir);
+}
+
+TEST(IcebergV2ReaderTest,
IcebergTableReaderRejectsCorruptDeletionVectorPayload) {
+ const auto test_dir =
+ std::filesystem::temp_directory_path() /
"doris_iceberg_v2_corrupt_dv_test";
+ std::filesystem::remove_all(test_dir);
+ std::filesystem::create_directories(test_dir);
+
+ const auto file_path = (test_dir / "split.parquet").string();
+ const auto dv_path = (test_dir / "delete-vector.bin").string();
+ write_int_pair_parquet_file(file_path, {1, 2, 3}, {10, 20, 30}, {"one",
"two", "three"});
+ std::vector<char> corrupted(12, 0);
+ BigEndian::Store32(corrupted.data(), 4);
+ memcpy(corrupted.data() + 4, "BAD!", 4);
+ {
+ std::ofstream output(dv_path, std::ios::binary);
+ ASSERT_TRUE(output.is_open());
+ output.write(corrupted.data(),
static_cast<std::streamsize>(corrupted.size()));
+ ASSERT_TRUE(output.good());
+ }
+
+ std::vector<ColumnDefinition> projected_columns;
+ projected_columns.push_back(make_table_column(0, "id",
std::make_shared<DataTypeInt32>()));
+
+ RuntimeProfile profile("test_profile");
+ RuntimeState state {TQueryOptions(), TQueryGlobals()};
+ auto scan_params = make_local_parquet_scan_params();
+ io::FileReaderStats file_reader_stats;
+ io::FileCacheStatistics file_cache_stats;
+ auto io_ctx = make_io_context(&file_reader_stats, &file_cache_stats);
+ ShardedKVCache cache(1);
+ doris::format::iceberg::IcebergTableReader reader;
+ ASSERT_TRUE(reader.init({
+ .projected_columns = projected_columns,
+ .conjuncts = {},
+ .format = FileFormat::PARQUET,
+ .scan_params = &scan_params,
+ .io_ctx = io_ctx,
+ .runtime_state = &state,
+ .scanner_profile = &profile,
+ })
+ .ok());
+
+ auto split_options = build_split_options(file_path);
+ split_options.cache = &cache;
+
split_options.current_range.__set_table_format_params(make_iceberg_table_format_desc(
+ file_path,
+ {make_iceberg_deletion_vector(dv_path, 0,
static_cast<int64_t>(corrupted.size()))}));
+ auto status = reader.prepare_split(split_options);
+
+ EXPECT_TRUE(status.is<ErrorCode::DATA_QUALITY_ERROR>());
+ EXPECT_NE(status.to_string().find("magic number mismatch"),
std::string::npos);
+ ASSERT_TRUE(reader.close().ok());
+ std::filesystem::remove_all(test_dir);
+}
+
TEST(IcebergV2ReaderTest,
IcebergTableReaderDoesNotPushDownAggregateWithDeletes) {
const auto test_dir =
std::filesystem::temp_directory_path() /
"doris_iceberg_aggregate_delete_test";
diff --git a/regression-test/pipeline/external/conf/be.conf
b/regression-test/pipeline/external/conf/be.conf
index f29f342366e..beea7be7175 100644
--- a/regression-test/pipeline/external/conf/be.conf
+++ b/regression-test/pipeline/external/conf/be.conf
@@ -53,7 +53,8 @@ priority_networks=172.19.0.0/24
enable_fuzzy_mode=true
max_depth_of_expr_tree=200
enable_feature_binlog=true
-max_sys_mem_available_low_water_mark_bytes=69206016
+mem_limit=35%
+max_sys_mem_available_low_water_mark_bytes=2147483648
user_files_secure_path=/
enable_debug_points=true
# debug scanner context dead loop
diff --git a/regression-test/pipeline/external/conf/fe.conf
b/regression-test/pipeline/external/conf/fe.conf
index 365c0b93375..fc818a862a1 100644
--- a/regression-test/pipeline/external/conf/fe.conf
+++ b/regression-test/pipeline/external/conf/fe.conf
@@ -28,7 +28,7 @@ DATE = `date +%Y%m%d-%H%M%S`
JAVA_OPTS="-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=$DORIS_HOME/log/fe.jmap -XX:+UseMembar -XX:SurvivorRatio=8
-XX:MaxTenuringThreshold=7 -XX:+PrintGCDateStamps -XX:+PrintGCDetails
-XX:+PrintClassHistogramAfterFullGC -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
-XX:+CMSClassUnloadingEnabled -XX:-CMSParallelRemarkEnabled
-XX:CMSInitiatingOccupancyFraction=80 -XX:SoftRefLRUPolicyMSPerMB=0
-Xloggc:$DORIS_HOME/log/fe.gc.log.$DATE
-Dcom.mysql.cj.disableAbandonedConnectionCleanup=true"
# For jdk 17+, this JAVA_OPTS will be used as default JVM options
-JAVA_OPTS_FOR_JDK_17="-Dfile.encoding=UTF-8
-Djavax.security.auth.useSubjectCredsOnly=false -Xmx8192m -Xms8192m
-XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$LOG_DIR
-Xlog:gc*,classhisto*=trace:$LOG_DIR/fe.gc.log.$CUR_DATE:time,uptime:filecount=10,filesize=50M
-Darrow.enable_null_check_for_get=false
--add-opens=java.base/java.lang=ALL-UNNAMED
--add-opens=java.base/java.lang.invoke=ALL-UNNAMED
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.
[...]
+JAVA_OPTS_FOR_JDK_17="-Dfile.encoding=UTF-8
-Djavax.security.auth.useSubjectCredsOnly=false -Xmx4096m -XX:+UseG1GC
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$LOG_DIR
-Xlog:gc*,classhisto*=trace:$LOG_DIR/fe.gc.log.$CUR_DATE:time,uptime:filecount=10,filesize=50M
-Darrow.enable_null_check_for_get=false
--add-opens=java.base/java.lang=ALL-UNNAMED
--add-opens=java.base/java.lang.invoke=ALL-UNNAMED
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED
--add-opens=java.base/java.io=ALL-UNN [...]
##
## the lowercase properties are read by main program.
diff --git
a/regression-test/suites/external_table_p0/iceberg/test_iceberg_deletion_vector.groovy
b/regression-test/suites/external_table_p0/iceberg/test_iceberg_deletion_vector.groovy
index a870779b259..8136df3cef4 100644
---
a/regression-test/suites/external_table_p0/iceberg/test_iceberg_deletion_vector.groovy
+++
b/regression-test/suites/external_table_p0/iceberg/test_iceberg_deletion_vector.groovy
@@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
-suite("test_iceberg_deletion_vector", "p0,external") {
+suite("test_iceberg_deletion_vector", "p0,external,nonConcurrent") {
String enabled = context.config.otherConfigs.get("enableIcebergTest")
if (enabled == null || !enabled.equalsIgnoreCase("true")) {
logger.info("disable iceberg test.")
@@ -328,6 +328,33 @@ class IcebergRestCatalog {
qt_q3 """ SELECT * FROM dv_test_orc ORDER BY id; """
qt_no_delete """ SELECT * FROM dv_test_no_delete ORDER BY id; """
+ def enableFileScannerV2Rows = sql """SHOW VARIABLES LIKE
'enable_file_scanner_v2'"""
+ assertTrue(enableFileScannerV2Rows.size() > 0,
+ "Session variable enable_file_scanner_v2 is not found")
+ String originalEnableFileScannerV2 =
enableFileScannerV2Rows[0][1].toString()
+ try {
+ sql """set enable_file_scanner_v2=false"""
+ GetDebugPoint().clearDebugPointsForAllBEs()
+ GetDebugPoint().enableDebugPointForAllBEs(
+ "IcebergDeleteFileReader.read_deletion_vector.io_error")
+ test {
+ sql """ SELECT count(*) FROM dv_test; """
+ exception "injected Iceberg deletion vector read failure"
+ }
+
+ sql """set enable_file_scanner_v2=true"""
+ GetDebugPoint().clearDebugPointsForAllBEs()
+ GetDebugPoint().enableDebugPointForAllBEs(
+ "TableReader.parse_deletion_vector.io_error")
+ test {
+ sql """ SELECT count(*) FROM dv_test; """
+ exception "injected format v2 deletion vector read failure"
+ }
+ } finally {
+ GetDebugPoint().clearDebugPointsForAllBEs()
+ sql """set enable_file_scanner_v2=${originalEnableFileScannerV2}"""
+ }
+
// Delete-type matrix checks cover equality-only, position-only, DV-only,
DV+position,
// and DV+equality combinations.
qt_equality_only """ SELECT * FROM dv_delete_matrix_equality_only ORDER BY
id; """
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]