This is an automated email from the ASF dual-hosted git repository.
Mryange 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 c611239b7a3 [opt](exec) Avoid copies when publishing projection
results (#66085)
c611239b7a3 is described below
commit c611239b7a3ee0c42c61136248a8fda44a838f43
Author: Mryange <[email protected]>
AuthorDate: Fri Jul 31 16:14:46 2026 +0800
[opt](exec) Avoid copies when publishing projection results (#66085)
### What problem does this PR solve?
Projection results were converted to mutable columns before being
published. Shared results such as SlotRef columns therefore triggered
COW clones and full-column copies.
This change keeps scoped output-block reuse while moving exclusive
results directly and publishing shared immutable columns after restore.
It also makes `ColumnConst` and `ColumnVariant` ownership checks include
their nested columns, and uses actual peak memory tracking instead of
charging shared input buffers as projection allocations.
### Release note
None
### Check List (For Author)
- Test <!-- At least one of them must be included. -->
- [ ] Regression test
- [ ] 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.
- [ ] Yes. <!-- Explain the behavior change -->
- Does this need documentation?
- [ ] 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/core/column/column_const.h | 2 +
be/src/core/column/column_variant.cpp | 14 +++
be/src/core/column/column_variant.h | 2 +
be/src/exec/operator/operator.cpp | 109 ++++++++++-----------
be/src/exec/operator/operator.h | 4 -
be/src/exec/scan/scanner.cpp | 65 ++++++------
be/test/core/column/column_const_test.cpp | 9 ++
be/test/core/column/column_nullable_test.cpp | 7 ++
be/test/core/column/column_variant_test.cpp | 38 +++++--
be/test/exec/operator/operator_projection_test.cpp | 81 +++++++++++++++
be/test/exec/scan/scanner_late_arrival_rf_test.cpp | 45 +++++++++
11 files changed, 279 insertions(+), 97 deletions(-)
diff --git a/be/src/core/column/column_const.h
b/be/src/core/column/column_const.h
index 834bfc2a08c..f4a373f3178 100644
--- a/be/src/core/column/column_const.h
+++ b/be/src/core/column/column_const.h
@@ -119,6 +119,8 @@ public:
bool is_variable_length() const override { return
data->is_variable_length(); }
+ bool is_exclusive() const override { return IColumn::is_exclusive() &&
data->is_exclusive(); }
+
std::string get_name() const override { return "Const(" + data->get_name()
+ ")"; }
void resize(size_t new_size) override { s = new_size; }
diff --git a/be/src/core/column/column_variant.cpp
b/be/src/core/column/column_variant.cpp
index c1ae100bf47..88d72ee5d91 100644
--- a/be/src/core/column/column_variant.cpp
+++ b/be/src/core/column/column_variant.cpp
@@ -829,6 +829,20 @@ size_t ColumnVariant::allocated_bytes() const {
return res;
}
+bool ColumnVariant::is_exclusive() const {
+ if (!IColumn::is_exclusive()) {
+ return false;
+ }
+ for (const auto& entry : subcolumns) {
+ for (const auto& part : entry->data.data) {
+ if (!part->is_exclusive()) {
+ return false;
+ }
+ }
+ }
+ return serialized_sparse_column->is_exclusive() &&
serialized_doc_value_column->is_exclusive();
+}
+
void ColumnVariant::mutate_subcolumns() {
for (auto& entry : subcolumns) {
for (auto& part : entry->data.data) {
diff --git a/be/src/core/column/column_variant.h
b/be/src/core/column/column_variant.h
index a650555f49e..d4e7e005494 100644
--- a/be/src/core/column/column_variant.h
+++ b/be/src/core/column/column_variant.h
@@ -469,6 +469,8 @@ public:
bool has_enough_capacity(const IColumn& src) const override { return
false; }
+ bool is_exclusive() const override;
+
void mutate_subcolumns() override;
void for_each_subcolumn(ColumnCallback callback) const override;
diff --git a/be/src/exec/operator/operator.cpp
b/be/src/exec/operator/operator.cpp
index 1f255078b5d..cfc9d664511 100644
--- a/be/src/exec/operator/operator.cpp
+++ b/be/src/exec/operator/operator.cpp
@@ -323,63 +323,45 @@ Status OperatorXBase::do_projections(RuntimeState* state,
Block* origin_block,
if (rows == 0) {
return Status::OK();
}
- Block input_block = *origin_block;
-
- size_t bytes_usage = 0;
- ColumnsWithTypeAndName new_columns;
- for (const auto& projections : local_state->_intermediate_projections) {
- if (projections.empty()) {
- return Status::InternalError("meet empty intermediate projection,
node id: {}",
- node_id());
- }
- new_columns.resize(projections.size());
- for (int i = 0; i < projections.size(); i++) {
- RETURN_IF_ERROR(projections[i]->execute(&input_block,
new_columns[i]));
- if (new_columns[i].column->size() != rows) {
- return Status::InternalError(
- "intermediate projection result column size {} not
equal input rows {}, "
- "expr: {}",
- new_columns[i].column->size(), rows,
- projections[i]->root()->debug_string());
- }
- }
- Block tmp_block {new_columns};
- bytes_usage += tmp_block.allocated_bytes();
- input_block.swap(tmp_block);
- }
-
- if (input_block.rows() != rows) {
- return Status::InternalError(
- "after intermediate projections input block rows {} not equal
origin rows {}, "
- "input_block: {}",
- input_block.rows(), rows, input_block.dump_structure());
- }
- auto insert_column_datas = [&](auto& to, ColumnPtr& from, size_t rows) {
- if (is_column_nullable(*to) && !is_column_nullable(*from)) {
- if (_keep_origin || !from->is_exclusive()) {
- auto& null_column = reinterpret_cast<ColumnNullable&>(*to);
- null_column.get_nested_column().insert_range_from(*from, 0,
rows);
- null_column.get_null_map_column().get_data().resize_fill(rows,
0);
- bytes_usage += null_column.allocated_bytes();
- } else {
- to = make_nullable(from, false)->assert_mutable();
+ SCOPED_PEAK_MEM(&local_state->_estimate_memory_usage);
+
+ {
+ Block input_block = *origin_block;
+
+ ColumnsWithTypeAndName new_columns;
+ for (const auto& projections : local_state->_intermediate_projections)
{
+ if (projections.empty()) {
+ return Status::InternalError("meet empty intermediate
projection, node id: {}",
+ node_id());
}
- } else {
- if (_keep_origin || !from->is_exclusive()) {
- to->insert_range_from(*from, 0, rows);
- bytes_usage += from->allocated_bytes();
- } else {
- to = from->assert_mutable();
+ new_columns.resize(projections.size());
+ for (int i = 0; i < projections.size(); i++) {
+ RETURN_IF_ERROR(projections[i]->execute(&input_block,
new_columns[i]));
+ if (new_columns[i].column->size() != rows) {
+ return Status::InternalError(
+ "intermediate projection result column size {} not
equal input rows "
+ "{}, expr: {}",
+ new_columns[i].column->size(), rows,
+ projections[i]->root()->debug_string());
+ }
}
+ Block tmp_block {new_columns};
+ input_block.swap(tmp_block);
}
- };
- auto scoped_mutable_block =
VectorizedUtils::build_scoped_mutable_mem_reuse_block(
- output_block, *_output_row_descriptor);
- auto& mutable_block = scoped_mutable_block.mutable_block();
- auto& mutable_columns = mutable_block.mutable_columns();
- if (rows != 0) {
+ if (input_block.rows() != rows) {
+ return Status::InternalError(
+ "after intermediate projections input block rows {} not
equal origin rows {}, "
+ "input_block: {}",
+ input_block.rows(), rows, input_block.dump_structure());
+ }
+
+ auto scoped_mutable_block =
VectorizedUtils::build_scoped_mutable_mem_reuse_block(
+ output_block, *_output_row_descriptor);
+ auto& mutable_columns = scoped_mutable_block.mutable_columns();
DCHECK_EQ(mutable_columns.size(), local_state->_projections.size()) <<
debug_string();
+ Columns shared_columns(mutable_columns.size());
+
for (int i = 0; i < mutable_columns.size(); ++i) {
ColumnPtr column_ptr;
RETURN_IF_ERROR(local_state->_projections[i]->execute(&input_block,
column_ptr));
@@ -390,12 +372,27 @@ Status OperatorXBase::do_projections(RuntimeState* state,
Block* origin_block,
local_state->_projections[i]->root()->debug_string());
}
column_ptr = column_ptr->convert_to_full_column_if_const();
- bytes_usage += column_ptr->allocated_bytes();
- insert_column_datas(mutable_columns[i], column_ptr, rows);
+ if (is_column_nullable(*mutable_columns[i]) &&
!is_column_nullable(*column_ptr)) {
+ column_ptr = make_nullable(column_ptr, false);
+ }
+ if (column_ptr->is_exclusive()) {
+ mutable_columns[i] = IColumn::mutate(std::move(column_ptr));
+ } else {
+ shared_columns[i] = std::move(column_ptr);
+ }
+ }
+
+ scoped_mutable_block.restore();
+ for (int i = 0; i < shared_columns.size(); ++i) {
+ if (shared_columns[i]) {
+ output_block->replace_by_position(i,
std::move(shared_columns[i]));
+ }
}
- DCHECK(mutable_block.rows() == rows);
}
- local_state->_estimate_memory_usage += bytes_usage;
+
+ origin_block->clear_column_data(
+
local_state->_parent->intermediate_row_desc().num_materialized_slots());
+ DCHECK_EQ(output_block->rows(), rows);
return Status::OK();
}
diff --git a/be/src/exec/operator/operator.h b/be/src/exec/operator/operator.h
index 4654ee7e542..becffbb171e 100644
--- a/be/src/exec/operator/operator.h
+++ b/be/src/exec/operator/operator.h
@@ -1014,10 +1014,6 @@ protected:
std::string _op_name;
int _parallel_tasks = 0;
- //_keep_origin is used to avoid copying during projection,
- // currently set to false only in the nestloop join.
- bool _keep_origin = true;
-
// _blockable is true if the operator contains expressions that may block
execution
bool _blockable = false;
};
diff --git a/be/src/exec/scan/scanner.cpp b/be/src/exec/scan/scanner.cpp
index 3eaf5ee77eb..cbdf5fa7eee 100644
--- a/be/src/exec/scan/scanner.cpp
+++ b/be/src/exec/scan/scanner.cpp
@@ -210,41 +210,50 @@ Status Scanner::_do_projections(Block* origin_block,
Block* output_block) {
if (rows == 0) {
return Status::OK();
}
- Block input_block = *origin_block;
- std::vector<int> result_column_ids;
- for (auto& projections : _intermediate_projections) {
- result_column_ids.resize(projections.size());
- for (int i = 0; i < projections.size(); i++) {
- RETURN_IF_ERROR(projections[i]->execute(&input_block,
&result_column_ids[i]));
- }
- input_block.shuffle_columns(result_column_ids);
- }
-
- DCHECK_EQ(rows, input_block.rows());
- auto scoped_mutable_block =
VectorizedUtils::build_scoped_mutable_mem_reuse_block(
- output_block, *_output_row_descriptor);
- auto& mutable_block = scoped_mutable_block.mutable_block();
+ {
+ Block input_block = *origin_block;
- auto& mutable_columns = mutable_block.mutable_columns();
+ std::vector<int> result_column_ids;
+ for (auto& projections : _intermediate_projections) {
+ result_column_ids.resize(projections.size());
+ for (int i = 0; i < projections.size(); i++) {
+ RETURN_IF_ERROR(projections[i]->execute(&input_block,
&result_column_ids[i]));
+ }
+ input_block.shuffle_columns(result_column_ids);
+ }
- DCHECK_EQ(mutable_columns.size(), _projections.size());
+ DCHECK_EQ(rows, input_block.rows());
+ auto scoped_mutable_block =
VectorizedUtils::build_scoped_mutable_mem_reuse_block(
+ output_block, *_output_row_descriptor);
+ auto& mutable_columns = scoped_mutable_block.mutable_columns();
+ DCHECK_EQ(mutable_columns.size(), _projections.size());
+ Columns shared_columns(mutable_columns.size());
+
+ for (int i = 0; i < mutable_columns.size(); ++i) {
+ ColumnPtr column_ptr;
+ RETURN_IF_ERROR(_projections[i]->execute(&input_block,
column_ptr));
+ column_ptr = column_ptr->convert_to_full_column_if_const();
+ if (mutable_columns[i]->is_nullable() !=
column_ptr->is_nullable()) {
+ throw Exception(ErrorCode::INTERNAL_ERROR, "Nullable
mismatch");
+ }
+ if (column_ptr->is_exclusive()) {
+ mutable_columns[i] = IColumn::mutate(std::move(column_ptr));
+ } else {
+ shared_columns[i] = std::move(column_ptr);
+ }
+ }
- for (int i = 0; i < mutable_columns.size(); ++i) {
- ColumnPtr column_ptr;
- RETURN_IF_ERROR(_projections[i]->execute(&input_block, column_ptr));
- column_ptr = column_ptr->convert_to_full_column_if_const();
- if (mutable_columns[i]->is_nullable() != column_ptr->is_nullable()) {
- throw Exception(ErrorCode::INTERNAL_ERROR, "Nullable mismatch");
+ scoped_mutable_block.restore();
+ for (int i = 0; i < shared_columns.size(); ++i) {
+ if (shared_columns[i]) {
+ output_block->replace_by_position(i,
std::move(shared_columns[i]));
+ }
}
- mutable_columns[i] = IColumn::mutate(std::move(column_ptr));
}
- scoped_mutable_block.restore();
-
- // origin columns was moved into output_block, so we need to set
origin_block to empty columns
- auto empty_columns = origin_block->clone_empty_columns();
- origin_block->set_columns(std::move(empty_columns));
+ origin_block->clear_column_data(
+ _local_state->_parent->row_descriptor().num_materialized_slots());
DCHECK_EQ(output_block->rows(), rows);
return Status::OK();
diff --git a/be/test/core/column/column_const_test.cpp
b/be/test/core/column/column_const_test.cpp
index da0c8e103ec..3056d5276a8 100644
--- a/be/test/core/column/column_const_test.cpp
+++ b/be/test/core/column/column_const_test.cpp
@@ -42,6 +42,15 @@ TEST(ColumnConstTest, TestCreate) {
EXPECT_TRUE(!is_column_const(column_const2->get_data_column()));
}
+TEST(ColumnConstTest, IsExclusiveChecksNestedColumn) {
+ auto column_data = ColumnHelper::create_column<DataTypeInt64>({7});
+ auto column_const = ColumnConst::create(column_data, 3);
+
+ EXPECT_FALSE(column_const->is_exclusive());
+ column_data.reset();
+ EXPECT_TRUE(column_const->is_exclusive());
+}
+
TEST(ColumnConstTest, ConstNullableIsSemanticallyNullable) {
auto nullable_data =
ColumnHelper::create_nullable_column<DataTypeInt64>({7}, {0});
auto const_nullable = ColumnConst::create(nullable_data, 3);
diff --git a/be/test/core/column/column_nullable_test.cpp
b/be/test/core/column/column_nullable_test.cpp
index 77f167c9ea8..088e2071795 100644
--- a/be/test/core/column/column_nullable_test.cpp
+++ b/be/test/core/column/column_nullable_test.cpp
@@ -136,6 +136,13 @@ TEST(ColumnNullableTest,
SharedCreatePreservesImmutableSubcolumns) {
EXPECT_EQ(nullable_ref.get_null_map_column_ptr().get(),
null_map_alias.get());
EXPECT_EQ(nested_alias->size(), 1);
EXPECT_EQ(null_map_alias->size(), 1);
+ EXPECT_FALSE(nullable->is_exclusive());
+
+ nested.reset();
+ nested_alias.reset();
+ null_map.reset();
+ null_map_alias.reset();
+ EXPECT_TRUE(nullable->is_exclusive());
}
TEST(ColumnNullableTest, UpdateCrc32cBatchKeepsBlockInsertable) {
diff --git a/be/test/core/column/column_variant_test.cpp
b/be/test/core/column/column_variant_test.cpp
index 9494212fef5..9275979cab0 100644
--- a/be/test/core/column/column_variant_test.cpp
+++ b/be/test/core/column/column_variant_test.cpp
@@ -1817,16 +1817,36 @@ TEST_F(ColumnVariantTest, is_scalar_variant) {
}
TEST_F(ColumnVariantTest, is_exclusive) {
- auto test_func = [](const auto& source_column) {
- auto src_size = source_column->size();
- EXPECT_TRUE(src_size > 0);
+ auto variant = VariantUtil::construct_basic_varint_column();
+ EXPECT_GT(variant->size(), 0);
+ EXPECT_TRUE(variant->is_exclusive());
- // Test is_exclusive
- bool is_exclusive = source_column->is_exclusive();
- // The result depends on the actual data structure
- EXPECT_TRUE(is_exclusive);
- };
- test_func(column_variant);
+ const auto& subcolumns = variant->get_subcolumns();
+ const auto* root = subcolumns.get_root();
+ ColumnPtr shared_subcolumn;
+ for (const auto& entry : subcolumns) {
+ if (entry.get() != root && !entry->data.data.empty()) {
+ shared_subcolumn = static_cast<const
IColumn::Ptr&>(entry->data.data[0]);
+ break;
+ }
+ }
+ ASSERT_TRUE(shared_subcolumn);
+ EXPECT_FALSE(variant->is_exclusive());
+
+ shared_subcolumn.reset();
+ EXPECT_TRUE(variant->is_exclusive());
+
+ auto shared_sparse_column = variant->get_sparse_column();
+ EXPECT_FALSE(variant->is_exclusive());
+
+ shared_sparse_column.reset();
+ EXPECT_TRUE(variant->is_exclusive());
+
+ auto shared_doc_value_column = variant->get_doc_value_column();
+ EXPECT_FALSE(variant->is_exclusive());
+
+ shared_doc_value_column.reset();
+ EXPECT_TRUE(variant->is_exclusive());
}
TEST_F(ColumnVariantTest, get_root_type) {
diff --git a/be/test/exec/operator/operator_projection_test.cpp
b/be/test/exec/operator/operator_projection_test.cpp
new file mode 100644
index 00000000000..02ddc5f7f9e
--- /dev/null
+++ b/be/test/exec/operator/operator_projection_test.cpp
@@ -0,0 +1,81 @@
+// 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 <cstdint>
+#include <memory>
+#include <vector>
+
+#include "common/object_pool.h"
+#include "core/data_type/data_type_number.h"
+#include "exec/operator/mock_operator.h"
+#include "runtime/runtime_profile.h"
+#include "testutil/column_helper.h"
+#include "testutil/mock/mock_descriptors.h"
+#include "testutil/mock/mock_runtime_state.h"
+#include "testutil/mock/mock_slot_ref.h"
+
+namespace doris {
+
+TEST(OperatorProjectionTest, PublishesSharedColumnAndReusesOutputBlock) {
+ ObjectPool pool;
+ auto data_type = std::make_shared<DataTypeInt32>();
+ auto row_descriptor = MockRowDescriptor({data_type}, &pool);
+
+ MockOperatorX op;
+ op._row_descriptor = row_descriptor;
+ op._output_row_descriptor =
+ std::make_unique<MockRowDescriptor>(std::vector<DataTypePtr>
{data_type}, &pool);
+
+ MockRuntimeState state;
+ const auto max_operator_id = op.operator_id() - 1;
+ state.resize_op_id_to_local_state(max_operator_id);
+ state.set_max_operator_id(max_operator_id);
+ RuntimeProfile parent_profile("parent");
+ LocalStateInfo info {&parent_profile, {}, nullptr, {}, 0};
+ ASSERT_TRUE(op.setup_local_state(&state, info).ok());
+
+ auto* local_state = state.get_local_state(op.operator_id());
+ local_state->_projections = MockSlotRef::create_mock_contexts(0,
data_type);
+
+ std::vector<int32_t> first_values(1 << 18, 7);
+ Block first_origin =
ColumnHelper::create_block<DataTypeInt32>(first_values);
+ const auto* first_column = first_origin.get_by_position(0).column.get();
+ const auto first_allocated_bytes =
static_cast<int64_t>(first_origin.allocated_bytes());
+
+ Block output;
+ ASSERT_TRUE(op.do_projections(&state, &first_origin, &output).ok());
+ EXPECT_EQ(output.get_by_position(0).column.get(), first_column);
+ EXPECT_EQ(output.rows(), first_values.size());
+ EXPECT_EQ(output.get_by_position(0).column->get_int(0), 7);
+ EXPECT_EQ(first_origin.rows(), 0);
+ EXPECT_LT(local_state->estimate_memory_usage(), first_allocated_bytes);
+
+ output.clear_column_data();
+ Block second_origin = ColumnHelper::create_block<DataTypeInt32>({8, 9});
+ const auto* second_column = second_origin.get_by_position(0).column.get();
+
+ ASSERT_TRUE(op.do_projections(&state, &second_origin, &output).ok());
+ EXPECT_EQ(output.get_by_position(0).column.get(), second_column);
+ EXPECT_EQ(output.rows(), 2);
+ EXPECT_EQ(output.get_by_position(0).column->get_int(0), 8);
+ EXPECT_EQ(output.get_by_position(0).column->get_int(1), 9);
+ EXPECT_EQ(second_origin.rows(), 0);
+}
+
+} // namespace doris
diff --git a/be/test/exec/scan/scanner_late_arrival_rf_test.cpp
b/be/test/exec/scan/scanner_late_arrival_rf_test.cpp
index 0d31b694951..960eba0f9c1 100644
--- a/be/test/exec/scan/scanner_late_arrival_rf_test.cpp
+++ b/be/test/exec/scan/scanner_late_arrival_rf_test.cpp
@@ -165,4 +165,49 @@ TEST(ScannerProjectionTest,
merges_padding_block_when_limit_eos_without_extra_fl
EXPECT_EQ(first_output.rows(), 7);
}
+TEST(ScannerProjectionTest, publishes_shared_column_and_reuses_output_block) {
+ ObjectPool pool;
+ auto data_type = std::make_shared<DataTypeInt32>();
+ auto row_descriptor = MockRowDescriptor({data_type}, &pool);
+
+ MockRuntimeState state;
+ state._batch_size = 4;
+
+ auto op = std::make_shared<MockScanOperatorX>();
+ op->_row_descriptor = row_descriptor;
+ op->_output_row_descriptor =
+ std::make_unique<MockRowDescriptor>(std::vector<DataTypePtr>
{data_type}, &pool);
+ op->_output_tuple_desc =
op->_output_row_descriptor->tuple_descriptors()[0];
+
+ auto local_state = std::make_shared<MockScanLocalState>(&state, op.get());
+ local_state->_projections = MockSlotRef::create_mock_contexts(0,
data_type);
+
+ RuntimeProfile profile("scanner");
+ TestScanner scanner(&state, local_state.get(), -1, &profile);
+ ASSERT_TRUE(scanner.init(&state, {}).ok());
+
+ Block first_input = ColumnHelper::create_block<DataTypeInt32>({1, 2});
+ const auto* first_column = first_input.get_by_position(0).column.get();
+ scanner.add_block(std::move(first_input));
+
+ Block second_input = ColumnHelper::create_block<DataTypeInt32>({3, 4});
+ const auto* second_column = second_input.get_by_position(0).column.get();
+ scanner.add_block(std::move(second_input));
+
+ Block output;
+ bool eos = false;
+ ASSERT_TRUE(scanner.get_block_after_projects(&state, &output, &eos).ok());
+ EXPECT_FALSE(eos);
+ EXPECT_EQ(output.get_by_position(0).column.get(), first_column);
+ EXPECT_EQ(output.get_by_position(0).column->get_int(0), 1);
+ EXPECT_EQ(output.get_by_position(0).column->get_int(1), 2);
+
+ output.clear_column_data();
+ ASSERT_TRUE(scanner.get_block_after_projects(&state, &output, &eos).ok());
+ EXPECT_FALSE(eos);
+ EXPECT_EQ(output.get_by_position(0).column.get(), second_column);
+ EXPECT_EQ(output.get_by_position(0).column->get_int(0), 3);
+ EXPECT_EQ(output.get_by_position(0).column->get_int(1), 4);
+}
+
} // namespace doris
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]