This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.1 by this push:
new facf9cc50d4 [fix](virtual slot) Materialize constant virtual columns
before caching (#67112)
facf9cc50d4 is described below
commit facf9cc50d47174571f1525feb67fdce38fb464b
Author: foxtail463 <[email protected]>
AuthorDate: Wed Aug 26 10:41:07 2026 +0800
[fix](virtual slot) Materialize constant virtual columns before caching
(#67112)
Problem Summary:
Virtual-column expressions may return ColumnConst, including all-NULL
nullable constants. SegmentIterator previously stored these results
directly in the
output block, while downstream consumers expect row-aligned concrete
columns. This could cause type assertion failures during CSE
virtual-column queries with
short-circuit evaluation.
Solution:
Materialize constant expression results with
convert_to_full_column_if_const() before caching them in the
virtual-column slot.
Co-authored-by: yangtao555 <[email protected]>
---
be/src/storage/segment/segment_iterator.cpp | 3 +
.../segment_iterator_virtual_column_test.cpp | 82 ++++++++++++++++++++++
2 files changed, 85 insertions(+)
diff --git a/be/src/storage/segment/segment_iterator.cpp
b/be/src/storage/segment/segment_iterator.cpp
index c4787cb0968..8198e00b674 100644
--- a/be/src/storage/segment/segment_iterator.cpp
+++ b/be/src/storage/segment/segment_iterator.cpp
@@ -3406,6 +3406,9 @@ Status
SegmentIterator::_materialization_of_virtual_column(Block* block) {
ColumnPtr result_column;
RETURN_IF_ERROR(column_expr->execute(block, result_column));
+ // The materialized value is cached in the block and later
consumed as the slot's
+ // concrete column type, so do not let a ColumnConst cross this
boundary.
+ result_column = result_column->convert_to_full_column_if_const();
block->replace_by_position(idx_in_block, std::move(result_column));
if (block->get_by_position(idx_in_block).column->size() == 0) {
LOG_WARNING("Result of expr column {} is empty. cid {},
idx_in_block {}",
diff --git a/be/test/storage/segment/segment_iterator_virtual_column_test.cpp
b/be/test/storage/segment/segment_iterator_virtual_column_test.cpp
new file mode 100644
index 00000000000..489b0125d52
--- /dev/null
+++ b/be/test/storage/segment/segment_iterator_virtual_column_test.cpp
@@ -0,0 +1,82 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "core/column/column_nothing.h"
+#include "core/data_type/data_type_nullable.h"
+#include "core/data_type/data_type_number.h"
+#include "exprs/vexpr_context.h"
+#include "exprs/vliteral.h"
+#include "storage/olap_common.h"
+#include "storage/schema.h"
+#include "storage/segment/segment.h"
+#include "storage/segment/segment_iterator.h"
+#include "storage/tablet/tablet_schema.h"
+
+namespace doris::segment_v2 {
+namespace {
+
+TabletSchemaSPtr make_tablet_schema() {
+ TabletSchemaPB schema_pb;
+ schema_pb.set_keys_type(KeysType::DUP_KEYS);
+ auto* key_column = schema_pb.add_column();
+ key_column->set_unique_id(0);
+ key_column->set_name("id");
+ key_column->set_type("INT");
+ key_column->set_is_key(true);
+ key_column->set_is_nullable(false);
+ auto* virtual_column = schema_pb.add_column();
+ virtual_column->set_unique_id(1);
+ virtual_column->set_name("virtual_column");
+ virtual_column->set_type("DOUBLE");
+ virtual_column->set_is_key(false);
+ virtual_column->set_is_nullable(true);
+ auto tablet_schema = std::make_shared<TabletSchema>();
+ tablet_schema->init_from_pb(schema_pb);
+ return tablet_schema;
+}
+
+} // namespace
+
+TEST(SegmentIteratorVirtualColumnTest,
MaterializationExpandsConstNullableResult) {
+ auto tablet_schema = make_tablet_schema();
+ auto segment = std::make_shared<Segment>(0, RowsetId(), tablet_schema,
InvertedIndexFileInfo());
+ auto schema = std::make_shared<Schema>(tablet_schema->columns(),
std::vector<ColumnId> {0, 1});
+ SegmentIterator iterator(segment, schema);
+
+ auto type =
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeFloat64>());
+ auto expr = std::make_shared<VLiteral>(type, Field());
+ iterator._virtual_column_exprs[1] =
std::make_shared<VExprContext>(std::move(expr));
+ iterator._vir_cid_to_idx_in_block[1] = 1;
+
+ Block block;
+ block.insert({ColumnInt32::create(4, 1),
std::make_shared<DataTypeInt32>(), "id"});
+ block.insert({ColumnNothing::create(0), type, "virtual_column"});
+
+ ASSERT_TRUE(iterator._materialization_of_virtual_column(&block).ok());
+ const auto& result = block.get_by_position(1).column;
+ EXPECT_FALSE(is_column_const(*result));
+ const auto* nullable = check_and_get_column<ColumnNullable>(result.get());
+ ASSERT_NE(nullable, nullptr);
+ EXPECT_EQ(nullable->size(), 4);
+ EXPECT_TRUE(nullable->only_null());
+}
+
+} // namespace doris::segment_v2
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]