Coverage Report

Created: 2026-08-14 13:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/delete/delete_handler.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 "storage/delete/delete_handler.h"
19
20
#include <gen_cpp/PaloInternalService_types.h>
21
#include <gen_cpp/olap_file.pb.h>
22
#include <thrift/protocol/TDebugProtocol.h>
23
24
#include <string>
25
#include <vector>
26
27
#include "common/cast_set.h"
28
#include "common/check.h"
29
#include "common/config.h"
30
#include "common/logging.h"
31
#include "common/status.h"
32
#include "core/data_type_serde/data_type_serde.h"
33
#include "storage/olap_common.h"
34
#include "storage/predicate/block_column_predicate.h"
35
#include "storage/predicate/null_predicate.h"
36
#include "storage/predicate/predicate_creator.h"
37
#include "storage/tablet/tablet_schema.h"
38
#include "storage/utils.h"
39
#include "util/debug_points.h"
40
41
using apache::thrift::ThriftDebugString;
42
using std::vector;
43
using std::string;
44
45
using ::google::protobuf::RepeatedPtrField;
46
47
namespace doris {
48
49
// Parses a string value into a Field using the serde's from_fe_string, then builds
50
// a HybridSetBase for IN/NOT_IN predicates.
51
// The type-dispatch via switch/case is still needed because build_set<PType>() and
52
// HybridSet::insert(const void*) require compile-time PrimitiveType, and Field::get<PType>()
53
// must be invoked with the correct type to extract the underlying CppType value.
54
template <PrimitiveType PType>
55
9
void insert_field_to_set(const Field& field, HybridSetBase* set) {
56
9
    if constexpr (is_string_type(PType)) {
57
        // StringSet::insert expects const StringRef*, so we must construct a StringRef
58
        // from the std::string returned by Field::get<>.
59
0
        const auto& tmp = field.get<PType>();
60
0
        StringRef ref(tmp.data(), tmp.size());
61
0
        set->insert(reinterpret_cast<const void*>(&ref));
62
9
    } else {
63
9
        auto tmp = field.get<PType>();
64
9
        set->insert(reinterpret_cast<const void*>(&tmp));
65
9
    }
66
9
}
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE3EEEvRKNS_5FieldEPNS_13HybridSetBaseE
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE4EEEvRKNS_5FieldEPNS_13HybridSetBaseE
_ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE5EEEvRKNS_5FieldEPNS_13HybridSetBaseE
Line
Count
Source
55
4
void insert_field_to_set(const Field& field, HybridSetBase* set) {
56
    if constexpr (is_string_type(PType)) {
57
        // StringSet::insert expects const StringRef*, so we must construct a StringRef
58
        // from the std::string returned by Field::get<>.
59
        const auto& tmp = field.get<PType>();
60
        StringRef ref(tmp.data(), tmp.size());
61
        set->insert(reinterpret_cast<const void*>(&ref));
62
4
    } else {
63
4
        auto tmp = field.get<PType>();
64
4
        set->insert(reinterpret_cast<const void*>(&tmp));
65
4
    }
66
4
}
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE6EEEvRKNS_5FieldEPNS_13HybridSetBaseE
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE7EEEvRKNS_5FieldEPNS_13HybridSetBaseE
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE8EEEvRKNS_5FieldEPNS_13HybridSetBaseE
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE9EEEvRKNS_5FieldEPNS_13HybridSetBaseE
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE11EEEvRKNS_5FieldEPNS_13HybridSetBaseE
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE12EEEvRKNS_5FieldEPNS_13HybridSetBaseE
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE25EEEvRKNS_5FieldEPNS_13HybridSetBaseE
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE26EEEvRKNS_5FieldEPNS_13HybridSetBaseE
_ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE42EEEvRKNS_5FieldEPNS_13HybridSetBaseE
Line
Count
Source
55
5
void insert_field_to_set(const Field& field, HybridSetBase* set) {
56
    if constexpr (is_string_type(PType)) {
57
        // StringSet::insert expects const StringRef*, so we must construct a StringRef
58
        // from the std::string returned by Field::get<>.
59
        const auto& tmp = field.get<PType>();
60
        StringRef ref(tmp.data(), tmp.size());
61
        set->insert(reinterpret_cast<const void*>(&ref));
62
5
    } else {
63
5
        auto tmp = field.get<PType>();
64
5
        set->insert(reinterpret_cast<const void*>(&tmp));
65
5
    }
66
5
}
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE2EEEvRKNS_5FieldEPNS_13HybridSetBaseE
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE36EEEvRKNS_5FieldEPNS_13HybridSetBaseE
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE37EEEvRKNS_5FieldEPNS_13HybridSetBaseE
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE20EEEvRKNS_5FieldEPNS_13HybridSetBaseE
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE28EEEvRKNS_5FieldEPNS_13HybridSetBaseE
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE29EEEvRKNS_5FieldEPNS_13HybridSetBaseE
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE30EEEvRKNS_5FieldEPNS_13HybridSetBaseE
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE35EEEvRKNS_5FieldEPNS_13HybridSetBaseE
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE15EEEvRKNS_5FieldEPNS_13HybridSetBaseE
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE10EEEvRKNS_5FieldEPNS_13HybridSetBaseE
Unexecuted instantiation: _ZN5doris19insert_field_to_setILNS_13PrimitiveTypeE23EEEvRKNS_5FieldEPNS_13HybridSetBaseE
67
68
#define FROM_FE_STRING_CASE(PType)                            \
69
4
    case PType: {                                             \
70
4
        set = build_set<PType>();                             \
71
9
        for (const auto& s : str) {                           \
72
9
            Field field;                                      \
73
9
            RETURN_IF_ERROR(serde->from_fe_string(s, field)); \
74
9
            insert_field_to_set<PType>(field, set.get());     \
75
9
        }                                                     \
76
4
        return Status::OK();                                  \
77
4
    }
78
Status convert(const DataTypePtr& data_type, const std::list<std::string>& str,
79
4
               std::shared_ptr<HybridSetBase>& set) {
80
4
    auto serde = data_type->get_serde();
81
4
    switch (data_type->get_primitive_type()) {
82
0
        FROM_FE_STRING_CASE(TYPE_TINYINT);
83
0
        FROM_FE_STRING_CASE(TYPE_SMALLINT);
84
2
        FROM_FE_STRING_CASE(TYPE_INT);
85
0
        FROM_FE_STRING_CASE(TYPE_BIGINT);
86
0
        FROM_FE_STRING_CASE(TYPE_LARGEINT);
87
0
        FROM_FE_STRING_CASE(TYPE_FLOAT);
88
0
        FROM_FE_STRING_CASE(TYPE_DOUBLE);
89
0
        FROM_FE_STRING_CASE(TYPE_DATE);
90
0
        FROM_FE_STRING_CASE(TYPE_DATETIME);
91
0
        FROM_FE_STRING_CASE(TYPE_DATEV2);
92
0
        FROM_FE_STRING_CASE(TYPE_DATETIMEV2);
93
2
        FROM_FE_STRING_CASE(TYPE_TIMESTAMPTZ);
94
0
        FROM_FE_STRING_CASE(TYPE_BOOLEAN);
95
0
        FROM_FE_STRING_CASE(TYPE_IPV4);
96
0
        FROM_FE_STRING_CASE(TYPE_IPV6);
97
0
        FROM_FE_STRING_CASE(TYPE_DECIMALV2);
98
0
        FROM_FE_STRING_CASE(TYPE_DECIMAL32);
99
0
        FROM_FE_STRING_CASE(TYPE_DECIMAL64);
100
0
        FROM_FE_STRING_CASE(TYPE_DECIMAL128I);
101
0
        FROM_FE_STRING_CASE(TYPE_DECIMAL256);
102
0
        FROM_FE_STRING_CASE(TYPE_CHAR);
103
0
        FROM_FE_STRING_CASE(TYPE_VARCHAR);
104
0
        FROM_FE_STRING_CASE(TYPE_STRING);
105
0
    default:
106
0
        return Status::Error<ErrorCode::INVALID_ARGUMENT>(
107
0
                "unsupported data type in delete handler. type={}",
108
0
                type_to_string(data_type->get_primitive_type()));
109
4
    }
110
0
    return Status::OK();
111
4
}
112
#undef FROM_FE_STRING_CASE
113
114
// Parses a single condition value string into a Field and creates a comparison predicate.
115
// Uses serde->from_fe_string to do the parsing, which handles all type-specific
116
// conversions (including decimal scale, etc.).
117
Status parse_to_predicate(const uint32_t index, const std::string col_name, const DataTypePtr& type,
118
                          DeleteHandler::ConditionParseResult& res, Arena& arena,
119
189
                          std::shared_ptr<ColumnPredicate>& predicate) {
120
189
    DCHECK_EQ(res.value_str.size(), 1);
121
189
    if (res.condition_op == PredicateType::IS_NULL ||
122
189
        res.condition_op == PredicateType::IS_NOT_NULL) {
123
37
        predicate = NullPredicate::create_shared(index, col_name,
124
37
                                                 res.condition_op == PredicateType::IS_NOT_NULL,
125
37
                                                 type->get_primitive_type());
126
37
        return Status::OK();
127
37
    }
128
129
152
    Field v;
130
152
    if (is_string_type(type->get_primitive_type())) {
131
38
        v = Field::create_field<TYPE_STRING>(res.value_str.front());
132
114
    } else {
133
114
        auto serde = type->get_serde();
134
114
        RETURN_IF_ERROR(serde->from_fe_string(res.value_str.front(), v));
135
114
    }
136
137
145
    switch (res.condition_op) {
138
55
    case PredicateType::EQ:
139
55
        predicate = create_comparison_predicate<PredicateType::EQ>(index, col_name, type, v, true);
140
55
        return Status::OK();
141
8
    case PredicateType::NE:
142
8
        predicate = create_comparison_predicate<PredicateType::NE>(index, col_name, type, v, true);
143
8
        return Status::OK();
144
2
    case PredicateType::GT:
145
2
        predicate = create_comparison_predicate<PredicateType::GT>(index, col_name, type, v, true);
146
2
        return Status::OK();
147
2
    case PredicateType::GE:
148
2
        predicate = create_comparison_predicate<PredicateType::GE>(index, col_name, type, v, true);
149
2
        return Status::OK();
150
76
    case PredicateType::LT:
151
76
        predicate = create_comparison_predicate<PredicateType::LT>(index, col_name, type, v, true);
152
76
        return Status::OK();
153
2
    case PredicateType::LE:
154
2
        predicate = create_comparison_predicate<PredicateType::LE>(index, col_name, type, v, true);
155
2
        return Status::OK();
156
0
    default:
157
0
        return Status::Error<ErrorCode::INVALID_ARGUMENT>("invalid condition operator. operator={}",
158
0
                                                          type_to_op_str(res.condition_op));
159
145
    }
160
145
}
161
162
Status parse_to_in_predicate(const uint32_t index, const std::string& col_name,
163
                             const DataTypePtr& type, DeleteHandler::ConditionParseResult& res,
164
4
                             Arena& arena, std::shared_ptr<ColumnPredicate>& predicate) {
165
4
    DCHECK_GT(res.value_str.size(), 1);
166
4
    switch (res.condition_op) {
167
3
    case PredicateType::IN_LIST: {
168
3
        std::shared_ptr<HybridSetBase> set;
169
3
        RETURN_IF_ERROR(convert(type, res.value_str, set));
170
3
        predicate =
171
3
                create_in_list_predicate<PredicateType::IN_LIST>(index, col_name, type, set, true);
172
3
        break;
173
3
    }
174
1
    case PredicateType::NOT_IN_LIST: {
175
1
        std::shared_ptr<HybridSetBase> set;
176
1
        RETURN_IF_ERROR(convert(type, res.value_str, set));
177
1
        predicate = create_in_list_predicate<PredicateType::NOT_IN_LIST>(index, col_name, type, set,
178
1
                                                                         true);
179
1
        break;
180
1
    }
181
0
    default:
182
0
        return Status::Error<ErrorCode::INVALID_ARGUMENT>("invalid condition operator. operator={}",
183
0
                                                          type_to_op_str(res.condition_op));
184
4
    }
185
4
    return Status::OK();
186
4
}
187
188
// construct sub condition from TCondition
189
101
std::string construct_sub_predicate(const TCondition& condition) {
190
101
    string op = condition.condition_op;
191
101
    if (op == "<") {
192
50
        op += "<";
193
51
    } else if (op == ">") {
194
3
        op += ">";
195
3
    }
196
101
    string condition_str;
197
101
    if ("IS" == op) {
198
        // ATTN: tricky! Surround IS with spaces to make it "special"
199
2
        condition_str = condition.column_name + " IS " + condition.condition_values[0];
200
99
    } else { // multi-elements IN expr has been processed with InPredicatePB
201
99
        if (op == "*=") {
202
2
            op = "=";
203
97
        } else if (op == "!*=") {
204
2
            op = "!=";
205
2
        }
206
99
        condition_str = condition.column_name + op + "'" + condition.condition_values[0] + "'";
207
99
    }
208
101
    return condition_str;
209
101
}
210
211
// make operators from FE adaptive to BE
212
101
std::string trans_op(const std::string& opt) {
213
101
    std::string op = string(opt);
214
101
    if (op == "<") {
215
50
        op += "<";
216
51
    } else if (op == ">") {
217
3
        op += ">";
218
3
    }
219
101
    if ("IS" != op) {
220
99
        if (op == "*=") {
221
2
            op = "=";
222
97
        } else if (op == "!*=") {
223
2
            op = "!=";
224
2
        }
225
99
    }
226
101
    return op;
227
101
}
228
229
Status DeleteHandler::generate_delete_predicate(const TabletSchema& schema,
230
                                                const std::vector<TCondition>& conditions,
231
106
                                                DeletePredicatePB* del_pred) {
232
106
    DBUG_EXECUTE_IF("DeleteHandler::generate_delete_predicate.inject_failure", {
233
106
        return Status::Error<false>(dp->param<int>("error_code"),
234
106
                                    dp->param<std::string>("error_msg"));
235
106
    })
236
106
    if (conditions.empty()) {
237
1
        return Status::Error<ErrorCode::INVALID_ARGUMENT>(
238
1
                "invalid parameters for store_cond. condition_size={}", conditions.size());
239
1
    }
240
241
    // Check whether the delete condition meets the requirements
242
135
    for (const TCondition& condition : conditions) {
243
135
        RETURN_IF_ERROR(check_condition_valid(schema, condition));
244
135
    }
245
246
    // Store delete condition
247
105
    for (const TCondition& condition : conditions) {
248
105
        if (condition.condition_values.size() > 1) {
249
4
            InPredicatePB* in_pred = del_pred->add_in_predicates();
250
4
            if (condition.__isset.column_unique_id) {
251
0
                in_pred->set_column_unique_id(condition.column_unique_id);
252
0
            }
253
4
            in_pred->set_column_name(condition.column_name);
254
4
            bool is_not_in = condition.condition_op == "!*=";
255
4
            in_pred->set_is_not_in(is_not_in);
256
10
            for (const auto& condition_value : condition.condition_values) {
257
10
                in_pred->add_values(condition_value);
258
10
            }
259
260
4
            LOG(INFO) << "store one sub-delete condition. condition name=" << in_pred->column_name()
261
4
                      << "condition size=" << in_pred->values().size();
262
101
        } else {
263
            // write sub predicate v1 for compactbility
264
101
            std::string condition_str = construct_sub_predicate(condition);
265
101
            VLOG_NOTICE << __PRETTY_FUNCTION__ << " condition_str: " << condition_str;
266
101
            del_pred->add_sub_predicates(condition_str);
267
101
            DeleteSubPredicatePB* sub_predicate = del_pred->add_sub_predicates_v2();
268
101
            if (condition.__isset.column_unique_id) {
269
                // only light schema change capable table set this field
270
0
                sub_predicate->set_column_unique_id(condition.column_unique_id);
271
101
            } else {
272
101
                try {
273
101
                    [[maybe_unused]] auto parsed_cond = parse_condition(condition_str);
274
101
                } catch (const Exception& e) {
275
0
                    return Status::Error<ErrorCode::INVALID_ARGUMENT>(
276
0
                            "failed to parse condition_str, condition={}, error={}",
277
0
                            ThriftDebugString(condition), e.to_string());
278
0
                }
279
101
            }
280
281
101
            sub_predicate->set_column_name(condition.column_name);
282
101
            sub_predicate->set_op(trans_op(condition.condition_op));
283
101
            sub_predicate->set_cond_value(condition.condition_values[0]);
284
101
            LOG(INFO) << "store one sub-delete condition. condition="
285
101
                      << fmt::format(" {} {} {}", condition.column_name, condition.condition_op,
286
101
                                     condition.condition_values[0]);
287
101
        }
288
105
    }
289
75
    del_pred->set_version(-1);
290
291
75
    return Status::OK();
292
75
}
293
294
Status DeleteHandler::convert_to_sub_pred_v2(DeletePredicatePB* delete_pred,
295
0
                                             TabletSchemaSPtr schema) {
296
0
    if (!delete_pred->sub_predicates().empty() && delete_pred->sub_predicates_v2().empty()) {
297
0
        for (const auto& condition_str : delete_pred->sub_predicates()) {
298
0
            auto* sub_pred = delete_pred->add_sub_predicates_v2();
299
0
            auto condition = parse_condition(condition_str);
300
0
            const auto& column = *DORIS_TRY(schema->column(condition.column_name));
301
0
            sub_pred->set_column_unique_id(column.unique_id());
302
0
            sub_pred->set_column_name(condition.column_name);
303
0
            sub_pred->set_op(type_to_op_str(condition.condition_op));
304
0
            sub_pred->set_cond_value(condition.value_str.front());
305
0
        }
306
0
    }
307
308
0
    auto* in_pred_list = delete_pred->mutable_in_predicates();
309
0
    for (auto& in_pred : *in_pred_list) {
310
0
        const auto& column = *DORIS_TRY(schema->column(in_pred.column_name()));
311
0
        in_pred.set_column_unique_id(column.unique_id());
312
0
    }
313
0
    return Status::OK();
314
0
}
315
316
bool DeleteHandler::is_condition_value_valid(const TabletColumn& column,
317
                                             const std::string& condition_op,
318
140
                                             const string& value_str) {
319
140
    if ("IS" == condition_op && ("NULL" == value_str || "NOT NULL" == value_str)) {
320
2
        return true;
321
2
    }
322
323
138
    FieldType field_type = column.type();
324
138
    switch (field_type) {
325
11
    case FieldType::OLAP_FIELD_TYPE_TINYINT:
326
11
        return valid_signed_number<int8_t>(value_str);
327
13
    case FieldType::OLAP_FIELD_TYPE_SMALLINT:
328
13
        return valid_signed_number<int16_t>(value_str);
329
54
    case FieldType::OLAP_FIELD_TYPE_INT:
330
54
        return valid_signed_number<int32_t>(value_str);
331
5
    case FieldType::OLAP_FIELD_TYPE_BIGINT:
332
5
        return valid_signed_number<int64_t>(value_str);
333
4
    case FieldType::OLAP_FIELD_TYPE_LARGEINT:
334
4
        return valid_signed_number<int128_t>(value_str);
335
0
    case FieldType::OLAP_FIELD_TYPE_UNSIGNED_TINYINT:
336
0
        return valid_unsigned_number<uint8_t>(value_str);
337
0
    case FieldType::OLAP_FIELD_TYPE_UNSIGNED_SMALLINT:
338
0
        return valid_unsigned_number<uint16_t>(value_str);
339
0
    case FieldType::OLAP_FIELD_TYPE_UNSIGNED_INT:
340
0
        return valid_unsigned_number<uint32_t>(value_str);
341
0
    case FieldType::OLAP_FIELD_TYPE_UNSIGNED_BIGINT:
342
0
        return valid_unsigned_number<uint64_t>(value_str);
343
7
    case FieldType::OLAP_FIELD_TYPE_DECIMAL:
344
7
        return valid_decimal(value_str, column.precision(), column.frac());
345
0
    case FieldType::OLAP_FIELD_TYPE_DECIMAL32:
346
0
        return valid_decimal(value_str, column.precision(), column.frac());
347
0
    case FieldType::OLAP_FIELD_TYPE_DECIMAL64:
348
0
        return valid_decimal(value_str, column.precision(), column.frac());
349
0
    case FieldType::OLAP_FIELD_TYPE_DECIMAL128I:
350
0
        return valid_decimal(value_str, column.precision(), column.frac());
351
0
    case FieldType::OLAP_FIELD_TYPE_DECIMAL256:
352
0
        return valid_decimal(value_str, column.precision(), column.frac());
353
3
    case FieldType::OLAP_FIELD_TYPE_CHAR:
354
7
    case FieldType::OLAP_FIELD_TYPE_VARCHAR:
355
7
        return value_str.size() <= column.length();
356
0
    case FieldType::OLAP_FIELD_TYPE_STRING:
357
0
        return value_str.size() <= config::string_type_length_soft_limit_bytes;
358
5
    case FieldType::OLAP_FIELD_TYPE_DATE:
359
11
    case FieldType::OLAP_FIELD_TYPE_DATETIME:
360
11
    case FieldType::OLAP_FIELD_TYPE_DATEV2:
361
11
    case FieldType::OLAP_FIELD_TYPE_DATETIMEV2:
362
24
    case FieldType::OLAP_FIELD_TYPE_TIMESTAMPTZ:
363
24
        return valid_datetime(value_str, column.frac());
364
0
    case FieldType::OLAP_FIELD_TYPE_BOOL:
365
0
        return valid_bool(value_str);
366
5
    case FieldType::OLAP_FIELD_TYPE_IPV4:
367
5
        return valid_ipv4(value_str);
368
8
    case FieldType::OLAP_FIELD_TYPE_IPV6:
369
8
        return valid_ipv6(value_str);
370
0
    default:
371
0
        LOG(WARNING) << "unknown field type. [type=" << int(field_type) << "]";
372
138
    }
373
0
    return false;
374
138
}
375
376
135
Status DeleteHandler::check_condition_valid(const TabletSchema& schema, const TCondition& cond) {
377
    // Check whether the column exists
378
135
    int32_t field_index = schema.field_index(cond.column_name);
379
135
    if (field_index < 0) {
380
1
        return Status::Error<ErrorCode::INVALID_ARGUMENT>("field is not existent. [field_index={}]",
381
1
                                                          field_index);
382
1
    }
383
384
    // Delete condition should only applied on key columns or duplicate key table, and
385
    // the condition column type should not be float or double.
386
134
    const TabletColumn& column = schema.column(field_index);
387
388
134
    if (column.type() == FieldType::OLAP_FIELD_TYPE_DOUBLE ||
389
134
        column.type() == FieldType::OLAP_FIELD_TYPE_FLOAT) {
390
0
        return Status::Error<ErrorCode::INVALID_ARGUMENT>("data type is float or double.");
391
0
    }
392
393
    // Check operator and operands size are matched.
394
134
    if ("*=" != cond.condition_op && "!*=" != cond.condition_op &&
395
134
        cond.condition_values.size() != 1) {
396
0
        return Status::Error<ErrorCode::INVALID_ARGUMENT>("invalid condition value size. [size={}]",
397
0
                                                          cond.condition_values.size());
398
0
    }
399
400
    // Check each operand is valid
401
140
    for (const auto& condition_value : cond.condition_values) {
402
140
        if (!is_condition_value_valid(column, cond.condition_op, condition_value)) {
403
29
            return Status::Error<ErrorCode::INVALID_ARGUMENT>("invalid condition value. [value={}]",
404
29
                                                              condition_value);
405
29
        }
406
140
    }
407
408
105
    if (!cond.__isset.column_unique_id) {
409
105
        LOG(WARNING) << "column=" << cond.column_name
410
105
                     << " in predicate does not have uid, table id=" << schema.table_id();
411
        // TODO(tsy): make it fail here after FE forbidding hard-link-schema-change
412
105
        return Status::OK();
413
105
    }
414
0
    if (schema.field_index(cond.column_unique_id) == -1) {
415
0
        const auto& err_msg =
416
0
                fmt::format("column id does not exists in table={}, schema version={},",
417
0
                            schema.table_id(), schema.schema_version());
418
0
        return Status::Error<ErrorCode::INVALID_ARGUMENT>(err_msg);
419
0
    }
420
0
    if (!iequal(schema.column_by_uid(cond.column_unique_id).name(), cond.column_name)) {
421
0
        const auto& err_msg = fmt::format(
422
0
                "colum name={} does not belongs to column uid={}, which "
423
0
                "column name={}, "
424
0
                "delete_cond.column_name ={}",
425
0
                cond.column_name, cond.column_unique_id,
426
0
                schema.column_by_uid(cond.column_unique_id).name(), cond.column_name);
427
0
        return Status::Error<ErrorCode::INVALID_ARGUMENT>(err_msg);
428
0
    }
429
430
0
    return Status::OK();
431
0
}
432
433
PredicateType DeleteHandler::parse_condition_op(const std::string& op_str,
434
310
                                                const std::list<std::string>& cond_values) {
435
310
    if (trim(to_lower(op_str)) == "=") {
436
99
        return PredicateType::EQ;
437
211
    } else if (trim(to_lower(op_str)) == "!=") {
438
19
        return PredicateType::NE;
439
192
    } else if (trim(to_lower(op_str)) == ">>") {
440
9
        return PredicateType::GT;
441
183
    } else if (trim(to_lower(op_str)) == "<<") {
442
129
        return PredicateType::LT;
443
129
    } else if (trim(to_lower(op_str)) == ">=") {
444
5
        return PredicateType::GE;
445
49
    } else if (trim(to_lower(op_str)) == "<=") {
446
7
        return PredicateType::LE;
447
42
    } else if (trim(to_lower(op_str)) == "*=") {
448
0
        return cond_values.size() > 1 ? PredicateType::IN_LIST : PredicateType::EQ;
449
42
    } else if (trim(to_lower(op_str)) == "!*=") {
450
0
        return cond_values.size() > 1 ? PredicateType::NOT_IN_LIST : PredicateType::NE;
451
42
    } else if (trim(to_lower(op_str)) == "is") {
452
42
        return to_lower(cond_values.front()) == "null" ? PredicateType::IS_NULL
453
42
                                                       : PredicateType::IS_NOT_NULL;
454
42
    } else {
455
0
        throw Exception(Status::Error<ErrorCode::INVALID_ARGUMENT>(
456
0
                "invalid condition operator. operator={}", op_str));
457
0
    }
458
0
    return PredicateType::UNKNOWN;
459
310
}
460
461
DeleteHandler::ConditionParseResult DeleteHandler::parse_condition(
462
106
        const DeleteSubPredicatePB& sub_cond) {
463
106
    ConditionParseResult res;
464
106
    if (!sub_cond.has_column_name() || !sub_cond.has_op() || !sub_cond.has_cond_value()) {
465
0
        throw Exception(Status::Error<ErrorCode::INVALID_ARGUMENT>(
466
0
                "fail to parse condition. condition={} {} {}", sub_cond.column_name(),
467
0
                sub_cond.op(), sub_cond.cond_value()));
468
0
    }
469
106
    if (sub_cond.has_column_unique_id()) {
470
6
        res.col_unique_id = sub_cond.column_unique_id();
471
6
    }
472
106
    res.column_name = sub_cond.column_name();
473
106
    res.value_str.push_back(sub_cond.cond_value());
474
106
    res.condition_op = parse_condition_op(sub_cond.op(), res.value_str);
475
106
    return res;
476
106
}
477
478
// clang-format off
479
// Condition string format, the format is (column_name)(op)(value)
480
// eg: condition_str="c1 = 1597751948193618247 and length(source)<1;\n;\n"
481
// column_name: matches "c1", must include FeNameFormat.java COLUMN_NAME_REGEX
482
//              and compactible with any the lagacy
483
// operator: matches "="
484
// value: matches "1597751948193618247  and length(source)<1;\n;\n"
485
//
486
// For more info, see DeleteHandler::construct_sub_predicates
487
// FIXME(gavin): This is a tricky implementation, it should not be the final resolution, refactor it.
488
const char* const CONDITION_STR_PATTERN =
489
    // .----------------- column-name --------------------------.   .----------------------- operator ------------------------.   .------------ value ----------.
490
    R"(([_a-zA-Z@0-9\s/\p{L}][.a-zA-Z0-9_+-/?@#$%^&*"\s,:\p{L}]*)\s*((?:=)|(?:!=)|(?:>>)|(?:<<)|(?:>=)|(?:<=)|(?:\*=)|(?: IS ))\s*('((?:[\s\S]+)?)'|(?:[\s\S]+)?))";
