Coverage Report

Created: 2026-07-07 22:17

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