Coverage Report

Created: 2026-03-15 08:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exprs/short_circuit_evaluation_expr.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include "exprs/short_circuit_evaluation_expr.h"
19
20
#include <gen_cpp/Exprs_types.h>
21
22
#include <optional>
23
#include <sstream>
24
#include <string>
25
#include <vector>
26
27
#include "common/exception.h"
28
#include "common/logging.h"
29
#include "core/assert_cast.h"
30
#include "core/column/column.h"
31
#include "core/column/column_const.h"
32
#include "core/column/column_nullable.h"
33
#include "core/data_type/data_type_nullable.h"
34
#include "core/data_type/define_primitive_type.h"
35
#include "core/data_type/primitive_type.h"
36
#include "core/field.h"
37
#include "exprs/short_circuit_util.h"
38
#include "exprs/vexpr.h"
39
40
namespace doris {
41
42
// For short-circuit execution, we need to distinguish between three types of indices:
43
// 1. self_index: The index of the current column (the 'i' in for(int i=0; i<size; i++),
44
//    which always refers to the currently executing column)
45
// 2. executor_index: The index relative to the entire expr tree executor, passed down through
46
//    execute_column and ultimately used in leaf nodes (e.g., SlotRef)
47
// 3. current_index: The index relative to the column returned by the current expr node
48
//
49
// For if/ifnull, self_index and current_index are the same because only one column serves
50
// as the condition column.
51
//
52
// For coalesce/case, self_index and current_index are different because multiple columns
53
// serve as condition columns (however, for the first condition column execution, they are
54
// the same, similar to if/ifnull)
55
Status ShortCircuitExpr::prepare(RuntimeState* state, const RowDescriptor& desc,
56
0
                                 VExprContext* context) {
57
0
    RETURN_IF_ERROR_OR_PREPARED(VExpr::prepare(state, desc, context));
58
0
    _prepare_finished = true;
59
0
    return Status::OK();
60
0
}
61
62
Status ShortCircuitExpr::open(RuntimeState* state, VExprContext* context,
63
0
                              FunctionContext::FunctionStateScope scope) {
64
0
    DCHECK(_prepare_finished);
65
0
    RETURN_IF_ERROR(VExpr::open(state, context, scope));
66
0
    _open_finished = true;
67
0
    return Status::OK();
68
0
}
69
70
0
void ShortCircuitExpr::close(VExprContext* context, FunctionContext::FunctionStateScope scope) {
71
0
    DCHECK(_prepare_finished);
72
0
    VExpr::close(context, scope);
73
0
}
74
75
0
std::string ShortCircuitExpr::debug_string() const {
76
0
    std::string result = expr_name() + "(";
77
0
    for (size_t i = 0; i < _children.size(); ++i) {
78
0
        if (i != 0) {
79
0
            result += ", ";
80
0
        }
81
0
        result += _children[i]->debug_string();
82
0
    }
83
0
    result += ")";
84
0
    return result;
85
0
}
86
87
// Returns empty result column if count==0
88
// For some exprs, executing with a size-0 column may cause errors.
89
// These are issues with the exprs themselves, but for convenience, we handle this case uniformly in short-circuit expr.
90
/// TODO: Once all exprs support size-0 columns in the future, this function can be removed.
91
[[nodiscard]] Status try_early_return_on_empty(const VExprSPtr& expr, VExprContext* context,
92
                                               const Block* block, Selector* selector, size_t count,
93
0
                                               ColumnPtr& result_columnn) {
94
0
    if (count == 0) {
95
0
        result_columnn = expr->execute_type(block)->create_column();
96
0
        DCHECK_EQ(result_columnn->size(), 0);
97
0
        return Status::OK();
98
0
    }
99
100
0
    return expr->execute_column(context, block, selector, count, result_columnn);
101
0
}
102
103
ShortCircuitCaseExpr::ShortCircuitCaseExpr(const TExprNode& node)
104
0
        : ShortCircuitExpr(node), _has_else_expr(node.case_expr.has_else_expr) {}
105
106
ColumnPtr ShortCircuitExpr::dispatch_fill_columns(const ColumnPtr& true_column,
107
                                                  const Selector& true_selector,
108
                                                  const ColumnPtr& false_column,
109
                                                  const Selector& false_selector,
110
0
                                                  size_t count) const {
111
0
    ColumnPtr result_column;
112
0
    auto scalar_fill = [&](const auto& type) -> bool {
113
0
        using DataType = std::decay_t<decltype(type)>;
114
0
        result_column = ScalarFillWithSelector<DataType::PType>::fill(
115
0
                _data_type, true_column, true_selector, false_column, false_selector, count);
116
0
        return true;
117
0
    };
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE2EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE3EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE4EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE5EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE6EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE7EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE8EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE9EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE28EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE29EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE20EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE30EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE35EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE11EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE25EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE26EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE12EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE27EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE42EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE36EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKNS_3COWINS_7IColumnEE13immutable_ptrIS2_EERKNS_8PODArrayIjLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb0EEELm16ELm15EEES7_SE_mENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE37EEEEEbRKT_
118
119
0
    if (!dispatch_switch_scalar(_data_type->get_primitive_type(), scalar_fill)) {
120
0
        result_column = NonScalarFillWithSelector::fill(_data_type, true_column, true_selector,
121
0
                                                        false_column, false_selector, count);
122
0
    }
123
0
    return result_column;
124
0
}
125
126
ColumnPtr ShortCircuitExpr::dispatch_fill_columns(
127
0
        const std::vector<ColumnAndSelector>& columns_and_selectors, size_t count) const {
128
0
    ColumnPtr result_column;
129
0
    auto scalar_fill = [&](const auto& type) -> bool {
130
0
        using DataType = std::decay_t<decltype(type)>;
131
0
        result_column = ScalarFillWithSelector<DataType::PType>::fill(_data_type,
132
0
                                                                      columns_and_selectors, count);
133
0
        return true;
134
0
    };
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE2EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE3EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE4EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE5EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE6EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE7EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE8EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE9EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE28EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE29EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE20EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE30EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE35EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE11EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE25EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE26EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE12EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE27EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE42EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE36EEEEEbRKT_
Unexecuted instantiation: short_circuit_evaluation_expr.cpp:_ZZNK5doris16ShortCircuitExpr21dispatch_fill_columnsERKSt6vectorINS_17ColumnAndSelectorESaIS2_EEmENK3$_0clINS_16DispatchDataTypeILNS_13PrimitiveTypeE37EEEEEbRKT_
135
136
0
    if (!dispatch_switch_scalar(_data_type->get_primitive_type(), scalar_fill)) {
137
0
        result_column = NonScalarFillWithSelector::fill(_data_type, columns_and_selectors, count);
138
0
    }
139
0
    return result_column;
140
0
}
141
142
/// TODO: Potential optimization opportunities:
143
// 1. The logic of IF and CASE is similar; IFNULL and COALESCE are also similar.
144
//    A better approach would be to keep only CASE and COALESCE, with the optimizer
145
//    rewriting IF to CASE and IFNULL to COALESCE.
146
// 2. The following functions (execute_if_selector, execute_case_selector,
147
//    execute_ifnull_selector, execute_coalesce_selector) could theoretically be
148
//    further abstracted, e.g., by introducing a "column with selector" structure.
149
//    However, since there are currently few places handling selectors and keeping
150
//    them separate makes code review easier, each function is implemented individually.
151
152
void execute_if_selector(const ColumnPtr& cond_column, const Selector* selector, size_t count,
153
                         Selector& matched_executor_selector, Selector& matched_self_selector,
154
                         Selector& not_matched_executor_selector,
155
0
                         Selector& not_matched_self_selector) {
156
0
    ConditionColumnView condition_view = ConditionColumnView::create(cond_column, selector, count);
157
158
0
    matched_executor_selector.reserve(count);
159
0
    matched_self_selector.reserve(count);
160
0
    not_matched_executor_selector.reserve(count);
161
0
    not_matched_self_selector.reserve(count);
162
163
0
    auto null_func = [&](size_t self_index, size_t executor_index) {
164
0
        not_matched_self_selector.push_back(self_index);
165
0
        not_matched_executor_selector.push_back(executor_index);
166
0
    };
167
168
0
    auto true_func = [&](size_t self_index, size_t executor_index) {
169
0
        matched_self_selector.push_back(self_index);
170
0
        matched_executor_selector.push_back(executor_index);
171
0
    };
172
0
    auto false_func = [&](size_t self_index, size_t executor_index) {
173
0
        not_matched_self_selector.push_back(self_index);
174
0
        not_matched_executor_selector.push_back(executor_index);
175
0
    };
176
177
0
    condition_view.for_each(null_func, true_func, false_func);
178
0
}
179
180
Status ShortCircuitIfExpr::execute_column(VExprContext* context, const Block* block,
181
                                          Selector* selector, size_t count,
182
0
                                          ColumnPtr& result_column) const {
183
0
    DCHECK(_open_finished || block == nullptr) << debug_string();
184
0
    DCHECK(selector == nullptr || selector->size() == count);
185
0
    ColumnPtr cond_column;
186
0
    RETURN_IF_ERROR(
187
0
            try_early_return_on_empty(_children[0], context, block, selector, count, cond_column));
188
0
    DCHECK_EQ(cond_column->size(), count);
189
190
0
    Selector true_executor_selector;
191
0
    Selector true_self_selector;
192
0
    Selector false_executor_selector;
193
0
    Selector false_self_selector;
194
195
0
    execute_if_selector(cond_column, selector, count, true_executor_selector, true_self_selector,
196
0
                        false_executor_selector, false_self_selector);
197
198
0
    ColumnPtr true_column;
199
200
0
    RETURN_IF_ERROR(try_early_return_on_empty(_children[1], context, block, &true_executor_selector,
201
0
                                              true_executor_selector.size(), true_column));
202
0
    ColumnPtr false_column;
203
0
    RETURN_IF_ERROR(try_early_return_on_empty(_children[2], context, block,
204
0
                                              &false_executor_selector,
205
0
                                              false_executor_selector.size(), false_column));
206
207
0
    result_column = dispatch_fill_columns(true_column, true_self_selector, false_column,
208
0
                                          false_self_selector, count);
209
0
    return Status::OK();
210
0
}
211
212
void execute_case_selector(const ColumnPtr& cond_column, const Selector* executor_selector,
213
                           size_t executor_count, const Selector* current_selector,
214
                           Selector& matched_executor_selector, Selector& matched_current_selector,
215
                           Selector& not_matched_executor_selector,
216
0
                           Selector& not_matched_current_selector) {
217
0
    ConditionColumnView condition_view =
218
0
            ConditionColumnView::create(cond_column, executor_selector, executor_count);
219
0
    if (current_selector == nullptr) {
220
        // If current_selector is nullptr, this is the first condition column execution,
221
        // so self_index and current_index are the same
222
0
        auto null_func = [&](size_t self_index, size_t executor_index) {
223
0
            not_matched_current_selector.push_back(self_index);
224
0
            not_matched_executor_selector.push_back(executor_index);
225
0
        };
226
0
        auto true_func = [&](size_t self_index, size_t executor_index) {
227
0
            matched_current_selector.push_back(self_index);
228
0
            matched_executor_selector.push_back(executor_index);
229
0
        };
230
0
        auto false_func = [&](size_t self_index, size_t executor_index) {
231
0
            not_matched_current_selector.push_back(self_index);
232
0
            not_matched_executor_selector.push_back(executor_index);
233
0
        };
234
0
        condition_view.for_each(null_func, true_func, false_func);
235
0
    } else {
236
        // If current_selector is not nullptr, this is not the first condition column execution,
237
        // so self_index and current_index are different.
238
        // We need to determine current_index based on self_index and current_selector
239
0
        const auto& current_selector_data = *current_selector;
240
0
        DCHECK_EQ(current_selector_data.size(), executor_count);
241
242
0
        auto null_func = [&](size_t self_index, size_t executor_index) {
243
0
            not_matched_current_selector.push_back(current_selector_data[self_index]);
244
0
            not_matched_executor_selector.push_back(executor_index);
245
0
        };
246
0
        auto true_func = [&](size_t self_index, size_t executor_index) {
247
0
            matched_current_selector.push_back(current_selector_data[self_index]);
248
0
            matched_executor_selector.push_back(executor_index);
249
0
        };
250
0
        auto false_func = [&](size_t self_index, size_t executor_index) {
251
0
            not_matched_current_selector.push_back(current_selector_data[self_index]);
252
0
            not_matched_executor_selector.push_back(executor_index);
253
0
        };
254
0
        condition_view.for_each(null_func, true_func, false_func);
255
0
    }
256
0
}
257
258
Status ShortCircuitCaseExpr::execute_column(VExprContext* context, const Block* block,
259
                                            Selector* selector, size_t count,
260
0
                                            ColumnPtr& result_column) const {
261
0
    DCHECK(_open_finished || block == nullptr) << debug_string();
262
0
    DCHECK(selector == nullptr || selector->size() == count);
263
264
    // Structure: WHEN expr1 THEN result1 [WHEN expr2 THEN result2 ...] [ELSE else_result]
265
    // _children layout: [when1, then1, when2, then2, ..., else?]
266
    //
267
    // Examples:
268
    //   CASE WHEN a THEN b END           -> children=[a,b], size=2, num_branches=2 (b + null)
269
    //   CASE WHEN a THEN b ELSE c END    -> children=[a,b,c], size=3, num_branches=2 (b + c)
270
    //   CASE WHEN a THEN b WHEN c THEN d END -> children=[a,b,c,d], size=4, num_branches=3 (b + d + null)
271
    //
272
    // num_branches = number of when/then pairs + 1 (for else or null output)
273
0
    const size_t num_branches = _children.size() / 2 + 1;
274
0
    std::vector<ColumnAndSelector> columns_and_selectors;
275
0
    columns_and_selectors.resize(num_branches);
276
277
0
    Selector* executor_selector = selector;
278
0
    size_t executor_count = count;
279
280
0
    Selector* current_selector = nullptr;
281
282
0
    Selector left_not_matched_executor_selector;
283
0
    Selector left_not_matched_current_selector;
284
285
0
    int64_t executed_branches = 0;
286
287
0
    for (int64_t i = 0; i < static_cast<int64_t>(_children.size()) - _has_else_expr; i += 2) {
288
0
        executed_branches++;
289
0
        ColumnPtr when_column_ptr;
290
291
0
        RETURN_IF_ERROR(try_early_return_on_empty(_children[i], context, block, executor_selector,
292
0
                                                  executor_count, when_column_ptr));
293
294
0
        DCHECK(executor_selector == nullptr ||
295
0
               executor_selector->size() == when_column_ptr->size());
296
297
0
        Selector matched_executor_selector;
298
0
        Selector matched_current_selector;
299
0
        Selector not_matched_executor_selector;
300
0
        Selector not_matched_current_selector;
301
302
0
        execute_case_selector(when_column_ptr, executor_selector, executor_count, current_selector,
303
0
                              matched_executor_selector, matched_current_selector,
304
0
                              not_matched_executor_selector, not_matched_current_selector);
305
306
0
        ColumnPtr then_column_ptr;
307
0
        RETURN_IF_ERROR(try_early_return_on_empty(
308
0
                _children[i + 1], context, block, &matched_executor_selector,
309
0
                matched_executor_selector.size(), then_column_ptr));
310
311
0
        columns_and_selectors[i / 2].column = then_column_ptr;
312
0
        columns_and_selectors[i / 2].selector.swap(matched_current_selector);
313
314
0
        left_not_matched_executor_selector.swap(not_matched_executor_selector);
315
0
        left_not_matched_current_selector.swap(not_matched_current_selector);
316
317
0
        executor_selector = &left_not_matched_executor_selector;
318
0
        executor_count = left_not_matched_executor_selector.size();
319
0
        current_selector = &left_not_matched_current_selector;
320
321
0
        if (executor_count == 0) {
322
0
            columns_and_selectors.resize(executed_branches);
323
            // All rows have been matched; no need to process other branch
324
0
            result_column = dispatch_fill_columns(columns_and_selectors, count);
325
0
            return Status::OK();
326
0
        }
327
0
    }
328
329
    // handle the else branch
330
0
    if (_has_else_expr) {
331
0
        DCHECK_EQ(columns_and_selectors.size(), (_children.size() + 1) / 2);
332
333
0
        ColumnPtr else_column_ptr;
334
335
0
        RETURN_IF_ERROR(try_early_return_on_empty(_children.back(), context, block,
336
0
                                                  executor_selector, executor_count,
337
0
                                                  else_column_ptr));
338
0
        columns_and_selectors.back().column = else_column_ptr;
339
0
        columns_and_selectors.back().selector.swap(left_not_matched_current_selector);
340
0
    } else {
341
        // no else branch, all remaining rows are null
342
0
        columns_and_selectors.back().selector.swap(left_not_matched_current_selector);
343
0
    }
344
345
0
    result_column = dispatch_fill_columns(columns_and_selectors, count);
346
0
    return Status::OK();
347
0
}
348
349
// For ifnull(expr1, expr2), we distinguish between null and not-null rows of expr1:
350
// - Not-null rows: Use expr1's value directly (already computed), only need self_selector
351
//   to filter the column. No need for executor_selector since we don't execute further.
352
// - Null rows: Need executor_selector to execute expr2, and current_selector for result filling.
353
//
354
// This differs from IF where both branches need to execute child expressions.
355
356
void execute_ifnull_selector(const ColumnPtr& cond_column, const Selector* selector, size_t count,
357
                             Selector& null_executor_selector, Selector& null_current_selector,
358
0
                             Selector& not_null_self_selector) {
359
0
    ConditionColumnNullView condition_view =
360
0
            ConditionColumnNullView::create(cond_column, selector, count);
361
362
0
    null_executor_selector.reserve(count);
363
0
    null_current_selector.reserve(count);
364
0
    not_null_self_selector.reserve(count);
365
366
0
    auto null_func = [&](size_t self_index, size_t executor_index) {
367
0
        null_current_selector.push_back(self_index);
368
0
        null_executor_selector.push_back(executor_index);
369
0
    };
370
0
    auto not_null_func = [&](size_t self_index, size_t executor_index) {
371
0
        not_null_self_selector.push_back(self_index);
372
0
    };
373
374
0
    condition_view.for_each(null_func, not_null_func);
375
0
}
376
377
Status ShortCircuitIfNullExpr::execute_column(VExprContext* context, const Block* block,
378
                                              Selector* selector, size_t count,
379
0
                                              ColumnPtr& result_column) const {
380
0
    DCHECK(_open_finished || block == nullptr) << debug_string();
381
0
    DCHECK(selector == nullptr || selector->size() == count);
382
0
    ColumnPtr expr1_column;
383
0
    RETURN_IF_ERROR(
384
0
            try_early_return_on_empty(_children[0], context, block, selector, count, expr1_column));
385
0
    DCHECK_EQ(expr1_column->size(), count);
386
0
    Selector null_executor_selector;
387
0
    Selector null_current_selector;
388
0
    Selector not_null_self_selector;
389
390
0
    execute_ifnull_selector(expr1_column, selector, count, null_executor_selector,
391
0
                            null_current_selector, not_null_self_selector);
392
    // filter not null part
393
0
    expr1_column = filter_column_with_selector(expr1_column, &not_null_self_selector,
394
0
                                               not_null_self_selector.size());
395
396
0
    ColumnPtr expr2_column;
397
398
0
    RETURN_IF_ERROR(try_early_return_on_empty(_children[1], context, block, &null_executor_selector,
399
0
                                              null_executor_selector.size(), expr2_column));
400
401
0
    result_column = dispatch_fill_columns(expr1_column, not_null_self_selector, expr2_column,
402
0
                                          null_current_selector, count);
403
0
    return Status::OK();
404
0
}
405
406
void execute_coalesce_selector(const ColumnPtr& cond_column, const Selector* executor_selector,
407
                               size_t executor_count, const Selector* current_selector,
408
                               Selector& null_executor_selector, Selector& null_current_selector,
409
                               Selector& not_null_current_selector,
410
0
                               Selector& not_null_self_selector) {
411
0
    ConditionColumnNullView condition_view =
412
0
            ConditionColumnNullView::create(cond_column, executor_selector, executor_count);
413
0
    if (current_selector == nullptr) {
414
        // If current_selector is nullptr, this is the first condition column execution,
415
        // so self_index and current_index are the same
416
0
        auto null_func = [&](size_t self_index, size_t executor_index) {
417
0
            null_current_selector.push_back(self_index);
418
0
            null_executor_selector.push_back(executor_index);
419
0
        };
420
0
        auto not_null_func = [&](size_t self_index, size_t executor_index) {
421
0
            not_null_current_selector.push_back(self_index);
422
0
            not_null_self_selector.push_back(self_index);
423
0
        };
424
0
        condition_view.for_each(null_func, not_null_func);
425
0
    } else {
426
        // If current_selector is not nullptr, this is not the first condition column execution,
427
        // so self_index and current_index are different.
428
        // We need to determine current_index based on self_index and current_selector
429
0
        const auto& current_selector_data = *current_selector;
430
0
        DCHECK_EQ(current_selector_data.size(), executor_count);
431
0
        auto null_func = [&](size_t self_index, size_t executor_index) {
432
0
            null_current_selector.push_back(current_selector_data[self_index]);
433
0
            null_executor_selector.push_back(executor_index);
434
0
        };
435
0
        auto not_null_func = [&](size_t self_index, size_t executor_index) {
436
0
            not_null_current_selector.push_back(current_selector_data[self_index]);
437
0
            not_null_self_selector.push_back(self_index);
438
0
        };
439
0
        condition_view.for_each(null_func, not_null_func);
440
0
    }
441
0
}
442
443
Status ShortCircuitCoalesceExpr::execute_column(VExprContext* context, const Block* block,
444
                                                Selector* selector, size_t count,
445
0
                                                ColumnPtr& result_column) const {
446
0
    DCHECK(_open_finished || block == nullptr) << debug_string();
447
0
    DCHECK(selector == nullptr || selector->size() == count);
448
449
0
    std::vector<ColumnAndSelector> columns_and_selectors;
450
0
    columns_and_selectors.resize(_children.size() +
451
0
                                 1); // the last one represents the selector for null output
452
453
0
    Selector* executor_selector = selector;
454
0
    size_t executor_count = count;
455
456
0
    Selector* current_selector = nullptr;
457
0
    Selector left_null_executor_selector;
458
0
    Selector left_null_current_selector;
459
460
0
    int64_t executed_branches = 0;
461
462
0
    for (int64_t i = 0; i < _children.size(); ++i) {
463
0
        executed_branches++;
464
0
        ColumnPtr child_column_ptr;
465
466
0
        RETURN_IF_ERROR(try_early_return_on_empty(_children[i], context, block, executor_selector,
467
0
                                                  executor_count, child_column_ptr));
468
469
0
        DCHECK(executor_selector == nullptr ||
470
0
               executor_selector->size() == child_column_ptr->size());
471
472
0
        Selector null_executor_selector;
473
0
        Selector null_current_selector;
474
0
        Selector not_null_current_selector;
475
0
        Selector not_null_self_selector;
476
477
0
        execute_coalesce_selector(child_column_ptr, executor_selector, executor_count,
478
0
                                  current_selector, null_executor_selector, null_current_selector,
479
0
                                  not_null_current_selector, not_null_self_selector);
480
481
        // use not_null_self_selector to filter child_column_ptr
482
0
        child_column_ptr = filter_column_with_selector(child_column_ptr, &not_null_self_selector,
483
0
                                                       not_null_self_selector.size());
484
485
0
        columns_and_selectors[i].column = child_column_ptr;
486
0
        columns_and_selectors[i].selector.swap(not_null_current_selector);
487
488
0
        left_null_executor_selector.swap(null_executor_selector);
489
0
        left_null_current_selector.swap(null_current_selector);
490
491
0
        executor_selector = &left_null_executor_selector;
492
0
        executor_count = left_null_executor_selector.size();
493
0
        current_selector = &left_null_current_selector;
494
495
0
        if (executor_count == 0) {
496
0
            columns_and_selectors.resize(executed_branches);
497
            // All rows have been matched; no need to process other branch
498
0
            result_column = dispatch_fill_columns(columns_and_selectors, count);
499
0
            return Status::OK();
500
0
        }
501
0
    }
502
503
    // the remaining null rows at the end
504
0
    columns_and_selectors.back().selector.swap(left_null_current_selector);
505
506
0
    result_column = dispatch_fill_columns(columns_and_selectors, count);
507
0
    return Status::OK();
508
0
}
509
} // namespace doris