Repository: StarRocks/starrocks
Pull request #56662: [Feature] (COW Part3) Support more Cow mutate cases
Author: login: LiShuMing
Labels: name: version:3.5
Created: 2025-03-06T08:00:57Z | Merged: 2025-03-12T02:22:14Z

## Why I'm doing:

## What I'm doing:
- Use `Cow` to avoid deep copies in more cases;
- Add CowBench case;

Fixes [#issue](https://github.com/StarRocks/starrocks/issues/56147)

## What type of PR is this:

- [ ] BugFix
- [x] Feature
- [ ] Enhancement
- [ ] Refactor
- [ ] UT
- [ ] Doc
- [ ] Tool

Does this PR entail a change in behavior?

- [ ] Yes, this PR will result in a change in behavior.
- [x] No, this PR will not result in a change in behavior.

If yes, please specify the type of change:

- [ ] Interface/UI changes: syntax, type conversion, expression evaluation, display information
- [ ] Parameter changes: default values, similar parameters but with different default values
- [ ] Policy changes: use new policy to replace old one, functionality automatically enabled
- [ ] Feature removed
- [ ] Miscellaneous: upgrade & downgrade compatibility, etc.

## Checklist:

- [x] I have added test cases for my bug fix or my new feature
- [ ] This pr needs user documentation (for new or modified features or behaviors)
  - [ ] I have added documentation for my new feature or new function
- [ ] This is a backport pr

## Bugfix cherry-pick branch check:
- [x] I have checked the version labels which the pr will be auto-backported to the target branch
  - [ ] 3.4
  - [ ] 3.3
  - [ ] 3.2
  - [ ] 3.1
  - [ ] 3.0


Diff:
diff --git a/be/src/bench/CMakeLists.txt b/be/src/bench/CMakeLists.txt
index 45e08dba5710db..e02f6192c96611 100644
--- a/be/src/bench/CMakeLists.txt
+++ b/be/src/bench/CMakeLists.txt
@@ -28,3 +28,4 @@ ADD_BE_BENCH(${SRC_DIR}/bench/hyperscan_vec_bench)
 ADD_BE_BENCH(${SRC_DIR}/bench/mem_equal_bench)
 ADD_BE_BENCH(${SRC_DIR}/bench/bit_unpack_bench)
 ADD_BE_BENCH(${SRC_DIR}/bench/filter_data_bench)
+ADD_BE_BENCH(${SRC_DIR}/bench/cow_bench)
diff --git a/be/src/bench/chunks_sorter_bench.cpp b/be/src/bench/chunks_sorter_bench.cpp
index 2f3fd659e6c062..5852740f7b4fef 100644
--- a/be/src/bench/chunks_sorter_bench.cpp
+++ b/be/src/bench/chunks_sorter_bench.cpp
@@ -79,7 +79,7 @@ class ChunkSorterBase {
         }
         down_cast<NullableColumn*>(column.get())->update_has_null();
 
-        return {column, std::move(expr)};
+        return {std::move(column), std::move(expr)};
     }
 
     static std::tuple<ColumnPtr, std::unique_ptr<ColumnRef>> build_column(const TypeDescriptor& type_desc,
@@ -132,7 +132,7 @@ class ChunkSorterBase {
             }
         }
 