491
    // '----------------- group 1 ------------------------------'   '--------------------- group 2 ---------------------------'   | '-- group 4--'              |
492
    //                                                                   match any of: = != >> << >= <= *= " IS "                 '----------- group 3 ---------'
493
    //                                                                                                                             match **ANY THING** without(4)
494
    //                                                                                                                             or with(3) single quote
495
// clang-format on
496
RE2 DELETE_HANDLER_REGEX(CONDITION_STR_PATTERN);
497
498
DeleteHandler::ConditionParseResult DeleteHandler::parse_condition(
499
184
        const std::string& condition_str) {
500
184
    ConditionParseResult res;
501
184
    std::string col_name, op, value, g4;
502
503
184
    bool matched = RE2::FullMatch(condition_str, DELETE_HANDLER_REGEX, &col_name, &op, &value,
504
184
                                  &g4); // exact match
505
506
184
    if (!matched) {
507
0
        throw Exception(
508
0
                Status::InvalidArgument("fail to sub condition. condition={}", condition_str));
509
0
    }
510
511
184
    res.column_name = col_name;
512
513
    // match string with single quotes, a = b  or a = 'b'
514
184
    if (!g4.empty()) {
515
143
        res.value_str.push_back(g4);
516
143
    } else {
517
41
        res.value_str.push_back(value);
518
41
    }
519
184
    res.condition_op = DeleteHandler::parse_condition_op(op, res.value_str);
520
184
    VLOG_NOTICE << "parsed condition_str: col_name={" << col_name << "} op={" << op << "} val={"
521
48
                << res.value_str.back() << "}";
522
184
    return res;
523
184
}
524
525
template <typename SubPredType>
526
    requires(std::is_same_v<SubPredType, DeleteSubPredicatePB> or
527
             std::is_same_v<SubPredType, std::string>)
