Coverage Report

Created: 2026-04-17 21:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/format/table/paimon_predicate_converter.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 "format/table/paimon_predicate_converter.h"
19
20
#include <algorithm>
21
#include <cctype>
22
#include <utility>
23
24
#include "core/column/column_const.h"
25
#include "core/column/column_nullable.h"
26
#include "core/data_type/data_type.h"
27
#include "core/data_type/data_type_nullable.h"
28
#include "core/field.h"
29
#include "core/types.h"
30
#include "core/value/decimalv2_value.h"
31
#include "core/value/timestamptz_value.h"
32
#include "core/value/vdatetime_value.h"
33
#include "exprs/vcompound_pred.h"
34
#include "exprs/vdirect_in_predicate.h"
35
#include "exprs/vectorized_fn_call.h"
36
#include "exprs/vexpr.h"
37
#include "exprs/vin_predicate.h"
38
#include "exprs/vliteral.h"
39
#include "exprs/vslot_ref.h"
40
#include "paimon/data/decimal.h"
41
#include "paimon/data/timestamp.h"
42
#include "paimon/predicate/predicate_builder.h"
43
#include "runtime/descriptors.h"
44
#include "runtime/runtime_state.h"
45
#include "util/timezone_utils.h"
46
47
namespace doris {
48
49
PaimonPredicateConverter::PaimonPredicateConverter(
50
        const std::vector<SlotDescriptor*>& file_slot_descs, RuntimeState* state)
51
0
        : _state(state) {
52
0
    _field_index_by_name.reserve(file_slot_descs.size());
53
0
    for (size_t i = 0; i < file_slot_descs.size(); ++i) {
54
0
        const auto& name = file_slot_descs[i]->col_name();
55
0
        auto normalized = _normalize_name(name);
56
0
        if (_field_index_by_name.find(normalized) == _field_index_by_name.end()) {
57
0
            _field_index_by_name.emplace(std::move(normalized), static_cast<int32_t>(i));
58
0
        }
59
0
    }
60
61
0
    if (!TimezoneUtils::find_cctz_time_zone("GMT", _gmt_tz)) {
62
0
        TimezoneUtils::find_cctz_time_zone(TimezoneUtils::default_time_zone, _gmt_tz);
63
0
    }
64
0
}
65
66
std::shared_ptr<paimon::Predicate> PaimonPredicateConverter::build(
67
0
        const VExprContextSPtrs& conjuncts) {
68
0
    std::vector<std::shared_ptr<paimon::Predicate>> predicates;
69
0
    predicates.reserve(conjuncts.size());
70
0
    for (const auto& conjunct : conjuncts) {
71
0
        if (!conjunct || !conjunct->root()) {
72
0
            continue;
73
0
        }
74
0
        auto root = conjunct->root();
75
0
        if (root->is_rf_wrapper()) {
76
0
            if (auto impl = root->get_impl()) {
77
0
                root = impl;
78
0
            }
79
0
        }
80
0
        auto predicate = _convert_expr(root);
81
0
        if (predicate) {
82
0
            predicates.emplace_back(std::move(predicate));
83
0
        }
84
0
    }
85
86
0
    if (predicates.empty()) {
87
0
        return nullptr;
88
0
    }
89
0
    if (predicates.size() == 1) {
90
0
        return predicates.front();
91
0
    }
92
0
    auto and_result = paimon::PredicateBuilder::And(predicates);
93
0
    if (!and_result.ok()) {
94
0
        return nullptr;
95
0
    }
96
0
    return std::move(and_result).value();
97
0
}
98
99
0
std::shared_ptr<paimon::Predicate> PaimonPredicateConverter::_convert_expr(const VExprSPtr& expr) {
100
0
    if (!expr) {
101
0
        return nullptr;
102
0
    }
103
104
0
    auto uncast = VExpr::expr_without_cast(expr);
105
106
0
    if (auto* direct_in = dynamic_cast<VDirectInPredicate*>(uncast.get())) {
107
0
        VExprSPtr in_expr;
108
0
        if (direct_in->get_slot_in_expr(in_expr)) {
109
0
            return _convert_in(in_expr);
110
0
        }
111
0
        return nullptr;
112
0
    }
113
114
0
    if (dynamic_cast<VInPredicate*>(uncast.get()) != nullptr) {
115
0
        return _convert_in(uncast);
116
0
    }
117
118
0
    switch (uncast->op()) {
119
0
    case TExprOpcode::COMPOUND_AND:
120
0
    case TExprOpcode::COMPOUND_OR:
121
0
        return _convert_compound(uncast);
122
0
    case TExprOpcode::COMPOUND_NOT:
123
0
        return nullptr;
124
0
    case TExprOpcode::EQ:
125
0
    case TExprOpcode::EQ_FOR_NULL:
126
0
    case TExprOpcode::NE:
127
0
    case TExprOpcode::GE:
128
0
    case TExprOpcode::GT:
129
0
    case TExprOpcode::LE:
130
0
    case TExprOpcode::LT:
131
0
        return _convert_binary(uncast);
132
0
    default:
133
0
        break;
134
0
    }
135
136
0
    if (auto* fn = dynamic_cast<VectorizedFnCall*>(uncast.get())) {
137
0
        auto fn_name = _normalize_name(fn->function_name());
138
0
        if (fn_name == "is_null_pred" || fn_name == "is_not_null_pred") {
139
0
            return _convert_is_null(uncast, fn_name);
140
0
        }
141
0
        if (fn_name == "like") {
142
0
            return _convert_like_prefix(uncast);
143
0
        }
144
0
    }
145
146
0
    return nullptr;
147
0
}
148
149
std::shared_ptr<paimon::Predicate> PaimonPredicateConverter::_convert_compound(
150
0
        const VExprSPtr& expr) {
151
0
    if (!expr || expr->get_num_children() != 2) {
152
0
        return nullptr;
153
0
    }
154
0
    auto left = _convert_expr(expr->get_child(0));
155
0
    if (!left) {
156
0
        return nullptr;
157
0
    }
158
0
    auto right = _convert_expr(expr->get_child(1));
159
0
    if (!right) {
160
0
        return nullptr;
161
0
    }
162
163
0
    if (expr->op() == TExprOpcode::COMPOUND_AND) {
164
0
        auto and_result = paimon::PredicateBuilder::And({left, right});
165
0
        return and_result.ok() ? std::move(and_result).value() : nullptr;
166
0
    }
167
0
    if (expr->op() == TExprOpcode::COMPOUND_OR) {
168
0
        auto or_result = paimon::PredicateBuilder::Or({left, right});
169
0
        return or_result.ok() ? std::move(or_result).value() : nullptr;
170
0
    }
171
0
    return nullptr;
172
0
}
173
174
0
std::shared_ptr<paimon::Predicate> PaimonPredicateConverter::_convert_in(const VExprSPtr& expr) {
175
0
    auto* in_pred = dynamic_cast<VInPredicate*>(expr.get());
176
0
    if (!in_pred || expr->get_num_children() < 2) {
177
0
        return nullptr;
178
0
    }
179
0
    auto field_meta = _resolve_field(expr->get_child(0));
180
0
    if (!field_meta) {
181
0
        return nullptr;
182
0
    }
183
184
0
    std::vector<paimon::Literal> literals;
185
0
    literals.reserve(expr->get_num_children() - 1);
186
0
    for (uint16_t i = 1; i < expr->get_num_children(); ++i) {
187
0
        auto literal = _convert_literal(expr->get_child(i), *field_meta->slot_desc,
188
0
                                        field_meta->field_type);
189
0
        if (!literal) {
190
0
            return nullptr;
191
0
        }
192
0
        literals.emplace_back(std::move(*literal));
193
0
    }
194
195
0
    if (literals.empty()) {
196
0
        return nullptr;
197
0
    }
198
0
    if (in_pred->is_not_in()) {
199
0
        return paimon::PredicateBuilder::NotIn(field_meta->index, field_meta->slot_desc->col_name(),
200
0
                                               field_meta->field_type, literals);
201
0
    }
202
0
    return paimon::PredicateBuilder::In(field_meta->index, field_meta->slot_desc->col_name(),
203
0
                                        field_meta->field_type, literals);
204
0
}
205
206
std::shared_ptr<paimon::Predicate> PaimonPredicateConverter::_convert_binary(
207
0
        const VExprSPtr& expr) {
208
0
    if (!expr || expr->get_num_children() != 2) {
209
0
        return nullptr;
210
0
    }
211
0
    auto field_meta = _resolve_field(expr->get_child(0));
212
0
    if (!field_meta) {
213
0
        return nullptr;
214
0
    }
215
216
0
    if (expr->op() == TExprOpcode::EQ_FOR_NULL) {
217
0
        return paimon::PredicateBuilder::IsNull(
218
0
                field_meta->index, field_meta->slot_desc->col_name(), field_meta->field_type);
219
0
    }
220
221
0
    auto literal =
222
0
            _convert_literal(expr->get_child(1), *field_meta->slot_desc, field_meta->field_type);
223
0
    if (!literal) {
224
0
        return nullptr;
225
0
    }
226
227
0
    switch (expr->op()) {
228
0
    case TExprOpcode::EQ:
229
0
        return paimon::PredicateBuilder::Equal(field_meta->index, field_meta->slot_desc->col_name(),
230
0
                                               field_meta->field_type, *literal);
231
0
    case TExprOpcode::NE:
232
0
        return paimon::PredicateBuilder::NotEqual(field_meta->index,
233
0
                                                  field_meta->slot_desc->col_name(),
234
0
                                                  field_meta->field_type, *literal);
235
0
    case TExprOpcode::GE:
236
0
        return paimon::PredicateBuilder::GreaterOrEqual(field_meta->index,
237
0
                                                        field_meta->slot_desc->col_name(),
238
0
                                                        field_meta->field_type, *literal);
239
0
    case TExprOpcode::GT:
240
0
        return paimon::PredicateBuilder::GreaterThan(field_meta->index,
241
0
                                                     field_meta->slot_desc->col_name(),
242
0
                                                     field_meta->field_type, *literal);
243
0
    case TExprOpcode::LE:
244
0
        return paimon::PredicateBuilder::LessOrEqual(field_meta->index,
245
0
                                                     field_meta->slot_desc->col_name(),
246
0
                                                     field_meta->field_type, *literal);
247
0
    case TExprOpcode::LT:
248
0
        return paimon::PredicateBuilder::LessThan(field_meta->index,
249
0
                                                  field_meta->slot_desc->col_name(),
250
0
                                                  field_meta->field_type, *literal);
251
0
    default:
252
0
        break;
253
0
    }
254
0
    return nullptr;
255
0
}
256
257
std::shared_ptr<paimon::Predicate> PaimonPredicateConverter::_convert_is_null(
258
0
        const VExprSPtr& expr, const std::string& fn_name) {
259
0
    if (!expr || expr->get_num_children() != 1) {
260
0
        return nullptr;
261
0
    }
262
0
    auto field_meta = _resolve_field(expr->get_child(0));
263
0
    if (!field_meta) {
264
0
        return nullptr;
265
0
    }
266
0
    if (fn_name == "is_not_null_pred") {
267
0
        return paimon::PredicateBuilder::IsNotNull(
268
0
                field_meta->index, field_meta->slot_desc->col_name(), field_meta->field_type);
269
0
    }
270
0
    return paimon::PredicateBuilder::IsNull(field_meta->index, field_meta->slot_desc->col_name(),
271
0
                                            field_meta->field_type);
272
0
}
273
274
std::shared_ptr<paimon::Predicate> PaimonPredicateConverter::_convert_like_prefix(
275
0
        const VExprSPtr& expr) {
276
0
    if (!expr || expr->get_num_children() != 2) {
277
0
        return nullptr;
278
0
    }
279
0
    auto field_meta = _resolve_field(expr->get_child(0));
280
0
    if (!field_meta || field_meta->field_type != paimon::FieldType::STRING) {
281
0
        return nullptr;
282
0
    }
283
284
0
    auto pattern_opt = _extract_string_literal(expr->get_child(1));
285
0
    if (!pattern_opt) {
286
0
        return nullptr;
287
0
    }
288
0
    const std::string& pattern = *pattern_opt;
289
0
    if (!pattern.empty() && pattern.front() == '%') {
290
0
        return nullptr;
291
0
    }
292
0
    if (pattern.empty() || pattern.back() != '%') {
293
0
        return nullptr;
294
0
    }
295
296
0
    std::string prefix = pattern.substr(0, pattern.size() - 1);
297
0
    paimon::Literal lower_literal(paimon::FieldType::STRING, prefix.data(), prefix.size());
298
0
    auto lower_pred = paimon::PredicateBuilder::GreaterOrEqual(
299
0
            field_meta->index, field_meta->slot_desc->col_name(), field_meta->field_type,
300
0
            lower_literal);
301
302
0
    auto upper_prefix = _next_prefix(prefix);
303
0
    if (!upper_prefix) {
304
0
        return lower_pred;
305
0
    }
306
307
0
    paimon::Literal upper_literal(paimon::FieldType::STRING, upper_prefix->data(),
308
0
                                  upper_prefix->size());
309
0
    auto upper_pred =
310
0
            paimon::PredicateBuilder::LessThan(field_meta->index, field_meta->slot_desc->col_name(),
311
0
                                               field_meta->field_type, upper_literal);
312
0
    auto and_result = paimon::PredicateBuilder::And({lower_pred, upper_pred});
313
0
    return and_result.ok() ? std::move(and_result).value() : nullptr;
314
0
}
315
316
std::optional<PaimonPredicateConverter::FieldMeta> PaimonPredicateConverter::_resolve_field(
317
0
        const VExprSPtr& expr) const {
318
0
    if (!_state || !expr) {
319
0
        return std::nullopt;
320
0
    }
321
0
    auto slot_expr = VExpr::expr_without_cast(expr);
322
0
    auto* slot_ref = dynamic_cast<VSlotRef*>(slot_expr.get());
323
0
    if (!slot_ref) {
324
0
        return std::nullopt;
325
0
    }
326
0
    auto* slot_desc = _state->desc_tbl().get_slot_descriptor(slot_ref->slot_id());
327
0
    if (!slot_desc) {
328
0
        return std::nullopt;
329
0
    }
330
0
    auto normalized = _normalize_name(slot_desc->col_name());
331
0
    auto it = _field_index_by_name.find(normalized);
332
0
    if (it == _field_index_by_name.end()) {
333
0
        return std::nullopt;
334
0
    }
335
0
    auto slot_type = slot_desc->type();
336
0
    auto field_type =
337
0
            _to_paimon_field_type(slot_type->get_primitive_type(), slot_type->get_precision());
338
0
    if (!field_type) {
339
0
        return std::nullopt;
340
0
    }
341
0
    return FieldMeta {it->second, *field_type, slot_desc};
342
0
}
343
344
std::optional<paimon::Literal> PaimonPredicateConverter::_convert_literal(
345
        const VExprSPtr& expr, const SlotDescriptor& slot_desc,
346
0
        paimon::FieldType field_type) const {
347
0
    auto literal_expr = VExpr::expr_without_cast(expr);
348
0
    auto* literal = dynamic_cast<VLiteral*>(literal_expr.get());
349
0
    if (!literal) {
350
0
        return std::nullopt;
351
0
    }
352
353
0
    auto literal_type = remove_nullable(literal->get_data_type());
354
0
    PrimitiveType literal_primitive = literal_type->get_primitive_type();
355
0
    PrimitiveType slot_primitive = slot_desc.type()->get_primitive_type();
356
357
0
    ColumnPtr col = literal->get_column_ptr()->convert_to_full_column_if_const();
358
0
    if (const auto* nullable = check_and_get_column<ColumnNullable>(*col)) {
359
0
        if (nullable->is_null_at(0)) {
360
0
            return std::nullopt;
361
0
        }
362
0
        col = nullable->get_nested_column_ptr();
363
0
    }
364
365
0
    Field field;
366
0
    col->get(0, field);
367
368
0
    switch (slot_primitive) {
369
0
    case TYPE_BOOLEAN: {
370
0
        if (literal_primitive != TYPE_BOOLEAN) {
371
0
            return std::nullopt;
372
0
        }
373
0
        return paimon::Literal(static_cast<bool>(field.get<TYPE_BOOLEAN>()));
374
0
    }
375
0
    case TYPE_TINYINT:
376
0
    case TYPE_SMALLINT:
377
0
    case TYPE_INT:
378
0
    case TYPE_BIGINT: {
379
0
        if (!_is_integer_type(literal_primitive)) {
380
0
            return std::nullopt;
381
0
        }
382
0
        int64_t value = 0;
383
0
        switch (literal_primitive) {
384
0
        case TYPE_TINYINT:
385
0
            value = field.get<TYPE_TINYINT>();
386
0
            break;
387
0
        case TYPE_SMALLINT:
388
0
            value = field.get<TYPE_SMALLINT>();
389
0
            break;
390
0
        case TYPE_INT:
391
0
            value = field.get<TYPE_INT>();
392
0
            break;
393
0
        case TYPE_BIGINT:
394
0
            value = field.get<TYPE_BIGINT>();
395
0
            break;
396
0
        default:
397
0
            return std::nullopt;
398
0
        }
399
0
        if (slot_primitive == TYPE_TINYINT) {
400
0
            return paimon::Literal(static_cast<int8_t>(value));
401
0
        }
402
0
        if (slot_primitive == TYPE_SMALLINT) {
403
0
            return paimon::Literal(static_cast<int16_t>(value));
404
0
        }
405
0
        if (slot_primitive == TYPE_INT) {
406
0
            return paimon::Literal(static_cast<int32_t>(value));
407
0
        }
408
0
        return paimon::Literal(static_cast<int64_t>(value));
409
0
    }
410
0
    case TYPE_DOUBLE: {
411
0
        if (literal_primitive != TYPE_DOUBLE && literal_primitive != TYPE_FLOAT) {
412
0
            return std::nullopt;
413
0
        }
414
0
        double value = 0;
415
0
        if (literal_primitive == TYPE_FLOAT) {
416
0
            value = static_cast<double>(field.get<TYPE_FLOAT>());
417
0
        } else {
418
0
            value = field.get<TYPE_DOUBLE>();
419
0
        }
420
0
        return paimon::Literal(value);
421
0
    }
422
0
    case TYPE_DATE:
423
0
    case TYPE_DATEV2: {
424
0
        if (!_is_date_type(literal_primitive)) {
425
0
            return std::nullopt;
426
0
        }
427
0
        int64_t seconds = 0;
428
0
        if (literal_primitive == TYPE_DATE) {
429
0
            const auto& dt = field.get<TYPE_DATE>();
430
0
            if (!dt.is_valid_date()) {
431
0
                return std::nullopt;
432
0
            }
433
0
            dt.unix_timestamp(&seconds, _gmt_tz);
434
0
        } else if (literal_primitive == TYPE_DATEV2) {
435
0
            const auto& dt = field.get<TYPE_DATEV2>();
436
0
            if (!dt.is_valid_date()) {
437
0
                return std::nullopt;
438
0
            }
439
0
            dt.unix_timestamp(&seconds, _gmt_tz);
440
0
        }
441
0
        int32_t days = _seconds_to_days(seconds);
442
0
        return paimon::Literal(paimon::FieldType::DATE, days);
443
0
    }
444
0
    case TYPE_DATETIME:
445
0
    case TYPE_DATETIMEV2: {
446
0
        if (!_is_datetime_type(literal_primitive)) {
447
0
            return std::nullopt;
448
0
        }
449
0
        if (literal_primitive == TYPE_DATETIME) {
450
0
            const auto& dt = field.get<TYPE_DATETIME>();
451
0
            if (!dt.is_valid_date()) {
452
0
                return std::nullopt;
453
0
            }
454
0
            int64_t seconds = 0;
455
0
            dt.unix_timestamp(&seconds, _gmt_tz);
456
0
            return paimon::Literal(paimon::Timestamp::FromEpochMillis(seconds * 1000));
457
0
        }
458
0
        std::pair<int64_t, int64_t> ts;
459
0
        const auto& dt = field.get<TYPE_DATETIMEV2>();
460
0
        if (!dt.is_valid_date()) {
461
0
            return std::nullopt;
462
0
        }
463
0
        dt.unix_timestamp(&ts, _gmt_tz);
464
0
        int64_t millis = ts.first * 1000 + ts.second / 1000;
465
0
        return paimon::Literal(paimon::Timestamp::FromEpochMillis(millis));
466
0
    }
467
0
    case TYPE_VARCHAR:
468
0
    case TYPE_STRING: {
469
0
        if (!_is_string_type(literal_primitive)) {
470
0
            return std::nullopt;
471
0
        }
472
0
        const auto& value = field.get<TYPE_STRING>();
473
0
        return paimon::Literal(field_type, value.data(), value.size());
474
0
    }
475
0
    case TYPE_DECIMALV2:
476
0
    case TYPE_DECIMAL32:
477
0
    case TYPE_DECIMAL64:
478
0
    case TYPE_DECIMAL128I:
479
0
    case TYPE_DECIMAL256: {
480
0
        if (!_is_decimal_type(literal_primitive)) {
481
0
            return std::nullopt;
482
0
        }
483
0
        int32_t precision = static_cast<int32_t>(literal_type->get_precision());
484
0
        int32_t scale = static_cast<int32_t>(literal_type->get_scale());
485
0
        if (precision <= 0 || precision > paimon::Decimal::MAX_PRECISION) {
486
0
            return std::nullopt;
487
0
        }
488
489
0
        paimon::Decimal::int128_t value = 0;
490
0
        switch (literal_primitive) {
491
0
        case TYPE_DECIMALV2: {
492
0
            const auto& dec = field.get<TYPE_DECIMALV2>();
493
0
            value = dec.value();
494
0
            break;
495
0
        }
496
0
        case TYPE_DECIMAL32: {
497
0
            const auto& dec = field.get<TYPE_DECIMAL32>();
498
0
            value = dec.value;
499
0
            break;
500
0
        }
501
0
        case TYPE_DECIMAL64: {
502
0
            const auto& dec = field.get<TYPE_DECIMAL64>();
503
0
            value = dec.value;
504
0
            break;
505
0
        }
506
0
        case TYPE_DECIMAL128I: {
507
0
            const auto& dec = field.get<TYPE_DECIMAL128I>();
508
0
            value = dec.value;
509
0
            break;
510
0
        }
511
0
        default:
512
0
            return std::nullopt;
513
0
        }
514
0
        return paimon::Literal(paimon::Decimal(precision, scale, value));
515
0
    }
516
0
    default:
517
0
        break;
518
0
    }
519
0
    return std::nullopt;
520
0
}
521
522
std::optional<std::string> PaimonPredicateConverter::_extract_string_literal(
523
0
        const VExprSPtr& expr) const {
524
0
    auto literal_expr = VExpr::expr_without_cast(expr);
525
0
    auto* literal = dynamic_cast<VLiteral*>(literal_expr.get());
526
0
    if (!literal) {
527
0
        return std::nullopt;
528
0
    }
529
0
    auto literal_type = remove_nullable(literal->get_data_type());
530
0
    PrimitiveType literal_primitive = literal_type->get_primitive_type();
531
0
    if (!_is_string_type(literal_primitive)) {
532
0
        return std::nullopt;
533
0
    }
534
535
0
    ColumnPtr col = literal->get_column_ptr()->convert_to_full_column_if_const();
536
0
    if (const auto* nullable = check_and_get_column<ColumnNullable>(*col)) {
537
0
        if (nullable->is_null_at(0)) {
538
0
            return std::nullopt;
539
0
        }
540
0
        col = nullable->get_nested_column_ptr();
541
0
    }
542
0
    Field field;
543
0
    col->get(0, field);
544
0
    const auto& value = field.get<TYPE_STRING>();
545
0
    return value;
546
0
}
547
548
0
std::string PaimonPredicateConverter::_normalize_name(std::string_view name) {
549
0
    std::string out(name);
550
0
    std::transform(out.begin(), out.end(), out.begin(),
551
0
                   [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
552
0
    return out;
553
0
}
554
555
0
std::optional<std::string> PaimonPredicateConverter::_next_prefix(const std::string& prefix) {
556
0
    if (prefix.empty()) {
557
0
        return std::nullopt;
558
0
    }
559
0
    std::string upper = prefix;
560
0
    for (int i = static_cast<int>(upper.size()) - 1; i >= 0; --i) {
561
0
        auto c = static_cast<unsigned char>(upper[i]);
562
0
        if (c != 0xFF) {
563
0
            upper[i] = static_cast<char>(c + 1);
564
0
            upper.resize(i + 1);
565
0
            return upper;
566
0
        }
567
0
    }
568
0
    return std::nullopt;
569
0
}
570
571
0
int32_t PaimonPredicateConverter::_seconds_to_days(int64_t seconds) {
572
0
    static constexpr int64_t kSecondsPerDay = 24 * 60 * 60;
573
0
    int64_t days = seconds / kSecondsPerDay;
574
0
    if (seconds < 0 && seconds % kSecondsPerDay != 0) {
575
0
        --days;
576
0
    }
577
0
    return static_cast<int32_t>(days);
578
0
}
579
580
0
bool PaimonPredicateConverter::_is_integer_type(PrimitiveType type) {
581
0
    switch (type) {
582
0
    case TYPE_TINYINT:
583
0
    case TYPE_SMALLINT:
584
0
    case TYPE_INT:
585
0
    case TYPE_BIGINT:
586
0
        return true;
587
0
    default:
588
0
        return false;
589
0
    }
590
0
}
591
592
0
bool PaimonPredicateConverter::_is_string_type(PrimitiveType type) {
593
0
    return type == TYPE_CHAR || type == TYPE_VARCHAR || type == TYPE_STRING;
594
0
}
595
596
0
bool PaimonPredicateConverter::_is_decimal_type(PrimitiveType type) {
597
0
    switch (type) {
598
0
    case TYPE_DECIMALV2:
599
0
    case TYPE_DECIMAL32:
600
0
    case TYPE_DECIMAL64:
601
0
    case TYPE_DECIMAL128I:
602
0
    case TYPE_DECIMAL256:
603
0
        return true;
604
0
    default:
605
0
        return false;
606
0
    }
607
0
}
608
609
0
bool PaimonPredicateConverter::_is_date_type(PrimitiveType type) {
610
0
    return type == TYPE_DATE || type == TYPE_DATEV2;
611
0
}
612
613
0
bool PaimonPredicateConverter::_is_datetime_type(PrimitiveType type) {
614
0
    return type == TYPE_DATETIME || type == TYPE_DATETIMEV2;
615
0
}
616
617
std::optional<paimon::FieldType> PaimonPredicateConverter::_to_paimon_field_type(
618
0
        PrimitiveType type, uint32_t precision) {
619
0
    switch (type) {
620
0
    case TYPE_BOOLEAN:
621
0
        return paimon::FieldType::BOOLEAN;
622
0
    case TYPE_TINYINT:
623
0
        return paimon::FieldType::TINYINT;
624
0
    case TYPE_SMALLINT:
625
0
        return paimon::FieldType::SMALLINT;
626
0
    case TYPE_INT:
627
0
        return paimon::FieldType::INT;
628
0
    case TYPE_BIGINT:
629
0
        return paimon::FieldType::BIGINT;
630
0
    case TYPE_DOUBLE:
631
0
        return paimon::FieldType::DOUBLE;
632
0
    case TYPE_VARCHAR:
633
0
    case TYPE_STRING:
634
0
        return paimon::FieldType::STRING;
635
0
    case TYPE_DATE:
636
0
    case TYPE_DATEV2:
637
0
        return paimon::FieldType::DATE;
638
0
    case TYPE_DATETIME:
639
0
    case TYPE_DATETIMEV2:
640
0
        return paimon::FieldType::TIMESTAMP;
641
0
    case TYPE_DECIMALV2:
642
0
    case TYPE_DECIMAL32:
643
0
    case TYPE_DECIMAL64:
644
0
    case TYPE_DECIMAL128I:
645
0
    case TYPE_DECIMAL256:
646
0
        if (precision > 0 && precision > paimon::Decimal::MAX_PRECISION) {
647
0
            return std::nullopt;
648
0
        }
649
0
        return paimon::FieldType::DECIMAL;
650
0
    case TYPE_FLOAT:
651
0
    case TYPE_CHAR:
652
0
    default:
653
0
        return std::nullopt;
654
0
    }
655
0
}
656
657
} // namespace doris