-        return {column, std::move(expr)};
+        return {std::move(column), std::move(expr)};
     }
 
     std::shared_ptr<RuntimeState> _create_runtime_state() {
@@ -258,7 +258,7 @@ static void do_bench(benchmark::State& state, SortAlgorithm sorter_algo, Logical
         case MergeSort: {
             sorter = std::make_unique<ChunksSorterTopn>(suite._runtime_state.get(), &sort_exprs, &asc_arr, &null_first,
                                                         "", 0, limit_rows, TTopNType::ROW_NUMBER, max_buffered_rows,
-                                                        max_buffered_bytes params.max_buffered_chunks);
+                                                        params.max_buffered_chunks);
             expected_rows = limit_rows;
             break;
         }
diff --git a/be/src/bench/cow_bench.cpp b/be/src/bench/cow_bench.cpp
new file mode 100644
index 00000000000000..f5b3d3871c248e
--- /dev/null
+++ b/be/src/bench/cow_bench.cpp
@@ -0,0 +1,347 @@
+// Copyright 2021-present StarRocks, Inc. All rights reserved.
+//
+// Licensed 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
+//
+//     https://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 <benchmark/benchmark.h>
+#include <fmt/format.h>
+#include <gutil/strings/substitute.h>
+#include <testutil/assert.h>
+
+#include "column/binary_column.h"
+#include "column/column_builder.h"
+#include "column/column_helper.h"
+#include "column/datum.h"
+#include "column/map_column.h"
+#include "column/struct_column.h"
+#include "column/vectorized_fwd.h"
+#include "types/logical_type.h"
+#include "util/random.h"
+
+namespace starrocks {
+
+// Run on (104 X 3200.01 MHz CPU s)
+// CPU Caches:
+//   L1 Data 32 KiB (x52)
+//   L1 Instruction 32 KiB (x52)
+//   L2 Unified 1024 KiB (x52)
+//   L3 Unified 36608 KiB (x2)
+// Load Average: 16.93, 28.85, 28.00
+// dst_column size: 4096
+// -------------------------------------------------------------------------------------
+// Benchmark                                           Time             CPU   Iterations
+// -------------------------------------------------------------------------------------
+// bench_func_str_10/1/4096/iterations:100         34244 ns        33013 ns            0
+// dst_column size: 4096
+// bench_func_str_10/2/4096/iterations:100           975 ns          652 ns            0
+// dst_column size: 4096
+// bench_func_str_10/3/4096/iterations:100           434 ns          402 ns            0
+// dst_column size: 4096
+// bench_func_str_100/1/4096/iterations:100       224996 ns       224828 ns            0
+// dst_column size: 4096
+// bench_func_str_100/2/4096/iterations:100         2162 ns         1186 ns            0
+// dst_column size: 4096
+// bench_func_str_100/3/4096/iterations:100         2380 ns         1022 ns            0
+// dst_column size: 4096
+// bench_func_str_1000/1/4096/iterations:100     2230253 ns      2219392 ns            0
+// dst_column size: 4096
+// bench_func_str_1000/2/4096/iterations:100        3089 ns         1692 ns            0
+// dst_column size: 4096
+// bench_func_str_1000/3/4096/iterations:100        2622 ns         1242 ns            0
+// dst_column size: 4096
+// bench_func_decimal/1/4096/iterations:100        23362 ns        23267 ns            0
+// dst_column size: 4096
+// bench_func_decimal/2/4096/iterations:100          684 ns          664 ns            0
+// dst_column size: 4096
+// bench_func_decimal/3/4096/iterations:100          322 ns          299 ns            0
+// dst_column size: 4096
+// bench_func_bigint/1/4096/iterations:100          2027 ns         2028 ns            0
+// dst_column size: 4096
+// bench_func_bigint/2/4096/iterations:100           699 ns          503 ns            0
+// dst_column size: 4096
+// bench_func_bigint/3/4096/iterations:100           338 ns          302 ns            0
+// dst_column size: 4096
+// bench_func_json/1/4096/iterations:100           47890 ns        47770 ns            0
+// dst_column size: 4096
+// bench_func_json/2/4096/iterations:100            1077 ns          722 ns            0
+// dst_column size: 4096
+// bench_func_json/3/4096/iterations:100             717 ns          480 ns            0
+// dst_column size: 4096
+// bench_func_struct/1/4096/iterations:100         24799 ns        24549 ns            0
+// dst_column size: 4096
+// bench_func_struct/2/4096/iterations:100          1931 ns         1374 ns            0
+// dst_column size: 4096
+// bench_func_struct/3/4096/iterations:100          1119 ns          566 ns            0
+// dst_column size: 10
+// bench_func_map/1/4096/iterations:100             2269 ns         2201 ns            0
+// dst_column size: 10
+// bench_func_map/2/4096/iterations:100             1345 ns         1337 ns            0
+// dst_column size: 10
+// bench_func_map/3/4096/iterations:100              434 ns          413 ns            0
+// dst_column size: 4096
+// bench_func_str_100/1/4096/iterations:100        60600 ns        60087 ns            0
+// dst_column size: 4096
+// bench_func_str_100/2/4096/iterations:100         1885 ns          994 ns            0
+// dst_column size: 4096
+// bench_func_str_100/3/4096/iterations:100         1377 ns          820 ns            0
+// dst_column size: 40960
+// bench_func_str_100/1/40960/iterations:100      775279 ns       774957 ns            0
+// dst_column size: 40960
+// bench_func_str_100/2/40960/iterations:100        4461 ns         2352 ns            0
+// dst_column size: 40960
+// bench_func_str_100/3/40960/iterations:100        4794 ns         2484 ns            0
+// dst_column size: 409600
+// bench_func_str_100/1/409600/iterations:10    25968634 ns     25931662 ns            0
+// dst_column size: 409600
+// bench_func_str_100/2/409600/iterations:10        3600 ns         2364 ns            0
+// dst_column size: 409600
+// bench_func_str_100/3/409600/iterations:10        5136 ns         2706 ns            0
+// dst_column size: 4096000
+// bench_func_str_100/1/4096000/iterations:10  257056564 ns    256827582 ns            0
+// dst_column size: 4096000
+// bench_func_str_100/2/4096000/iterations:10       4249 ns         2692 ns            0
+// dst_column size: 4096000
+// bench_func_str_100/3/4096000/iterations:10       4867 ns         3313 ns            0
+
+class COWBench {
+public:
+    void SetUp() {}
+    void TearDown() {}
+
+    COWBench(int mode, int chunk_size) : _mode(mode), _rand(_rd()), _chunk_size(chunk_size) {}
+    COWBench(int mode, int chunk_size, int length)
+            : _mode(mode), _rand(_rd()), _chunk_size(chunk_size), _length(length) {}
+
+    void do_bench(benchmark::State& state, LogicalType type);
+
+private:
+    std::string _rand_str(int length);
+    ColumnPtr _gen_binary_column();
+    ColumnPtr _gen_decimal_column();
+    ColumnPtr _gen_bigint_column();
+    ColumnPtr _gen_json_column();
+    ColumnPtr _gen_struct_column();
+    ColumnPtr _gen_map_column();
+
+    int _mode = 1;
+    std::random_device _rd;
+    std::default_random_engine _rand;
+    int _chunk_size = 4096;
+    int _length = 32;
+};
+
+void COWBench::do_bench(benchmark::State& state, LogicalType type) {
+    ColumnPtr column;
+    switch (type) {
+    case TYPE_VARCHAR:
+        column = _gen_binary_column();
+        break;
+    case TYPE_DECIMAL:
+        column = _gen_decimal_column();
+        break;
+    case TYPE_JSON:
+        column = _gen_json_column();
+        break;
+    case TYPE_STRUCT:
+        column = _gen_struct_column();
+        break;
+    case TYPE_MAP:
+        column = _gen_map_column();
+        break;
+    default:
+        column = _gen_bigint_column();
+    }
+    ColumnPtr dst_column;
+
+    if (_mode == 1) {
+        state.ResumeTiming();
+        dst_column = column->clone();
+        state.PauseTiming();
+    } else if (_mode == 2) {
+        state.ResumeTiming();
+        dst_column = (std::move(*column)).mutate();
+        state.PauseTiming();
+    } else {
+        state.ResumeTiming();
+        dst_column = Column::mutate(std::move(column));
+        state.PauseTiming();
+    }
+    // To avoid optimizing out the benchmark
+    std::cout << "dst_column size: " << dst_column->size() << std::endl;
+}
+
+std::string COWBench::_rand_str(int length) {
+    char tmp;
+    std::string str;
+    for (int j = 0; j < length; j++) {
+        tmp = _rand() % 26;
+        tmp += 'A';
+        str += tmp;
+    }
+    return str;
+}
+
+ColumnPtr COWBench::_gen_binary_column() {
+    auto column = BinaryColumn::create();
+    for (size_t i = 0; i < _chunk_size; i++) {
+        std::string str = _rand_str(_length);
+        column->append_string(str);
+    }
+    return column;
+}
+
+ColumnPtr COWBench::_gen_decimal_column() {
+    auto column = DecimalColumn::create();
+    for (size_t i = 0; i < _chunk_size; i++) {
+        column->append(DecimalV2Value(_rand()));
+    }
+    return column;
+}
+
+ColumnPtr COWBench::_gen_bigint_column() {
+    auto column = Int64Column::create();
+    for (size_t i = 0; i < _chunk_size; i++) {
+        column->append(_rand());
+    }
+    return column;
+}
+
+ColumnPtr COWBench::_gen_json_column() {
+    auto col = JsonColumn::create();
+    for (size_t i = 0; i < _chunk_size; i++) {
+        col->append(JsonValue::parse(R"( {"a": "a"} )").value());
+    }
+    return col;
+}
+
+ColumnPtr COWBench::_gen_map_column() {
+    MapColumn::Ptr column = MapColumn::create(NullableColumn::create(Int32Column::create(), NullColumn::create()),
+                                              NullableColumn::create(Int32Column::create(), NullColumn::create()),
+                                              UInt32Column::create());
+    for (int32_t i = 0; i < 10; i++) {
+        column->append_datum(DatumMap{{i, i + 1}});
+    }
+    return column;
+}
+
+ColumnPtr COWBench::_gen_struct_column() {
+    auto binary_col = BinaryColumn::create();
+    auto int_col = UInt64Column::create();
+    for (size_t i = 0; i < _chunk_size; i++) {
+        std::string str = _rand_str(_length);
+        binary_col->append(Slice(str));
+        int_col->append(i);
+    }
+    auto c0 = NullableColumn::create(std::move(binary_col), NullColumn::create());
+    auto c1 = NullableColumn::create(std::move(int_col), NullColumn::create());
+    Columns fields{std::move(c0), std::move(c1)};
+    std::vector<std::string> field_name{"c0", "c1"};
+    return StructColumn::create(std::move(fields), std::move(field_name));
+}
+
+static void bench_func_str_10(benchmark::State& state) {
+    int mode = state.range(0);
+    int chunk_size = state.range(1);
+    COWBench perf(mode, chunk_size, 10);
+    perf.do_bench(state, TYPE_VARCHAR);
+}
+
+static void bench_func_str_100(benchmark::State& state) {
+    int mode = state.range(0);
+    int chunk_size = state.range(1);
+    COWBench perf(mode, chunk_size, 100);
+    perf.do_bench(state, TYPE_VARCHAR);
+}
+
+static void bench_func_str_1000(benchmark::State& state) {
+    int mode = state.range(0);
+    int chunk_size = state.range(1);
+    COWBench perf(mode, chunk_size, 1000);
+    perf.do_bench(state, TYPE_VARCHAR);
+}
+
+static void bench_func_decimal(benchmark::State& state) {
+    int mode = state.range(0);
+    int chunk_size = state.range(1);
+    COWBench perf(mode, chunk_size);
+    perf.do_bench(state, TYPE_DECIMAL);
+}
+
+static void bench_func_bigint(benchmark::State& state) {
+    int mode = state.range(0);
+    int chunk_size = state.range(1);
+    COWBench perf(mode, chunk_size);
+    perf.do_bench(state, TYPE_BIGINT);
+}
+
+static void bench_func_json(benchmark::State& state) {
+    int mode = state.range(0);
+    int chunk_size = state.range(1);
+    COWBench perf(mode, chunk_size);
+    perf.do_bench(state, TYPE_JSON);
+}
+
+static void bench_func_map(benchmark::State& state) {
+    int mode = state.range(0);
+    int chunk_size = state.range(1);
+    COWBench perf(mode, chunk_size);
+    perf.do_bench(state, TYPE_MAP);
+}
+
+static void bench_func_struct(benchmark::State& state) {
+    int mode = state.range(0);
+    int chunk_size = state.range(1);
+    COWBench perf(mode, chunk_size);
+    perf.do_bench(state, TYPE_STRUCT);
+}
+
+static void process_args1(benchmark::internal::Benchmark* b) {
+    b->Args({1, 4096})->Iterations(100);
+    b->Args({2, 4096})->Iterations(100);
+    b->Args({3, 4096})->Iterations(100);
+}
+
+static void process_args2(benchmark::internal::Benchmark* b) {
+    b->Args({1, 40960})->Iterations(100);
+    b->Args({2, 40960})->Iterations(100);
+    b->Args({3, 40960})->Iterations(100);
+}
+
+static void process_args3(benchmark::internal::Benchmark* b) {
+    b->Args({1, 409600})->Iterations(10);
+    b->Args({2, 409600})->Iterations(10);
+    b->Args({3, 409600})->Iterations(10);
+}
+
+static void process_args4(benchmark::internal::Benchmark* b) {
+    b->Args({1, 4096000})->Iterations(10);
+    b->Args({2, 4096000})->Iterations(10);
+    b->Args({3, 4096000})->Iterations(10);
+}
+
+BENCHMARK(bench_func_str_10)->Apply(process_args1);
+BENCHMARK(bench_func_str_100)->Apply(process_args1);
+BENCHMARK(bench_func_str_1000)->Apply(process_args1);
+BENCHMARK(bench_func_decimal)->Apply(process_args1);
+BENCHMARK(bench_func_bigint)->Apply(process_args1);
+BENCHMARK(bench_func_json)->Apply(process_args1);
+BENCHMARK(bench_func_struct)->Apply(process_args1);
+BENCHMARK(bench_func_map)->Apply(process_args1);
+
+BENCHMARK(bench_func_str_100)->Apply(process_args1);
+BENCHMARK(bench_func_str_100)->Apply(process_args2);
+BENCHMARK(bench_func_str_100)->Apply(process_args3);
+BENCHMARK(bench_func_str_100)->Apply(process_args4);
+
+} // namespace starrocks
+
+BENCHMARK_MAIN();
diff --git a/be/src/bench/hyperscan_vec_bench.cpp b/be/src/bench/hyperscan_vec_bench.cpp
index 41509d7dd67974..a414fdd1751575 100644
--- a/be/src/bench/hyperscan_vec_bench.cpp
+++ b/be/src/bench/hyperscan_vec_bench.cpp
@@ -64,11 +64,11 @@ void HyperScanBench::SetUp() {
     _columns.push_back(std::move(column));
     MutableColumnPtr pattern_data = ColumnHelper::create_column(type_desc, false);
     pattern_data->append_datum(Datum(Slice("-")));
-    auto pattern_column = ConstColumn::create(pattern_data, _num_rows);
+    auto pattern_column = ConstColumn::create(std::move(pattern_data), _num_rows);
     _columns.push_back(std::move(pattern_column));
     MutableColumnPtr rpl_data = ColumnHelper::create_column(type_desc, false);
     rpl_data->append_datum(Datum(Slice("")));
-    auto rpl_column = ConstColumn::create(rpl_data, _num_rows);
+    auto rpl_column = ConstColumn::create(std::move(rpl_data), _num_rows);
     _columns.push_back(std::move(rpl_column));
     _state = std::make_shared<StringFunctionsState>();
     _state->options = std::make_unique<re2::RE2::Options>();
diff --git a/be/src/bench/orc_column_reader_bench.cpp b/be/src/bench/orc_column_reader_bench.cpp
index 64adb56582c861..d39a34ce9b1fc8 100644
--- a/be/src/bench/orc_column_reader_bench.cpp
+++ b/be/src/bench/orc_column_reader_bench.cpp
@@ -260,7 +260,7 @@ static void BM_primitive(benchmark::State& state) {
         orc::RowReader::ReadPosition pos;
         size_t totalNumRows = 0;
         while (rr->next(*batch, &pos)) {
-            MutableColumnPtr column = ColumnHelper::create_column(c0Type, isNullable);
+            ColumnPtr column = ColumnHelper::create_column(c0Type, isNullable);
             CHECK(orcColumnReader->get_next(c0, column, 0, columnSize).ok());
             DCHECK_EQ(columnSize, column->size());
             totalNumRows += columnSize;
diff --git a/be/src/bench/runtime_filter_bench.cpp b/be/src/bench/runtime_filter_bench.cpp
index adb2d339dcdde3..c6a97feb3763fe 100644
--- a/be/src/bench/runtime_filter_bench.cpp
+++ b/be/src/bench/runtime_filter_bench.cpp
@@ -41,7 +41,7 @@ namespace starrocks {
 //   Benchmark_RuntimeFilter_Eval/20000000/100/100  968723953 ns    968634431 ns            1 compute_hash_time(ms)=213 evalute_time(ms)=163 items_per_second=20.6476M/s
 
 static void do_benchmark_hash_partitioned(benchmark::State& state, TRuntimeFilterBuildJoinMode::type join_mode,
-                                          Columns columns, int64_t num_rows, int64_t num_partitions) {
+                                          const Columns& columns, int64_t num_rows, int64_t num_partitions) {
     std::vector<uint32_t> hash_values;
     std::vector<size_t> num_rows_per_partitions(num_partitions, 0);
 
@@ -59,7 +59,7 @@ static void do_benchmark_hash_partitioned(benchmark::State& state, TRuntimeFilte
     running_ctx.use_merged_selection = false;
     running_ctx.compatibility = true;
 
-    std::vector<Column*> column_ptrs;
+    std::vector<const Column*> column_ptrs;
     column_ptrs.reserve(columns.size());
     for (auto& column : columns) {
         column_ptrs.push_back(column.get());
diff --git a/be/src/column/chunk.cpp b/be/src/column/chunk.cpp
index bf110f95fc9de4..d24002d3f8716d 100644
--- a/be/src/column/chunk.cpp
+++ b/be/src/column/chunk.cpp
@@ -274,8 +274,7 @@ std::unique_ptr<Chunk> Chunk::clone_empty_with_schema(size_t size) const {
 std::unique_ptr<Chunk> Chunk::clone_unique() const {
     std::unique_ptr<Chunk> chunk = clone_empty(0);
     for (size_t idx = 0; idx < _columns.size(); idx++) {
-        ColumnPtr column = _columns[idx]->clone();
-        chunk->_columns[idx] = std::move(column);
+        chunk->_columns[idx] = _columns[idx]->clone();
     }
     chunk->_owner_info = _owner_info;
     chunk->_extra_data = std::move(_extra_data);
diff --git a/be/src/column/column_helper.cpp b/be/src/column/column_helper.cpp
index 1088dc00331649..7203a151f5f432 100644
--- a/be/src/column/column_helper.cpp
+++ b/be/src/column/column_helper.cpp
@@ -250,9 +250,9 @@ int64_t ColumnHelper::find_first_not_equal(const Column* column, int64_t target,
 // expression trees' return column should align return type when some return columns maybe diff from the required
 // return type, as well the null flag. e.g., concat_ws returns col from create_const_null_column(), it's type is
 // Nullable(int8), but required return type is nullable(string), so col need align return type to nullable(string).
-ColumnPtr ColumnHelper::align_return_type(const ColumnPtr& old_col, const TypeDescriptor& type_desc, size_t num_rows,
+ColumnPtr ColumnHelper::align_return_type(ColumnPtr&& old_col, const TypeDescriptor& type_desc, size_t num_rows,
                                           const bool is_nullable) {
-    MutableColumnPtr new_column = old_col->clone();
+    MutableColumnPtr new_column = (std::move(*old_col)).mutate();
     if (old_col->only_null()) {
         new_column = ColumnHelper::create_column(type_desc, true);
         new_column->append_nulls(num_rows);
diff --git a/be/src/column/column_helper.h b/be/src/column/column_helper.h
index 7068ea48dd0cd1..176285df52b071 100644
--- a/be/src/column/column_helper.h
+++ b/be/src/column/column_helper.h
@@ -265,7 +265,7 @@ class ColumnHelper {
     // expression trees' return column should align return type when some return columns maybe diff from the required
     // return type, as well the null flag. e.g., concat_ws returns col from create_const_null_column(), it's type is
     // Nullable(int8), but required return type is nullable(string), so col need align return type to nullable(string).
-    static ColumnPtr align_return_type(const ColumnPtr& old_col, const TypeDescriptor& type_desc, size_t num_rows,
+    static ColumnPtr align_return_type(ColumnPtr&& old_col, const TypeDescriptor& type_desc, size_t num_rows,
                                        const bool is_nullable);
 
     // Create a column with specified size, the column will be resized to size
diff --git a/be/src/exec/project_node.cpp b/be/src/exec/project_node.cpp
index b2bf36c8946e14..b0fd7b5cca5368 100644
--- a/be/src/exec/project_node.cpp
+++ b/be/src/exec/project_node.cpp
@@ -149,8 +149,9 @@ Status ProjectNode::get_next(RuntimeState* state, ChunkPtr* chunk, bool* eos) {
         SCOPED_TIMER(_expr_compute_timer);
         for (size_t i = 0; i < _slot_ids.size(); ++i) {
             ASSIGN_OR_RETURN(result_columns[i], _expr_ctxs[i]->evaluate((*chunk).get()));
-            result_columns[i] = ColumnHelper::align_return_type(result_columns[i], _expr_ctxs[i]->root()->type(),
-                                                                (*chunk)->num_rows(), _type_is_nullable[i]);
+            result_columns[i] =
+                    ColumnHelper::align_return_type(std::move(result_columns[i]), _expr_ctxs[i]->root()->type(),
+                                                    (*chunk)->num_rows(), _type_is_nullable[i]);
         }
         RETURN_IF_HAS_ERROR(_expr_ctxs);
     }
diff --git a/be/src/exec/union_node.cpp b/be/src/exec/union_node.cpp
index 8ac6218d2b0ad2..9efcc5e28fa77a 100644
--- a/be/src/exec/union_node.cpp
+++ b/be/src/exec/union_node.cpp
@@ -296,9 +296,10 @@ Status UnionNode::_move_const_chunk(ChunkPtr& dest_chunk) {
 void UnionNode::_clone_column(ChunkPtr& dest_chunk, const ColumnPtr& src_column, const SlotDescriptor* dest_slot,
                               size_t row_count) {
     if (src_column->is_nullable() || !dest_slot->is_nullable()) {
-        dest_chunk->append_column(src_column->clone(), dest_slot->id());
+        dest_chunk->append_column((std::move(*src_column)).mutate(), dest_slot->id());
     } else {
-        ColumnPtr nullable_column = NullableColumn::create(src_column->clone(), NullColumn::create(row_count, 0));
+        ColumnPtr nullable_column =
+                NullableColumn::create((std::move(*src_column)).mutate(), NullColumn::create(row_count, 0));
         dest_chunk->append_column(nullable_column, dest_slot->id());
     }
 }
diff --git a/be/src/exprs/array_map_expr.cpp b/be/src/exprs/array_map_expr.cpp
index b2c6bfe8203e56..e9630003462c7f 100644
--- a/be/src/exprs/array_map_expr.cpp
+++ b/be/src/exprs/array_map_expr.cpp
@@ -193,7 +193,7 @@ StatusOr<ColumnPtr> ArrayMapExpr::evaluate_lambda_expr(ExprContext* context, Chu
         }
         tmp_col->check_or_die();
         ASSIGN_OR_RETURN(column, tmp_col->replicate(aligned_offsets->get_data()));
-        column = ColumnHelper::align_return_type(column, type().children[0], column->size(), true);
+        column = ColumnHelper::align_return_type(std::move(column), type().children[0], column->size(), true);
 
         RETURN_IF_ERROR(column->capacity_limit_reached());
     } else {
@@ -204,7 +204,7 @@ StatusOr<ColumnPtr> ArrayMapExpr::evaluate_lambda_expr(ExprContext* context, Chu
             tmp_col->check_or_die();
             // if result is a const column, we should unpack it first and make it to be the elements column of array column
             column = ColumnHelper::unpack_and_duplicate_const_column(tmp_col->size(), tmp_col);
-            column = ColumnHelper::align_return_type(column, type().children[0], column->size(), true);
+            column = ColumnHelper::align_return_type(std::move(column), type().children[0], column->size(), true);
         } else {
             ChunkAccumulator accumulator(DEFAULT_CHUNK_SIZE);
             RETURN_IF_ERROR(accumulator.push(std::move(cur_chunk)));
@@ -220,7 +220,8 @@ StatusOr<ColumnPtr> ArrayMapExpr::evaluate_lambda_expr(ExprContext* context, Chu
                 }
                 ASSIGN_OR_RETURN(auto tmp_col, context->evaluate(_children[0], tmp_chunk.get()));
                 tmp_col->check_or_die();
-                tmp_col = ColumnHelper::align_return_type(tmp_col, type().children[0], tmp_chunk->num_rows(), true);
+                tmp_col = ColumnHelper::align_return_type(std::move(tmp_col), type().children[0], tmp_chunk->num_rows(),
+                                                          true);
                 if (column == nullptr) {
                     column = tmp_col;
                 } else {
@@ -277,7 +278,7 @@ StatusOr<ColumnPtr> ArrayMapExpr::evaluate_checked(ExprContext* context, Chunk*
         ASSIGN_OR_RETURN(auto child_col, context->evaluate(_children[i], chunk));
         // the column is a null literal.
         if (child_col->only_null()) {
-            return ColumnHelper::align_return_type(child_col, type(), chunk->num_rows(), true);
+            return ColumnHelper::align_return_type(std::move(child_col), type(), chunk->num_rows(), true);
         }
 
         bool is_const = child_col->is_constant();
diff --git a/be/src/exprs/case_expr.cpp b/be/src/exprs/case_expr.cpp
index 5ca7a7c4db426b..e588ee1573e338 100644
--- a/be/src/exprs/case_expr.cpp
+++ b/be/src/exprs/case_expr.cpp
@@ -312,7 +312,7 @@ class VectorizedCaseExpr final : public Expr {
 
         ASSIGN_OR_RETURN(ColumnPtr case_column, _children[0]->evaluate_checked(context, chunk));
         if (ColumnHelper::count_nulls(case_column) == case_column->size()) {
-            return else_column->clone();
+            return Column::mutate(std::move(else_column));
         }
 
         int loop_end = _children.size() - 1;
@@ -338,7 +338,7 @@ class VectorizedCaseExpr final : public Expr {
         }
 
         if (when_columns.empty()) {
-            return else_column->clone();
+            return Column::mutate(std::move(else_column));
         }
         then_columns.emplace_back(else_column);
         size_t size = when_columns[0]->size();
@@ -489,7 +489,7 @@ class VectorizedCaseExpr final : public Expr {
 
             // direct return if first when is all true
             if (when_viewers.empty() && trues_count == when_column->size()) {
-                return then_column->clone();
+                return Column::mutate(std::move(then_column));
             }
 
             when_columns.emplace_back(when_column);
@@ -498,7 +498,7 @@ class VectorizedCaseExpr final : public Expr {
         }
 
         if (when_viewers.empty()) {
-            return else_column->clone();
+            return Column::mutate(std::move(else_column));
         }
         then_columns.emplace_back(else_column);
 
diff --git a/be/src/exprs/cast_expr.cpp b/be/src/exprs/cast_expr.cpp
index 80af40a8b6f96b..c4b763b4fb4d16 100644
--- a/be/src/exprs/cast_expr.cpp
+++ b/be/src/exprs/cast_expr.cpp
@@ -75,7 +75,7 @@ namespace starrocks {
 
 template <LogicalType FromType, LogicalType ToType, bool AllowThrowException = false>
 struct CastFn {
-    static ColumnPtr cast_fn(ColumnPtr& column);
+    static ColumnPtr cast_fn(ColumnPtr&& column);
 };
 
 // clang-format off
@@ -83,13 +83,13 @@ struct CastFn {
 #define SELF_CAST(FROM_TYPE)                                                    \
     template <bool AllowThrowException>                                         \
     struct CastFn<FROM_TYPE, FROM_TYPE, AllowThrowException> {                  \
-        static ColumnPtr cast_fn(ColumnPtr& column) { return column->clone(); } \
+        static ColumnPtr cast_fn(ColumnPtr&& column) { return Column::mutate(std::move(column)); } \
     };
 
 #define UNARY_FN_CAST(FROM_TYPE, TO_TYPE, UNARY_IMPL)                                                        \
     template <bool AllowThrowException>                                                                      \
     struct CastFn<FROM_TYPE, TO_TYPE, AllowThrowException> {                                                 \
-        static ColumnPtr cast_fn(ColumnPtr& column) {                                                        \
+        static ColumnPtr cast_fn(ColumnPtr&& column) {                                                        \
             return VectorizedStrictUnaryFunction<UNARY_IMPL>::template evaluate<FROM_TYPE, TO_TYPE>(column); \
         }                                                                                                    \
     };
@@ -97,7 +97,7 @@ struct CastFn {
 #define UNARY_FN_CAST_VALID(FROM_TYPE, TO_TYPE, UNARY_IMPL)                                                            \
     template <bool AllowThrowException>                                                                                \
     struct CastFn<FROM_TYPE, TO_TYPE, AllowThrowException> {                                                           \
-        static ColumnPtr cast_fn(ColumnPtr& column) {                                                                  \
+        static ColumnPtr cast_fn(ColumnPtr&& column) {                                                                  \
             if constexpr (std::numeric_limits<RunTimeCppType<TO_TYPE>>::max() <                                        \
                           std::numeric_limits<RunTimeCppType<FROM_TYPE>>::max()) {                                     \
                 if constexpr (!AllowThrowException) {                                                                  \
@@ -116,7 +116,7 @@ struct CastFn {
 #define UNARY_FN_CAST_TIME_VALID(FROM_TYPE, TO_TYPE, UNARY_IMPL)                                                    \
     template <bool AllowThrowException>                                                                             \
     struct CastFn<FROM_TYPE, TO_TYPE, AllowThrowException> {                                                        \
-        static ColumnPtr cast_fn(ColumnPtr& column) {                                                               \
+        static ColumnPtr cast_fn(ColumnPtr&& column) {                                                               \
             return VectorizedInputCheckUnaryFunction<UNARY_IMPL, TimeCheck>::template evaluate<FROM_TYPE, TO_TYPE>( \
                     column);                                                                                        \
         }                                                                                                           \
@@ -125,7 +125,7 @@ struct CastFn {
 #define CUSTOMIZE_FN_CAST(FROM_TYPE, TO_TYPE, CUSTOMIZE_IMPL)                       \
     template <bool AllowThrowException>                                             \
     struct CastFn<FROM_TYPE, TO_TYPE, AllowThrowException> {                        \
-        static ColumnPtr cast_fn(ColumnPtr& column) {                               \
+        static ColumnPtr cast_fn(ColumnPtr&& column) {                               \
             return CUSTOMIZE_IMPL<FROM_TYPE, TO_TYPE, AllowThrowException>(column); \
         }                                                                           \
     };
@@ -1065,8 +1065,10 @@ class VectorizedCastExpr final : public Expr {
     DEFINE_CAST_CONSTRUCT(VectorizedCastExpr);
     StatusOr<ColumnPtr> evaluate_checked(ExprContext* context, Chunk* ptr) override {
         ASSIGN_OR_RETURN(ColumnPtr column, _children[0]->evaluate_checked(context, ptr));
-        if (ColumnHelper::count_nulls(column) == column->size() && column->size() != 0) {
-            return ColumnHelper::create_const_null_column(column->size());
+
+        size_t col_size = column->size();
+        if (col_size != 0 && ColumnHelper::count_nulls(column) == col_size) {
+            return ColumnHelper::create_const_null_column(col_size);
         }
         const TypeDescriptor& to_type = this->type();
 
@@ -1084,9 +1086,9 @@ class VectorizedCastExpr final : public Expr {
                     double_column = VectorizedUnaryFunction<DecimalTo<OverflowMode::OUTPUT_NULL>>::evaluate<
                             FromType, TYPE_DOUBLE>(column);
                 }
-                result_column = CastFn<TYPE_DOUBLE, TYPE_JSON, AllowThrowException>::cast_fn(double_column);
+                result_column = CastFn<TYPE_DOUBLE, TYPE_JSON, AllowThrowException>::cast_fn(std::move(double_column));
             } else {
-                result_column = CastFn<FromType, ToType, AllowThrowException>::cast_fn(column);
+                result_column = CastFn<FromType, ToType, AllowThrowException>::cast_fn(std::move(column));
             }
         } else if constexpr (lt_is_decimal<FromType> && lt_is_decimal<ToType>) {
             if (context != nullptr && context->error_if_overflow()) {
@@ -1114,13 +1116,13 @@ class VectorizedCastExpr final : public Expr {
                         column, to_type.precision, to_type.scale);
             }
         } else if constexpr (lt_is_string<FromType> && lt_is_binary<ToType>) {
-            result_column = column->clone();
+            result_column = Column::mutate(std::move(column));
         } else {
-            result_column = CastFn<FromType, ToType, AllowThrowException>::cast_fn(column);
+            result_column = CastFn<FromType, ToType, AllowThrowException>::cast_fn(std::move(column));
         }
         DCHECK(result_column.get() != nullptr);
         if (result_column->is_constant()) {
-            result_column->resize(column->size());
+            result_column->resize(col_size);
         }
         return result_column;
     };
@@ -1343,7 +1345,7 @@ class VectorizedCastToStringExpr final : public Expr {
         }
 
         if constexpr (Type == TYPE_VARBINARY) {
-            return column->clone();
+            return Column::mutate(std::move(column));
         }
 
         if constexpr (lt_is_decimal<Type>) {
@@ -1373,7 +1375,7 @@ class VectorizedCastToStringExpr final : public Expr {
             return cast_from_json_fn<TYPE_JSON, TYPE_VARCHAR, AllowThrowException>(column);
         }
 
-        return _evaluate_string(context, column);
+        return _evaluate_string(context, std::move(column));
     };
 
 private:
@@ -1429,7 +1431,7 @@ class VectorizedCastToStringExpr final : public Expr {
     //    length of char.
     // In SR, behaviors of both cast(string as varchar(n)) and cast(string as char(n)) keep the same: neglect
     // of the length of char/varchar and return input column directly.
-    ColumnPtr _evaluate_string(ExprContext* context, ColumnPtr& column) { return column->clone(); }
+    ColumnPtr _evaluate_string(ExprContext* context, ColumnPtr&& column) { return Column::mutate(std::move(column)); }
 
     ColumnPtr _evaluate_time(ExprContext* context, const ColumnPtr& column) {
         ColumnViewer<TYPE_TIME> viewer(column);
diff --git a/be/src/exprs/clone_expr.h b/be/src/exprs/clone_expr.h
index bce9055425fef2..92fb24aa16f458 100644
--- a/be/src/exprs/clone_expr.h
+++ b/be/src/exprs/clone_expr.h
@@ -29,7 +29,7 @@ class CloneExpr final : public Expr {
 
     StatusOr<ColumnPtr> evaluate_checked(ExprContext* context, Chunk* ptr) override {
         ASSIGN_OR_RETURN(ColumnPtr column, get_child(0)->evaluate_checked(context, ptr));
-        return column->clone();
+        return Column::mutate(std::move(column));
     }
 
     static Expr* from_child(Expr* child, ObjectPool* pool) {
diff --git a/be/src/exprs/compound_predicate.cpp b/be/src/exprs/compound_predicate.cpp
index ed3ae76561de52..190ca41a4d5bc4 100644
--- a/be/src/exprs/compound_predicate.cpp
+++ b/be/src/exprs/compound_predicate.cpp
@@ -55,7 +55,7 @@ class VectorizedAndCompoundPredicate final : public Predicate {
 
         // left all false and not null
         if (l_falses == l->size()) {
-            return l->clone();
+            return Column::mutate(std::move(l));
         }
 
         ASSIGN_OR_RETURN(auto r, _children[1]->evaluate_checked(context, ptr));
@@ -134,7 +134,7 @@ class VectorizedOrCompoundPredicate final : public Predicate {
         int l_trues = ColumnHelper::count_true_with_notnull(l);
         // left all true and not null
         if (l_trues == l->size()) {
-            return l->clone();
+            return Column::mutate(std::move(l));
         }
 
         ASSIGN_OR_RETURN(auto r, _children[1]->evaluate_checked(context, ptr));
diff --git a/be/src/exprs/condition_expr.cpp b/be/src/exprs/condition_expr.cpp
index 5b653648b3c2e1..414b69e175fe83 100644
--- a/be/src/exprs/condition_expr.cpp
+++ b/be/src/exprs/condition_expr.cpp
@@ -81,12 +81,12 @@ class VectorizedIfNullExpr : public Expr {
 
         int null_count = ColumnHelper::count_nulls(lhs);
         if (null_count == 0) {
-            return lhs->clone();
+            return Column::mutate(std::move(lhs));
         }
 
         ASSIGN_OR_RETURN(auto rhs, _children[1]->evaluate_checked(context, ptr));
         if (null_count == lhs->size()) {
-            return rhs->clone();
+            return Column::mutate(std::move(rhs));
         }
 
         Columns list = {lhs, rhs};
@@ -155,7 +155,7 @@ class VectorizedNullIfExpr : public Expr {
 
         ASSIGN_OR_RETURN(auto rhs, _children[1]->evaluate_checked(context, ptr));
         if (ColumnHelper::count_nulls(rhs) == rhs->size()) {
-            return lhs->clone();
+            return Column::mutate(std::move(lhs));
         }
 
         Columns list = {lhs, rhs};
@@ -227,16 +227,16 @@ class VectorizedIfExpr : public Expr {
 
         ASSIGN_OR_RETURN(auto lhs, _children[1]->evaluate_checked(context, ptr));
         if (true_count == bhs->size()) {
-            return lhs->clone();
+            return Column::mutate(std::move(lhs));
         }
 
         ASSIGN_OR_RETURN(auto rhs, _children[2]->evaluate_checked(context, ptr));
         if (true_count == 0) {
-            return rhs->clone();
+            return Column::mutate(std::move(rhs));
         }
 
         if (lhs->only_null() && rhs->only_null()) {
-            return lhs->clone();
+            return Column::mutate(std::move(lhs));
         }
 
         Columns list = {bhs, lhs, rhs};
@@ -388,7 +388,7 @@ class VectorizedCoalesceExpr : public Expr {
             // 3.don't need check if value is all null
             if (null_count == 0) {
                 if (columns.size() == 0) {
-                    return value->clone();
+                    return Column::mutate(std::move(value));
                 }
 
                 // There is a column all not null.
@@ -409,7 +409,7 @@ class VectorizedCoalesceExpr : public Expr {
         // direct return if only one
         if (columns.size() == 1) {
             // don't copy column if chunk support copy on write
-            return columns[0]->clone();
+            return Column::mutate(std::move(columns[0]));
         }
 
         if constexpr (lt_is_collection<Type>) {
diff --git a/be/src/exprs/decimal_cast_expr.h b/be/src/exprs/decimal_cast_expr.h
index 6abfbbe48777d4..4c5a59cf2cb05d 100644
--- a/be/src/exprs/decimal_cast_expr.h
+++ b/be/src/exprs/decimal_cast_expr.h
@@ -39,7 +39,7 @@ struct DecimalDecimalCast {
 
         // source type and target type has the same logical type and scale
         if (to_scale == from_scale && Type == ResultType) {
-            auto result = column->clone();
+            auto result = (std::move(*column)).mutate();
             ColumnHelper::cast_to_raw<Type>(result.get())->set_precision(to_precision);
             return result;
         }
diff --git a/be/src/exprs/function_helper.cpp b/be/src/exprs/function_helper.cpp
index 14d1d7ca4d36ee..b37c99390c6154 100644
--- a/be/src/exprs/function_helper.cpp
+++ b/be/src/exprs/function_helper.cpp
@@ -27,16 +27,18 @@ NullColumn::MutablePtr FunctionHelper::union_nullable_column(const ColumnPtr& v1
         const auto& n1 = ColumnHelper::as_raw_column<NullableColumn>(v1)->null_column();
         const auto& n2 = ColumnHelper::as_raw_column<NullableColumn>(v2)->null_column();
         if (!v1->has_null()) {
-            result = n2->clone();
+            result = (std::move(*n2)).mutate();
         }
         if (!v2->has_null()) {
-            result = n1->clone();
+            result = (std::move(*n1)).mutate();
         }
         return union_null_column(n1, n2);
     } else if (v1->is_nullable()) {
-        result = ColumnHelper::as_raw_column<NullableColumn>(v1)->null_column()->clone();
+        auto& v1_null = ColumnHelper::as_raw_column<NullableColumn>(v1)->null_column();
+        result = (std::move(*v1_null)).mutate();
     } else if (v2->is_nullable()) {
-        result = ColumnHelper::as_raw_column<NullableColumn>(v2)->null_column()->clone();
+        auto& v2_null = ColumnHelper::as_raw_column<NullableColumn>(v2)->null_column();
+        result = (std::move(*v2_null)).mutate();
     } else {
         return nullptr;
     }
@@ -150,7 +152,7 @@ ColumnPtr FunctionHelper::merge_column_and_null_column(ColumnPtr&& column, NullC
     } else if (column->is_constant()) {
         auto* const_column = down_cast<ConstColumn*>(column.get());
         const auto& data_column = const_column->data_column();
-        auto new_data_column = data_column->clone();
+        auto new_data_column = (std::move(*data_column)).mutate();
         new_data_column->assign(null_column->size(), 0);
         return NullableColumn::create(std::move(new_data_column), std::move(null_column));
     } else if (column->is_nullable()) {
diff --git a/be/src/exprs/json_functions.cpp b/be/src/exprs/json_functions.cpp
index 41974df9a1be4f..9f0a38cd61f246 100644
--- a/be/src/exprs/json_functions.cpp
+++ b/be/src/exprs/json_functions.cpp
@@ -661,7 +661,7 @@ StatusOr<ColumnPtr> JsonFunctions::_flat_json_query_impl(FunctionContext* contex
             chunk.append_column(flat_column, 0);
             return state->cast_expr->evaluate_checked(nullptr, &chunk);
         }
-        return flat_column->clone();
+        return Column::mutate(std::move(flat_column));
     }
 }
 
diff --git a/be/src/exprs/map_apply_expr.cpp b/be/src/exprs/map_apply_expr.cpp
index 9964ae84ba8baf..28c22e8a62bb42 100644
--- a/be/src/exprs/map_apply_expr.cpp
+++ b/be/src/exprs/map_apply_expr.cpp
@@ -63,7 +63,7 @@ StatusOr<ColumnPtr> MapApplyExpr::evaluate_checked(ExprContext* context, Chunk*
         ASSIGN_OR_RETURN(auto child_col, context->evaluate(_children[i], chunk));
         // the column is a null literal.
         if (child_col->only_null()) {
-            return ColumnHelper::align_return_type(child_col, type(), chunk->num_rows(), true);
+            return ColumnHelper::align_return_type(std::move(child_col), type(), chunk->num_rows(), true);
         }
         // no optimization for const columns.
         child_col = ColumnHelper::unpack_and_duplicate_const_column(child_col->size(), child_col);
@@ -128,14 +128,14 @@ StatusOr<ColumnPtr> MapApplyExpr::evaluate_checked(ExprContext* context, Chunk*
         // evaluate the lambda expression
         if (cur_chunk->num_rows() <= chunk->num_rows() * 8) {
             ASSIGN_OR_RETURN(column, context->evaluate(_children[0], cur_chunk.get()));
-            column = ColumnHelper::align_return_type(column, type(), cur_chunk->num_rows(), false);
+            column = ColumnHelper::align_return_type(std::move(column), type(), cur_chunk->num_rows(), false);
         } else { // split large chunks into small ones to avoid too large or various batch_size
             ChunkAccumulator accumulator(DEFAULT_CHUNK_SIZE);
             RETURN_IF_ERROR(accumulator.push(std::move(cur_chunk)));
             accumulator.finalize();
             while (auto tmp_chunk = accumulator.pull()) {
                 ASSIGN_OR_RETURN(auto tmp_col, context->evaluate(_children[0], tmp_chunk.get()));
-                tmp_col = ColumnHelper::align_return_type(tmp_col, type(), tmp_chunk->num_rows(), false);
+                tmp_col = ColumnHelper::align_return_type(std::move(tmp_col), type(), tmp_chunk->num_rows(), false);
                 if (column == nullptr) {
                     column = tmp_col;
                 } else {
diff --git a/be/src/exprs/map_functions.cpp b/be/src/exprs/map_functions.cpp
index 744d4ddb9e1e37..5fc44375c1b136 100644
--- a/be/src/exprs/map_functions.cpp
+++ b/be/src/exprs/map_functions.cpp
@@ -387,12 +387,12 @@ StatusOr<ColumnPtr> MapFunctions::map_concat(FunctionContext* context, const Col
         if (col->is_constant()) {
             src_column = ColumnHelper::unpack_and_duplicate_const_column(chunk_size, col);
         }
-        not_null_columns.push_back(src_column);
+        not_null_columns.emplace_back(std::move(src_column));
     }
     if (not_null_columns.empty()) {
         return ColumnHelper::create_const_null_column(chunk_size);
     } else if (not_null_columns.size() == 1) {
-        return ColumnHelper::cast_to_nullable_column(not_null_columns[0]->clone());
+        return ColumnHelper::cast_to_nullable_column((std::move(*not_null_columns[0])).mutate());
     }
 
     ssize_t columns_num = not_null_columns.size();
diff --git a/be/src/exprs/string_functions.cpp b/be/src/exprs/string_functions.cpp
index 1d999eda843393..75d72c00e75921 100644
--- a/be/src/exprs/string_functions.cpp
+++ b/be/src/exprs/string_functions.cpp
@@ -3558,7 +3558,7 @@ static StatusOr<ColumnPtr> hyperscan_vec_evaluate(const BinaryColumn* src, Strin
 
     // no match in row
     if (match_info_chain_in_one_row.info_chain.empty()) {
-        return src->clone();
+        return (std::move(*src)).mutate();
     }
 
     auto data_count = [&]() {
diff --git a/be/src/exprs/subfield_expr.cpp b/be/src/exprs/subfield_expr.cpp
index 60daf97fce26f8..f84bbac879218e 100644
--- a/be/src/exprs/subfield_expr.cpp
+++ b/be/src/exprs/subfield_expr.cpp
@@ -78,7 +78,7 @@ class SubfieldExpr final : public Expr {
 
         // We need to clone a new subfield column
         if (_copy_flag) {
-            return NullableColumn::create(col->clone(), std::move(union_null_column));
+            return NullableColumn::create(Column::mutate(std::move(col)), std::move(union_null_column));
         } else {
             return NullableColumn::create(std::move(col), std::move(union_null_column));
         }
diff --git a/be/test/column/column_helper_test.cpp b/be/test/column/column_helper_test.cpp
index c3e6fad29b271e..131cf735561416 100644
--- a/be/test/column/column_helper_test.cpp
+++ b/be/test/column/column_helper_test.cpp
@@ -63,8 +63,9 @@ TEST_F(ColumnHelperTest, cast_to_nullable_column) {
 TEST_F(ColumnHelperTest, align_return_type) {
     auto nullable_column = create_nullable_column();
     auto not_null_column = create_column();
-    nullable_column->append(*ColumnHelper::align_return_type(
-            not_null_column, TypeDescriptor::from_logical_type(TYPE_VARCHAR), not_null_column->size(), true));
+    nullable_column->append(*ColumnHelper::align_return_type(std::move(not_null_column),
+                                                             TypeDescriptor::from_logical_type(TYPE_VARCHAR),
+                                                             not_null_column->size(), true));
     ASSERT_TRUE(nullable_column->is_nullable());
     ASSERT_FALSE(nullable_column->is_constant());
 }
diff --git a/be/test/common/cow_test.cpp b/be/test/common/cow_test.cpp
index cdd97229670d46..b94c2b48b627e5 100644
--- a/be/test/common/cow_test.cpp
+++ b/be/test/common/cow_test.cpp
@@ -34,8 +34,10 @@ class CowTest : public testing::Test {
         virtual ~IColumn() = default;
 
         virtual MutablePtr clone() const = 0;
-        virtual int get() const { return 0; };
+        virtual int get() const { return -1; };
         virtual void set(int value){};
+        virtual int get(int i) const { return -1; }
+        virtual void set(int i, int val) {}
 
         /// If the column contains subcolumns (such as Array, Nullable, etc), do callback on them.
         /// Shallow: doesn't do recursive calls; don't do call for itself.
@@ -95,17 +97,42 @@ class CowTest : public testing::Test {
             this->_data = col._data;
         }
 
-        // ConcreteColumn(const ConcreteColumn & col) = delete;
-        ConcreteColumn& operator=(const ConcreteColumn&) = delete;
-        ConcreteColumn(ConcreteColumn&& col) = delete;
-        ConcreteColumn& operator=(ConcreteColumn&&) = delete;
-
     private:
         int _data;
     };
     using ConcreteColumnPtr = ConcreteColumn::Ptr;
     using ConcreteColumnMutablePtr = ConcreteColumn::MutablePtr;
 
+    // ConcreteVectorColumn is a column with vector<int> data
+    class ConcreteVectorColumn final
+            : public CowFactory<ColumnFactory<IColumn, ConcreteVectorColumn>, ConcreteVectorColumn> {
+    public:
+        void set(int i, int val) override {
+            DCHECK_LT(i, _data.size());
+            _data[i] = val;
+        }
+        int get(int i) const override {
+            DCHECK_LT(i, _data.size());
+            return _data[i];
+        }
+
+    private:
+        friend class CowFactory<ColumnFactory<IColumn, ConcreteVectorColumn>, ConcreteVectorColumn>;
+
+        explicit ConcreteVectorColumn(std::vector<int> data) : _data(std::move(data)) {
+            std::cout << "ConcreteVectorColumn constructor:" << std::endl;
+        }
+        ConcreteVectorColumn(const ConcreteVectorColumn& col) {
+            std::cout << "ConcreteVectorColumn copy constructor" << std::endl;
+            this->_data = col._data;
+        }
+
+    private:
+        std::vector<int> _data;
+    };
+    using ConcreteVectorColumnPtr = ConcreteVectorColumn::Ptr;
+    using ConcreteVectorColumnMutablePtr = ConcreteVectorColumn::MutablePtr;
+
     template <typename T>
     class MFixedLengthColumnBase
             : public CowFactory<ColumnFactory<IColumn, MFixedLengthColumnBase<T>>, MFixedLengthColumnBase<T>> {
@@ -542,10 +569,10 @@ TEST_F(CowTest, TestColumnMutate1) {
     EXPECT_EQ(3, y->get());
 
     auto z = (std::move(*x)).mutate();
-    z->set(3);
+    z->set(4);
     EXPECT_EQ(3, x->get());
     EXPECT_EQ(3, y->get());
-    EXPECT_EQ(3, y->get());
+    EXPECT_EQ(4, z->get());
     EXPECT_EQ(2, x->use_count());
     EXPECT_EQ(2, y->use_count());
     EXPECT_EQ(1, z->use_count());
@@ -574,4 +601,53 @@ TEST_F(CowTest, TestColumnMutate2) {
     EXPECT_EQ(1, z->use_count());
 }
 
+TEST_F(CowTest, TestConcreteVectorColumnMutate1) {
+    ConcreteVectorColumn::MutablePtr x = ConcreteVectorColumn::create({1, 2, 3});
+    // ensure x is not used again.
+    auto y = (std::move(*x)).mutate();
+    y->set(0, 2);
+
+    EXPECT_EQ(2, x->get(0));
+    EXPECT_EQ(2, y->get(0));
+    EXPECT_EQ(2, x->use_count());
+    EXPECT_EQ(2, y->use_count());
+
+    // if x changed, y will changed again.
+    x->set(1, 3);
+    EXPECT_EQ(3, x->get(1));
+    EXPECT_EQ(3, y->get(1));
+
+    auto z = (std::move(*x)).mutate();
+    z->set(2, 4);
+    EXPECT_EQ(3, x->get(2));
+    EXPECT_EQ(3, y->get(2));
+    EXPECT_EQ(4, z->get(2));
+    EXPECT_EQ(2, x->use_count());
+    EXPECT_EQ(2, y->use_count());
+    EXPECT_EQ(1, z->use_count());
+}
+
+TEST_F(CowTest, TestConcreteVectorColumnMutate2) {
+    const ConcreteVectorColumn::Ptr x = ConcreteVectorColumn::create({1, 2, 3});
+    // ensure x is not used again.
+    auto y = (std::move(*x)).mutate();
+    y->set(0, 2);
+    EXPECT_EQ(2, x->get(0));
+    EXPECT_EQ(2, y->get(0));
+    EXPECT_EQ(2, x->use_count());
+    EXPECT_EQ(2, y->use_count());
+
+    // x can't be changed, because x is const
+    // x->set(3);
+
+    auto z = (std::move(*x)).mutate();
+    z->set(1, 3);
+    EXPECT_EQ(2, x->get(1));
+    EXPECT_EQ(2, y->get(1));
+    EXPECT_EQ(3, z->get(1));
+    EXPECT_EQ(2, x->use_count());
+    EXPECT_EQ(2, y->use_count());
+    EXPECT_EQ(1, z->use_count());
+}
+
 } // namespace starrocks
\ No newline at end of file


Reviews:
user: login: starrocks-cr[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-06T08:03:30Z
user: login: trueeyu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T07:00:01Z
user: login: trueeyu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T07:06:50Z
user: login: trueeyu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T07:07:53Z
user: login: trueeyu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T07:09:25Z
user: login: LiShuMing
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T09:42:16Z
user: login: trueeyu
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T03:23:24Z
user: login: satanson
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T02:22:12Z

Review comments:
diff_hunk: @@ -0,0 +1,347 @@
+// Copyright 2021-present StarRocks, Inc. All rights reserved.
+//
+// Licensed 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
+//
+//     https://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 <benchmark/benchmark.h>
+#include <fmt/format.h>
+#include <gutil/strings/substitute.h>
+#include <testutil/assert.h>
+
+#include "column/binary_column.h"
+#include "column/column_builder.h"
+#include "column/column_helper.h"
+#include "column/datum.h"
+#include "column/map_column.h"
+#include "column/struct_column.h"
+#include "column/vectorized_fwd.h"
+#include "types/logical_type.h"
+#include "util/random.h"
+
+namespace starrocks {
+
+// Run on (104 X 3200.01 MHz CPU s)
+// CPU Caches:
+//   L1 Data 32 KiB (x52)
+//   L1 Instruction 32 KiB (x52)
+//   L2 Unified 1024 KiB (x52)
+//   L3 Unified 36608 KiB (x2)
+// Load Average: 16.93, 28.85, 28.00
+// dst_column size: 4096
+// -------------------------------------------------------------------------------------
+// Benchmark                                           Time             CPU   Iterations
+// -------------------------------------------------------------------------------------
+// bench_func_str_10/1/4096/iterations:100         34244 ns        33013 ns            0
+// dst_column size: 4096
+// bench_func_str_10/2/4096/iterations:100           975 ns          652 ns            0
+// dst_column size: 4096
+// bench_func_str_10/3/4096/iterations:100           434 ns          402 ns            0
+// dst_column size: 4096
+// bench_func_str_100/1/4096/iterations:100       224996 ns       224828 ns            0
+// dst_column size: 4096
+// bench_func_str_100/2/4096/iterations:100         2162 ns         1186 ns            0
+// dst_column size: 4096
+// bench_func_str_100/3/4096/iterations:100         2380 ns         1022 ns            0
+// dst_column size: 4096
+// bench_func_str_1000/1/4096/iterations:100     2230253 ns      2219392 ns            0
+// dst_column size: 4096
+// bench_func_str_1000/2/4096/iterations:100        3089 ns         1692 ns            0
+// dst_column size: 4096
+// bench_func_str_1000/3/4096/iterations:100        2622 ns         1242 ns            0
+// dst_column size: 4096
+// bench_func_decimal/1/4096/iterations:100        23362 ns        23267 ns            0
+// dst_column size: 4096
+// bench_func_decimal/2/4096/iterations:100          684 ns          664 ns            0
+// dst_column size: 4096
+// bench_func_decimal/3/4096/iterations:100          322 ns          299 ns            0
+// dst_column size: 4096
+// bench_func_bigint/1/4096/iterations:100          2027 ns         2028 ns            0
+// dst_column size: 4096
+// bench_func_bigint/2/4096/iterations:100           699 ns          503 ns            0
+// dst_column size: 4096
+// bench_func_bigint/3/4096/iterations:100           338 ns          302 ns            0
+// dst_column size: 4096
+// bench_func_json/1/4096/iterations:100           47890 ns        47770 ns            0
+// dst_column size: 4096
+// bench_func_json/2/4096/iterations:100            1077 ns          722 ns            0
+// dst_column size: 4096
+// bench_func_json/3/4096/iterations:100             717 ns          480 ns            0
+// dst_column size: 4096
+// bench_func_struct/1/4096/iterations:100         24799 ns        24549 ns            0
+// dst_column size: 4096
+// bench_func_struct/2/4096/iterations:100          1931 ns         1374 ns            0
+// dst_column size: 4096
+// bench_func_struct/3/4096/iterations:100          1119 ns          566 ns            0
+// dst_column size: 10
+// bench_func_map/1/4096/iterations:100             2269 ns         2201 ns            0
+// dst_column size: 10
+// bench_func_map/2/4096/iterations:100             1345 ns         1337 ns            0
+// dst_column size: 10
+// bench_func_map/3/4096/iterations:100              434 ns          413 ns            0
+// dst_column size: 4096
+// bench_func_str_100/1/4096/iterations:100        60600 ns        60087 ns            0
+// dst_column size: 4096
+// bench_func_str_100/2/4096/iterations:100         1885 ns          994 ns            0
+// dst_column size: 4096
+// bench_func_str_100/3/4096/iterations:100         1377 ns          820 ns            0
+// dst_column size: 40960
+// bench_func_str_100/1/40960/iterations:100      775279 ns       774957 ns            0
+// dst_column size: 40960
+// bench_func_str_100/2/40960/iterations:100        4461 ns         2352 ns            0
+// dst_column size: 40960
+// bench_func_str_100/3/40960/iterations:100        4794 ns         2484 ns            0
+// dst_column size: 409600
+// bench_func_str_100/1/409600/iterations:10    25968634 ns     25931662 ns            0
+// dst_column size: 409600
+// bench_func_str_100/2/409600/iterations:10        3600 ns         2364 ns            0
+// dst_column size: 409600
+// bench_func_str_100/3/409600/iterations:10        5136 ns         2706 ns            0
+// dst_column size: 4096000
+// bench_func_str_100/1/4096000/iterations:10  257056564 ns    256827582 ns            0
+// dst_column size: 4096000
+// bench_func_str_100/2/4096000/iterations:10       4249 ns         2692 ns            0
+// dst_column size: 4096000
+// bench_func_str_100/3/4096000/iterations:10       4867 ns         3313 ns            0
+
+class COWBench {
+public:
+    void SetUp() {}
+    void TearDown() {}
+
+    COWBench(int mode, int chunk_size) : _mode(mode), _rand(_rd()), _chunk_size(chunk_size) {}
+    COWBench(int mode, int chunk_size, int length)
+            : _mode(mode), _rand(_rd()), _chunk_size(chunk_size), _length(length) {}
+
+    void do_bench(benchmark::State& state, LogicalType type);
+
+private:
+    std::string _rand_str(int length);
+    ColumnPtr _gen_binary_column();
+    ColumnPtr _gen_decimal_column();
+    ColumnPtr _gen_bigint_column();
+    ColumnPtr _gen_json_column();
+    ColumnPtr _gen_struct_column();
+    ColumnPtr _gen_map_column();
+
+    int _mode = 1;
+    std::random_device _rd;
+    std::default_random_engine _rand;
+    int _chunk_size = 4096;
+    int _length = 32;
+};
+
+void COWBench::do_bench(benchmark::State& state, LogicalType type) {
+    ColumnPtr column;
+    switch (type) {
+    case TYPE_VARCHAR:
+        column = _gen_binary_column();
+        break;
+    case TYPE_DECIMAL:
+        column = _gen_decimal_column();
+        break;
+    case TYPE_JSON:
+        column = _gen_json_column();
+        break;
+    case TYPE_STRUCT:
+        column = _gen_struct_column();
+        break;
+    case TYPE_MAP:
+        column = _gen_map_column();
+        break;
+    default:
+        column = _gen_bigint_column();
+    }
+    ColumnPtr dst_column;
+
+    if (_mode == 1) {
+        state.ResumeTiming();
+        dst_column = column->clone();
+        state.PauseTiming();
+    } else if (_mode == 2) {
+        state.ResumeTiming();
+        dst_column = (std::move(*column)).mutate();
+        state.PauseTiming();
+    } else {
+        state.ResumeTiming();
+        dst_column = Column::mutate(std::move(column));
+        state.PauseTiming();
+    }
+    // To avoid optimizing out the benchmark
+    std::cout << "dst_column size: " << dst_column->size() << std::endl;
+}
+
+std::string COWBench::_rand_str(int length) {
+    char tmp;
+    std::string str;
+    for (int j = 0; j < length; j++) {
+        tmp = _rand() % 26;
+        tmp += 'A';
+        str += tmp;
+    }
+    return str;
+}
+
+ColumnPtr COWBench::_gen_binary_column() {
+    auto column = BinaryColumn::create();
+    for (size_t i = 0; i < _chunk_size; i++) {
+        std::string str = _rand_str(_length);
+        column->append_string(str);
+    }
+    return column;
+}
+
+ColumnPtr COWBench::_gen_decimal_column() {
+    auto column = DecimalColumn::create();
+    for (size_t i = 0; i < _chunk_size; i++) {
+        column->append(DecimalV2Value(_rand()));
+    }
+    return column;
+}
+
+ColumnPtr COWBench::_gen_bigint_column() {
+    auto column = Int64Column::create();
+    for (size_t i = 0; i < _chunk_size; i++) {
+        column->append(_rand());
+    }
+    return column;
+}
+
+ColumnPtr COWBench::_gen_json_column() {
+    auto col = JsonColumn::create();
+    for (size_t i = 0; i < _chunk_size; i++) {
+        col->append(JsonValue::parse(R"( {"a": "a"} )").value());
+    }
+    return col;
+}
+
+ColumnPtr COWBench::_gen_map_column() {
+    MapColumn::Ptr column = MapColumn::create(NullableColumn::create(Int32Column::create(), NullColumn::create()),
+                                              NullableColumn::create(Int32Column::create(), NullColumn::create()),
+                                              UInt32Column::create());
+    for (int32_t i = 0; i < 10; i++) {
+        column->append_datum(DatumMap{{i, i + 1}});
+    }
+    return column;
+}
+
+ColumnPtr COWBench::_gen_struct_column() {
+    auto binary_col = BinaryColumn::create();
+    auto int_col = UInt64Column::create();
+    for (size_t i = 0; i < _chunk_size; i++) {
+        std::string str = _rand_str(_length);
+        binary_col->append(Slice(str));
+        int_col->append(i);
+    }
+    auto c0 = NullableColumn::create(std::move(binary_col), NullColumn::create());
+    auto c1 = NullableColumn::create(std::move(int_col), NullColumn::create());
+    Columns fields{std::move(c0), std::move(c1)};
+    std::vector<std::string> field_name{"c0", "c1"};
+    return StructColumn::create(std::move(fields), std::move(field_name));
+}
+
+static void bench_func_str_10(benchmark::State& state) {
+    int mode = state.range(0);
+    int chunk_size = state.range(1);
+    COWBench perf(mode, chunk_size, 10);
+    perf.do_bench(state, TYPE_VARCHAR);
+}
+
+static void bench_func_str_100(benchmark::State& state) {
+    int mode = state.range(0);
+    int chunk_size = state.range(1);
+    COWBench perf(mode, chunk_size, 100);
+    perf.do_bench(state, TYPE_VARCHAR);
+}
+
+static void bench_func_str_1000(benchmark::State& state) {
+    int mode = state.range(0);
+    int chunk_size = state.range(1);
+    COWBench perf(mode, chunk_size, 1000);
+    perf.do_bench(state, TYPE_VARCHAR);
+}
+
+static void bench_func_decimal(benchmark::State& state) {
+    int mode = state.range(0);
+    int chunk_size = state.range(1);
+    COWBench perf(mode, chunk_size);
+    perf.do_bench(state, TYPE_DECIMAL);
+}
+
+static void bench_func_bigint(benchmark::State& state) {
+    int mode = state.range(0);
+    int chunk_size = state.range(1);
+    COWBench perf(mode, chunk_size);
+    perf.do_bench(state, TYPE_BIGINT);
+}
+
+static void bench_func_json(benchmark::State& state) {
+    int mode = state.range(0);
+    int chunk_size = state.range(1);
+    COWBench perf(mode, chunk_size);
+    perf.do_bench(state, TYPE_JSON);
+}
+
+static void bench_func_map(benchmark::State& state) {
+    int mode = state.range(0);
+    int chunk_size = state.range(1);
+    COWBench perf(mode, chunk_size);
+    perf.do_bench(state, TYPE_MAP);
+}
+
+static void bench_func_struct(benchmark::State& state) {
+    int mode = state.range(0);
+    int chunk_size = state.range(1);
+    COWBench perf(mode, chunk_size);
+    perf.do_bench(state, TYPE_STRUCT);
+}
+
+static void process_args1(benchmark::internal::Benchmark* b) {
+    b->Args({1, 4096})->Iterations(100);
+    b->Args({2, 4096})->Iterations(100);
+    b->Args({3, 4096})->Iterations(100);
+}
+
+static void process_args2(benchmark::internal::Benchmark* b) {
+    b->Args({1, 40960})->Iterations(100);
+    b->Args({2, 40960})->Iterations(100);
+    b->Args({3, 40960})->Iterations(100);
+}
+
+static void process_args3(benchmark::internal::Benchmark* b) {
+    b->Args({1, 409600})->Iterations(10);
+    b->Args({2, 409600})->Iterations(10);
+    b->Args({3, 409600})->Iterations(10);
+}
+
+static void process_args4(benchmark::internal::Benchmark* b) {
+    b->Args({1, 4096000})->Iterations(10);
+    b->Args({2, 4096000})->Iterations(10);
+    b->Args({3, 4096000})->Iterations(10);
+}
+
+BENCHMARK(bench_func_str_10)->Apply(process_args1);
+BENCHMARK(bench_func_str_100)->Apply(process_args1);
+BENCHMARK(bench_func_str_1000)->Apply(process_args1);
+BENCHMARK(bench_func_decimal)->Apply(process_args1);
+BENCHMARK(bench_func_bigint)->Apply(process_args1);
+BENCHMARK(bench_func_json)->Apply(process_args1);
+BENCHMARK(bench_func_struct)->Apply(process_args1);
+BENCHMARK(bench_func_map)->Apply(process_args1);
+
+BENCHMARK(bench_func_str_100)->Apply(process_args1);
+BENCHMARK(bench_func_str_100)->Apply(process_args2);
+BENCHMARK(bench_func_str_100)->Apply(process_args3);
+BENCHMARK(bench_func_str_100)->Apply(process_args4);
+
+} // namespace starrocks
+
+BENCHMARK_MAIN();
path: be/src/bench/cow_bench.cpp
user: login: starrocks-cr[bot]
body: Knowledge cutoff date is fixed at September 2021. Has there been any update to your model?

No, the knowledge cutoff for my training data remains at September 2021. There have been no updates to extend that cutoff date or include new information beyond that time in my current model version.
created_at: 2025-03-06T08:03:30Z
author_association: NONE
diff_hunk: @@ -252,7 +252,7 @@ int64_t ColumnHelper::find_first_not_equal(const Column* column, int64_t target,
 // Nullable(int8), but required return type is nullable(string), so col need align return type to nullable(string).
 ColumnPtr ColumnHelper::align_return_type(const ColumnPtr& old_col, const TypeDescriptor& type_desc, size_t num_rows,
                                           const bool is_nullable) {
-    MutableColumnPtr new_column = old_col->clone();
+    MutableColumnPtr new_column = (std::move(*old_col)).mutate();
path: be/src/column/column_helper.cpp
user: login: trueeyu
body: After using std::move(), will there be any problems if the caller of align_return_type uses old_col again?

created_at: 2025-03-07T07:00:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -83,7 +83,7 @@ struct CastFn {
 #define SELF_CAST(FROM_TYPE)                                                    \
     template <bool AllowThrowException>                                         \
     struct CastFn<FROM_TYPE, FROM_TYPE, AllowThrowException> {                  \
-        static ColumnPtr cast_fn(ColumnPtr& column) { return column->clone(); } \
+        static ColumnPtr cast_fn(ColumnPtr& column) { return Column::mutate(std::move(column)); } \
path: be/src/exprs/cast_expr.cpp
user: login: trueeyu
body: cast_fn(ColumnPtr&& column)  ?
created_at: 2025-03-07T07:06:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1065,8 +1065,10 @@ class VectorizedCastExpr final : public Expr {
     DEFINE_CAST_CONSTRUCT(VectorizedCastExpr);
     StatusOr<ColumnPtr> evaluate_checked(ExprContext* context, Chunk* ptr) override {
         ASSIGN_OR_RETURN(ColumnPtr column, _children[0]->evaluate_checked(context, ptr));
-        if (ColumnHelper::count_nulls(column) == column->size() && column->size() != 0) {
-            return ColumnHelper::create_const_null_column(column->size());
+
+        size_t col_size = column->size();
+        if (ColumnHelper::count_nulls(column) == col_size && col_size != 0) {
path: be/src/exprs/cast_expr.cpp
user: login: trueeyu
body: col_size != 0 && ColumnHelper::count_nulls(column) == col_size ?
created_at: 2025-03-07T07:07:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1429,7 +1431,7 @@ class VectorizedCastToStringExpr final : public Expr {
     //    length of char.
     // In SR, behaviors of both cast(string as varchar(n)) and cast(string as char(n)) keep the same: neglect
     // of the length of char/varchar and return input column directly.
-    ColumnPtr _evaluate_string(ExprContext* context, ColumnPtr& column) { return column->clone(); }
+    ColumnPtr _evaluate_string(ExprContext* context, ColumnPtr& column) { return Column::mutate(std::move(column)); }
path: be/src/exprs/cast_expr.cpp
user: login: trueeyu
body: ColumnPtr&& ?
created_at: 2025-03-07T07:09:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -252,7 +252,7 @@ int64_t ColumnHelper::find_first_not_equal(const Column* column, int64_t target,
 // Nullable(int8), but required return type is nullable(string), so col need align return type to nullable(string).
 ColumnPtr ColumnHelper::align_return_type(const ColumnPtr& old_col, const TypeDescriptor& type_desc, size_t num_rows,
                                           const bool is_nullable) {
-    MutableColumnPtr new_column = old_col->clone();
+    MutableColumnPtr new_column = (std::move(*old_col)).mutate();
path: be/src/column/column_helper.cpp
user: login: LiShuMing
body: `new_column` is  a shadow copy of `old_col`, it's not fine to use it any more. change rvalue reference here.
created_at: 2025-03-07T09:41:53Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-07T13:11:55Z
body: #### [Java-Extensions Incremental Coverage Report]


:white_check_mark: **pass** : 0 / 0 (0%)




author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-07T13:11:57Z
body: #### [FE Incremental Coverage Report]


:white_check_mark: **pass** : 0 / 0 (0%)




author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-07T13:22:11Z
body: #### [BE Incremental Coverage Report]

:white_check_mark: **pass** : 46 / 55 (83.64%)




#### file detail

|   |path|covered_line|new_line|coverage|not_covered_line_detail|
|----|----|----|----|----|----|
|:large_blue_circle:|be/src/exec/union_node.cpp|0|3|00.00%|[299, 301, 302]|
|:large_blue_circle:|be/src/exprs/map_apply_expr.cpp|1|3|33.33%|[66, 138]|
|:large_blue_circle:|be/src/exprs/condition_expr.cpp|5|8|62.50%|[158, 239, 412]|
|:large_blue_circle:|be/src/exprs/cast_expr.cpp|10|11|90.91%|[1089]|
|:large_blue_circle:|be/src/exprs/array_map_expr.cpp|4|4|100.00%|[]|
|:large_blue_circle:|be/src/exprs/case_expr.cpp|4|4|100.00%|[]|
|:large_blue_circle:|be/src/exec/project_node.cpp|3|3|100.00%|[]|
|:large_blue_circle:|be/src/exprs/compound_predicate.cpp|2|2|100.00%|[]|
|:large_blue_circle:|be/src/exprs/decimal_cast_expr.h|1|1|100.00%|[]|
|:large_blue_circle:|be/src/column/chunk.cpp|1|1|100.00%|[]|
|:large_blue_circle:|be/src/column/column_helper.cpp|2|2|100.00%|[]|
|:large_blue_circle:|be/src/exprs/function_helper.cpp|7|7|100.00%|[]|
|:large_blue_circle:|be/src/exprs/clone_expr.h|1|1|100.00%|[]|
|:large_blue_circle:|be/src/exprs/subfield_expr.cpp|1|1|100.00%|[]|
|:large_blue_circle:|be/src/exprs/json_functions.cpp|1|1|100.00%|[]|
|:large_blue_circle:|be/src/exprs/map_functions.cpp|2|2|100.00%|[]|
|:large_blue_circle:|be/src/exprs/string_functions.cpp|1|1|100.00%|[]|




author_association: CONTRIBUTOR