528
Status DeleteHandler::_parse_column_pred(const ReadSchema& read_schema,
529
                                         const TabletSchemaSPtr& delete_pred_related_schema,
530
                                         const RepeatedPtrField<SubPredType>& sub_pred_list,
531
                                         DeleteConditions* delete_conditions,
532
176
                                         std::vector<TabletColumn>* dropped_columns) {
533
189
    for (const auto& sub_predicate : sub_pred_list) {
534
189
        auto condition = parse_condition(sub_predicate);
535
189
        int32_t col_unique_id = -1;
536
189
        if constexpr (std::is_same_v<SubPredType, DeleteSubPredicatePB>) {
537
106
            if (sub_predicate.has_column_unique_id()) [[likely]] {
538
6
                col_unique_id = sub_predicate.column_unique_id();
539
6
            }
540
106
        }
541
189
        ColumnId column_id;
542
189
        const TabletColumn* column;
543
189
        RETURN_IF_ERROR(_resolve_column(read_schema, col_unique_id, condition.column_name,
544
189
                                        delete_pred_related_schema, &column_id, &column,
545
189
                                        dropped_columns));
546
189
        condition.col_unique_id = column->unique_id();
547
189
        std::shared_ptr<ColumnPredicate> predicate;
548
189
        RETURN_IF_ERROR(parse_to_predicate(column_id, column->name(), column->get_vec_type(),
549
189
                                           condition, _predicate_arena, predicate));
550
182
        if (predicate != nullptr) {
551
182
            delete_conditions->column_predicate_vec.push_back(predicate);
552
182
        }
553
182
    }
554
169
    return Status::OK();
555
176
}
_ZN5doris13DeleteHandler18_parse_column_predINS_20DeleteSubPredicatePBEQoosr3stdE9is_same_vIT_S2_Esr3stdE9is_same_vIS3_NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEEENS_6StatusERKNS_10ReadSchemaERKSt10shared_ptrINS_12TabletSchemaEERKN6google8protobuf16RepeatedPtrFieldIS3_EEPNS_16DeleteConditionsEPSt6vectorINS_12TabletColumnESaISS_EE
Line
Count
Source
532
91
                                         std::vector<TabletColumn>* dropped_columns) {
533
106
    for (const auto& sub_predicate : sub_pred_list) {
534
106
        auto condition = parse_condition(sub_predicate);
535
106
        int32_t col_unique_id = -1;
536
106
        if constexpr (std::is_same_v<SubPredType, DeleteSubPredicatePB>) {
537
106
            if (sub_predicate.has_column_unique_id()) [[likely]] {
538
6
                col_unique_id = sub_predicate.column_unique_id();
539
6
            }
540
106
        }
541
106
        ColumnId column_id;
542
106
        const TabletColumn* column;
543
106
        RETURN_IF_ERROR(_resolve_column(read_schema, col_unique_id, condition.column_name,
544
106
                                        delete_pred_related_schema, &column_id, &column,
545
106
                                        dropped_columns));
546
106
        condition.col_unique_id = column->unique_id();
547
106
        std::shared_ptr<ColumnPredicate> predicate;
548
106
        RETURN_IF_ERROR(parse_to_predicate(column_id, column->name(), column->get_vec_type(),
549
106
                                           condition, _predicate_arena, predicate));
550
106
        if (predicate != nullptr) {
551
106
            delete_conditions->column_predicate_vec.push_back(predicate);
552
106
        }
553
106
    }
554
91
    return Status::OK();
555
91
}
_ZN5doris13DeleteHandler18_parse_column_predINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEQoosr3stdE9is_same_vIT_NS_20DeleteSubPredicatePBEEsr3stdE9is_same_vIS8_S7_EEENS_6StatusERKNS_10ReadSchemaERKSt10shared_ptrINS_12TabletSchemaEERKN6google8protobuf16RepeatedPtrFieldIS8_EEPNS_16DeleteConditionsEPSt6vectorINS_12TabletColumnESaISS_EE
Line
Count
Source
532
85
                                         std::vector<TabletColumn>* dropped_columns) {
533
85
    for (const auto& sub_predicate : sub_pred_list) {
534
83
        auto condition = parse_condition(sub_predicate);
535
83
        int32_t col_unique_id = -1;
536
        if constexpr (std::is_same_v<SubPredType, DeleteSubPredicatePB>) {
537
            if (sub_predicate.has_column_unique_id()) [[likely]] {
538
                col_unique_id = sub_predicate.column_unique_id();
539
            }
540
        }
541
83
        ColumnId column_id;
542
83
        const TabletColumn* column;
543
83
        RETURN_IF_ERROR(_resolve_column(read_schema, col_unique_id, condition.column_name,
544
83
                                        delete_pred_related_schema, &column_id, &column,
545
83
                                        dropped_columns));
546
83
        condition.col_unique_id = column->unique_id();
547
83
        std::shared_ptr<ColumnPredicate> predicate;
548
83
        RETURN_IF_ERROR(parse_to_predicate(column_id, column->name(), column->get_vec_type(),
549
83
                                           condition, _predicate_arena, predicate));
550
76
        if (predicate != nullptr) {
551
76
            delete_conditions->column_predicate_vec.push_back(predicate);
552
76
        }
553
76
    }
554
78
    return Status::OK();
555
85
}
556
557
Status DeleteHandler::_resolve_column(const ReadSchema& read_schema, int32_t col_unique_id,
558
                                      const std::string& column_name,
559
                                      const TabletSchemaSPtr& delete_pred_related_schema,
560
                                      ColumnId* column_id, const TabletColumn** column,
561
193
                                      std::vector<TabletColumn>* dropped_columns) {
562
    // A valid UID is the complete identity. A dropped column and a later
563
    // same-name replacement must remain distinct.
564
193
    if (col_unique_id >= 0) {
565
8
        int32_t ordinal = read_schema.ordinal_by_uid(col_unique_id);
566
8
        if (ordinal >= 0) {
567
5
            *column_id = ordinal;
568
5
            *column = read_schema.column(ordinal);
569
5
            return Status::OK();
570
5
        }
571
8
    }
572
573
188
    int32_t rowset_schema_ordinal = -1;
574
188
    if (col_unique_id >= 0) {
575
3
        rowset_schema_ordinal = delete_pred_related_schema->field_index(col_unique_id);
576
185
    } else {
577
185
        rowset_schema_ordinal = delete_pred_related_schema->field_index(column_name);
578
185
    }
579
188
    if (rowset_schema_ordinal < 0) {
580
0
        return Status::Error<ErrorCode::DELETE_INVALID_CONDITION>(
581
0
                "cannot find delete predicate column name={} unique_id={} in rowset schema",
582
0
                column_name, col_unique_id);
583
0
    }
584
585
188
    const auto& historical_column = delete_pred_related_schema->columns()[rowset_schema_ordinal];
586
188
    DORIS_CHECK_GE(historical_column->unique_id(), 0);
587
188
    int32_t ordinal = read_schema.ordinal_by_uid(historical_column->unique_id());
588
188
    if (ordinal >= 0) {
589
95
        *column_id = ordinal;
590
95
        *column = read_schema.column(ordinal);
591
95
        return Status::OK();
592
95
    }
593
594
93
    for (size_t i = 0; i < dropped_columns->size(); ++i) {
595
1
        const auto& dropped_column = (*dropped_columns)[i];
596
1
        if (dropped_column.unique_id() == historical_column->unique_id()) {
597
1
            *column_id = cast_set<ColumnId>(read_schema.num_read_columns() + i);
598
1
            *column = &dropped_column;
599
1
            return Status::OK();
600
1
        }
601
1
    }
602
603
92
    dropped_columns->emplace_back(*historical_column);
604
92
    *column_id = cast_set<ColumnId>(read_schema.num_read_columns() + dropped_columns->size() - 1);
605
92
    *column = &dropped_columns->back();
606
92
    return Status::OK();
607
93
}
608
609
Status DeleteHandler::init(const std::vector<RowsetMetaSharedPtr>& delete_preds, int64_t version,
610
                           const ReadSchemaSPtr& read_schema,
611
396
                           std::vector<TabletColumn>* dropped_columns) {
612
396
    DCHECK(!_is_inited) << "reinitialize delete handler.";
613
396
    DCHECK(version >= 0) << "invalid parameters. version=" << version;
614
396
    std::vector<TabletColumn> resolved_dropped_columns;
615
616
396
    for (const auto& delete_pred : delete_preds) {
617
        // Skip the delete condition with large version
618
176
        if (delete_pred->version().first > version) {
619
0
            continue;
620
0
        }
621
        // Resolve against the rowset schema that created this predicate. The current schema may
622
        // have dropped the column and added a same-name column with a different UID or type, while
623
        // legacy predicates without UIDs must still bind by name within this historical schema.
624
176
        const auto& delete_pred_related_schema = delete_pred->tablet_schema();
625
176
        const auto& delete_condition = delete_pred->delete_predicate();
626
176
        DeleteConditions temp;
627
176
        temp.filter_version = delete_pred->version().first;
628
176
        if (!delete_condition.sub_predicates_v2().empty()) {
629
91
            RETURN_IF_ERROR(_parse_column_pred(*read_schema, delete_pred_related_schema,
630
91
                                               delete_condition.sub_predicates_v2(), &temp,
631
91
                                               &resolved_dropped_columns));
632
91
        } else {
633
            // make it compatible with the former versions
634
85
            RETURN_IF_ERROR(_parse_column_pred(*read_schema, delete_pred_related_schema,
635
85
                                               delete_condition.sub_predicates(), &temp,
636
85
                                               &resolved_dropped_columns));
637
85
        }
638
169
        for (const auto& in_predicate : delete_condition.in_predicates()) {
639
4
            ConditionParseResult condition;
640
4
            condition.column_name = in_predicate.column_name();
641
642
4
            int32_t col_unique_id = -1;
643
4
            if (in_predicate.has_column_unique_id()) {
644
2
                col_unique_id = in_predicate.column_unique_id();
645
2
            }
646
647
4
            condition.condition_op =
648
4
                    in_predicate.is_not_in() ? PredicateType::NOT_IN_LIST : PredicateType::IN_LIST;
649
9
            for (const auto& value : in_predicate.values()) {
650
9
                condition.value_str.push_back(value);
651
9
            }
652
4
            ColumnId column_id;
653
4
            const TabletColumn* column;
654
4
            RETURN_IF_ERROR(_resolve_column(*read_schema, col_unique_id, condition.column_name,
655
4
                                            delete_pred_related_schema, &column_id, &column,
656
4
                                            &resolved_dropped_columns));
657
4
            condition.col_unique_id = column->unique_id();
658
4
            std::shared_ptr<ColumnPredicate> predicate;
659
4
            RETURN_IF_ERROR(parse_to_in_predicate(column_id, column->name(), column->get_vec_type(),
660
4
                                                  condition, _predicate_arena, predicate));
661
4
            temp.column_predicate_vec.push_back(predicate);
662
4
        }
663
664
169
        _del_conds.emplace_back(std::move(temp));
665
169
    }
666
667
389
    *dropped_columns = std::move(resolved_dropped_columns);
668
389
    _is_inited = true;
669
670
389
    return Status::OK();
671
396
}
672
673
490
DeleteHandler::~DeleteHandler() {
674
490
    if (!_is_inited) {
675
101
        return;
676
101
    }
677
678
389
    _del_conds.clear();
679
389
    _is_inited = false;
680
389
}
681
682
void DeleteHandler::get_delete_conditions_after_version(
683
        int64_t version, AndBlockColumnPredicate* and_block_column_predicate_ptr,
684
        std::unordered_map<int32_t, std::vector<std::shared_ptr<const ColumnPredicate>>>*
685
1.26k
                del_predicates_for_zone_map) const {
686
1.26k
    for (const auto& del_cond : _del_conds) {
687
562
        if (del_cond.filter_version > version) {
688
            // now, only query support delete column predicate operator
689
413
            if (!del_cond.column_predicate_vec.empty()) {
690
413
                if (del_cond.column_predicate_vec.size() == 1) {
691
411
                    auto single_column_block_predicate = SingleColumnBlockPredicate::create_unique(
692
411
                            del_cond.column_predicate_vec[0]);
693
411
                    and_block_column_predicate_ptr->add_column_predicate(
694
411
                            std::move(single_column_block_predicate));
695
411
                    if (del_predicates_for_zone_map->count(
696
411
                                del_cond.column_predicate_vec[0]->column_id()) < 1) {
697
411
                        del_predicates_for_zone_map->insert(
698
411
                                {del_cond.column_predicate_vec[0]->column_id(),
699
411
                                 std::vector<std::shared_ptr<const ColumnPredicate>> {}});
700
411
                    }
701
411
                    (*del_predicates_for_zone_map)[del_cond.column_predicate_vec[0]->column_id()]
702
411
                            .push_back(del_cond.column_predicate_vec[0]);
703
411
                } else {
704
2
                    auto or_column_predicate = OrBlockColumnPredicate::create_unique();
705
706
                    // build or_column_predicate
707
                    // when delete from where a = 1 and b = 2, we can not use del_predicates_for_zone_map to filter zone page,
708
                    // so here do not put predicate to del_predicates_for_zone_map,
709
                    // refer #17145 for more details.
710
                    // // TODO: need refactor design and code to use more version delete and more column delete to filter zone page.
711
2
                    std::for_each(del_cond.column_predicate_vec.cbegin(),
712
2
                                  del_cond.column_predicate_vec.cend(),
713
2
                                  [&or_column_predicate](
714
5
                                          const std::shared_ptr<const ColumnPredicate> predicate) {
715
5
                                      or_column_predicate->add_column_predicate(
716
5
                                              SingleColumnBlockPredicate::create_unique(predicate));
717
5
                                  });
718
2
                    and_block_column_predicate_ptr->add_column_predicate(
719
2
                            std::move(or_column_predicate));
720
2
                }
721
413
            }
722
413
        }
723
562
    }
724
1.26k
}
725
726
} // namespace doris