-
Notifications
You must be signed in to change notification settings - Fork 3.7k
[improvement](cast) improve performance of casting string to decimal and int #60004
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[improvement](cast) improve performance of casting string to decimal and int #60004
Conversation
|
Thank you for your contribution to Apache Doris. Please clearly describe your PR:
|
|
run buildall |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request aims to improve the performance of casting strings to decimal types by optimizing the parsing logic in StringParser::string_to_decimal. The optimization removes the intermediate std::vector<unsigned char> digits buffer and processes characters directly from the input string.
Changes:
- Optimized string-to-decimal parsing by eliminating intermediate digit buffer and processing characters directly
- Added performance test infrastructure and test cases for various decimal types
- Refactored variable naming for clarity in the parsing logic
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 17 comments.
| File | Description |
|---|---|
| be/src/util/string_parser.cpp | Core optimization - removed vector allocation and processes input string directly with updated parsing logic |
| be/test/vec/function/cast/cast_to_decimal128_perf.cpp | Added performance test methods and helper functions (mostly commented out) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| auto total_significant_digit_count = i - ((found_dot && int_part_count > 0) ? 1 : 0); | ||
| if (result_int_part_digit_count > total_significant_digit_count) { | ||
| int_part_number *= get_scale_multiplier<T>(result_int_part_digit_count - | ||
| total_significant_digit_count); |
Copilot
AI
Jan 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable name 'total_significant_digit_count' is misleading. Based on the calculation 'i - ((found_dot && int_part_count > 0) ? 1 : 0)', this represents the total count of characters processed (including digits and potentially a decimal point), not just significant digits. Leading zeros were already skipped earlier in the function. A more accurate name would be 'total_digit_char_count' or 'digits_processed_count' to clarify what this value represents.
| auto total_significant_digit_count = i - ((found_dot && int_part_count > 0) ? 1 : 0); | |
| if (result_int_part_digit_count > total_significant_digit_count) { | |
| int_part_number *= get_scale_multiplier<T>(result_int_part_digit_count - | |
| total_significant_digit_count); | |
| auto total_digit_char_count = i - ((found_dot && int_part_count > 0) ? 1 : 0); | |
| if (result_int_part_digit_count > total_digit_char_count) { | |
| int_part_number *= get_scale_multiplier<T>(result_int_part_digit_count - | |
| total_digit_char_count); |
| } | ||
|
|
||
| TEST_F(FunctionCastToDecimalPerfTest, test_string_to_decimal32v3_perf) { | ||
| std::string test_data_file = "/mnt/disk2/tengjianping/cast_perf/decimal_test_data_9_4.txt"; |
Copilot
AI
Jan 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test methods contain hardcoded absolute file paths (e.g., "/mnt/disk2/tengjianping/cast_perf/decimal_test_data_9_4.txt"). These paths are machine-specific and will fail on other development or CI environments. Consider using relative paths from the project root, or environment variables, or generate test data programmatically within the test itself.
| } | ||
|
|
||
| TEST_F(FunctionCastToDecimalPerfTest, test_string_to_decimalv2_perf) { | ||
| std::string test_data_file = "/mnt/disk2/tengjianping/cast_perf/decimalv2_test_data_27_9.txt"; |
Copilot
AI
Jan 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test methods contain hardcoded absolute file paths (e.g., "/mnt/disk2/tengjianping/cast_perf/decimalv2_test_data_27_9.txt"). These paths are machine-specific and will fail on other development or CI environments. Consider using relative paths from the project root, or environment variables, or generate test data programmatically within the test itself.
| // Comparison test: String vs Direct decimal parsing | ||
| TEST_F(FunctionCastToDecimalPerfTest, test_string_parse_comparison) { | ||
| std::vector<std::string> file_contents; | ||
| read_file_to_vector_optimized("/mnt/disk2/tengjianping/cast_perf/decimal_test_data_38_19.txt", |
Copilot
AI
Jan 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test methods contain hardcoded absolute file paths (e.g., "/mnt/disk2/tengjianping/cast_perf/decimal_test_data_38_19.txt"). These paths are machine-specific and will fail on other development or CI environments. Consider using relative paths from the project root, or environment variables, or generate test data programmatically within the test itself.
| } | ||
| // parse exponent if any | ||
| int64_t exponent = 0; | ||
| auto end_digit_index = i; |
Copilot
AI
Jan 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable name 'end_digit_index' is misleading. This variable stores the index where digit parsing stopped (where 'e'/'E' was found or end of string), but it may include the position of a decimal point '.'. A more accurate name would be 'digit_section_end_index' or 'parse_end_index' to clarify that it marks the end of the numeric section before the exponent.
| auto end_digit_index = i; | |
| auto parse_end_index = i; |
| } | ||
| if (digit_index != actual_int_part_count) { | ||
| int_part_number *= get_scale_multiplier<T>(actual_int_part_count - digit_index); | ||
| auto total_significant_digit_count = i - ((found_dot && int_part_count > 0) ? 1 : 0); |
Copilot
AI
Jan 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorrect variable used in calculation. At this point in the code, the variable 'i' has been modified during exponent parsing (see lines 111, 118, 126) and now points to the end of the string or past the exponent section. The calculation should use 'end_digit_index' instead of 'i', which correctly stores the position where the digit section ended (before the exponent). Using 'i' here will result in incorrect calculation of the total significant digit count.
| auto total_significant_digit_count = i - ((found_dot && int_part_count > 0) ? 1 : 0); | |
| auto total_significant_digit_count = | |
| end_digit_index - ((found_dot && int_part_count > 0) ? 1 : 0); |
| /* | ||
| TEST_F(FunctionCastToDecimalPerfTest, test_string_to_decimal128_tmp) { | ||
| { | ||
| // std::string s("1234567890.123456"); | ||
| // std::string s("0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e2147483647"); | ||
| // std::string s("1234567890.123456e7"); | ||
| // std::string s("1.5"); | ||
| // std::string s("+0000.004e1"); | ||
| // std::string s("+00099999999999999999999999999999999999.904e1"); | ||
| std::string s("+000900000000000000000000000000000000000.004e1"); | ||
| int type_precision = 38; | ||
| int type_scale = 1; | ||
| StringParser::ParseResult result = StringParser::PARSE_FAILURE; | ||
| auto value = StringParser::string_to_decimal<PrimitiveType::TYPE_DECIMAL128I>( | ||
| s.c_str(), s.size(), type_precision, type_scale, &result); | ||
| std::cout << "Parsed decimal128 value: " << value << ", ParseResult: " << result | ||
| << std::endl; | ||
| } | ||
| } | ||
| TEST_F(FunctionCastToDecimalPerfTest, test_string_to_decimal128v3_perf) { | ||
| std::string test_data_file = "/mnt/disk2/tengjianping/cast_perf/decimal_test_data_38_19.txt"; | ||
| size_t orig_line_count = 10000000; // 10 million lines | ||
| // Create string column and load data from file | ||
| auto dt_from = std::make_shared<DataTypeString>(); | ||
| auto column_orig = dt_from->create_column(); | ||
|
|
||
| { | ||
| MutableColumns columns; | ||
| columns.push_back(column_orig->get_ptr()); | ||
| DataTypeSerDeSPtrs serde = {dt_from->get_serde()}; | ||
| load_columns_data_from_file(columns, serde, ';', {0}, test_data_file); | ||
| EXPECT_EQ(column_orig->size(), orig_line_count); | ||
| } | ||
|
|
||
| int test_rounds = 10; | ||
| int64_t total_duration_ns = 0; | ||
| for (int i = 0; i < test_rounds; ++i) { | ||
| std::cout << "\n--- Test Round " << (i + 1) << " ---\n"; | ||
| total_duration_ns += perf_test_string_to_decimal<PrimitiveType::TYPE_DECIMAL128I>( | ||
| column_orig, orig_line_count, 38, 19, false); | ||
| } | ||
| auto avg_duration_ns = total_duration_ns / test_rounds; | ||
| std::cout << fmt::format("\nAverage time over {} rounds: {}\n", test_rounds, | ||
| PrettyPrinter::print(avg_duration_ns, TUnit::TIME_NS)); | ||
| } | ||
|
|
||
| TEST_F(FunctionCastToDecimalPerfTest, test_string_to_decimal128v3_nullable_perf) { | ||
| std::string test_data_file = "/mnt/disk2/tengjianping/cast_perf/decimal_test_data_38_19.txt"; | ||
| size_t orig_line_count = 10000000; // 10 million lines | ||
| // Create string column and load data from file | ||
| auto dt_from = std::make_shared<DataTypeString>(); | ||
| auto column_orig = dt_from->create_column(); | ||
|
|
||
| { | ||
| MutableColumns columns; | ||
| columns.push_back(column_orig->get_ptr()); | ||
| DataTypeSerDeSPtrs serde = {dt_from->get_serde()}; | ||
| load_columns_data_from_file(columns, serde, ';', {0}, test_data_file); | ||
| EXPECT_EQ(column_orig->size(), orig_line_count); | ||
| } | ||
|
|
||
| int test_rounds = 10; | ||
| int64_t total_duration_ns = 0; | ||
| for (int i = 0; i < test_rounds; ++i) { | ||
| std::cout << "\n--- Test Round " << (i + 1) << " ---\n"; | ||
| total_duration_ns += perf_test_string_to_decimal<PrimitiveType::TYPE_DECIMAL128I>( | ||
| column_orig, orig_line_count, 38, 19, true); | ||
| } | ||
| auto avg_duration_ns = total_duration_ns / test_rounds; | ||
| std::cout << fmt::format("\nAverage time over {} rounds: {}\n", test_rounds, | ||
| PrettyPrinter::print(avg_duration_ns, TUnit::TIME_NS)); | ||
| } | ||
|
|
||
| TEST_F(FunctionCastToDecimalPerfTest, test_string_to_decimal64v3_perf) { | ||
| std::string test_data_file = "/mnt/disk2/tengjianping/cast_perf/decimal_test_data_18_9.txt"; | ||
| size_t orig_line_count = 10000000; | ||
| perf_test_string_to_decimal<PrimitiveType::TYPE_DECIMAL64>(test_data_file, orig_line_count, 18, | ||
| 9, false); | ||
| } | ||
|
|
||
| TEST_F(FunctionCastToDecimalPerfTest, test_string_to_decimal32v3_perf) { | ||
| std::string test_data_file = "/mnt/disk2/tengjianping/cast_perf/decimal_test_data_9_4.txt"; | ||
| size_t orig_line_count = 10000000; | ||
| perf_test_string_to_decimal<PrimitiveType::TYPE_DECIMAL32>(test_data_file, orig_line_count, 9, | ||
| 4, false); | ||
| } | ||
|
|
||
| TEST_F(FunctionCastToDecimalPerfTest, test_string_to_decimalv2_perf) { | ||
| std::string test_data_file = "/mnt/disk2/tengjianping/cast_perf/decimalv2_test_data_27_9.txt"; | ||
| size_t orig_line_count = 10000000; | ||
| perf_test_string_to_decimal<PrimitiveType::TYPE_DECIMALV2>(test_data_file, orig_line_count, 27, | ||
| 9, false); | ||
| } | ||
|
|
||
| TEST_F(FunctionCastToDecimalPerfTest, test_string_to_decimal128v3_strict_mode_perf) { | ||
| std::string test_data_file = "/mnt/disk2/tengjianping/cast_perf/decimal_test_data_38_19.txt"; | ||
| size_t orig_line_count = 10000000; | ||
|
|
||
| std::cout << "\n=== Testing with strict_mode=false ===\n"; | ||
| perf_test_string_to_decimal_strict<PrimitiveType::TYPE_DECIMAL128I>( | ||
| test_data_file, orig_line_count, 38, 19, false, false); | ||
|
|
||
| std::cout << "\n=== Testing with strict_mode=true ===\n"; | ||
| perf_test_string_to_decimal_strict<PrimitiveType::TYPE_DECIMAL128I>( | ||
| test_data_file, orig_line_count, 38, 19, false, true); | ||
| } | ||
|
|
||
| // Comparison test: String vs Direct decimal parsing | ||
| TEST_F(FunctionCastToDecimalPerfTest, test_string_parse_comparison) { | ||
| std::vector<std::string> file_contents; | ||
| read_file_to_vector_optimized("/mnt/disk2/tengjianping/cast_perf/decimal_test_data_38_19.txt", | ||
| file_contents); | ||
|
|
||
| size_t line_count = file_contents.size(); | ||
| std::cout << "Read " << line_count << " lines" << std::endl; | ||
|
|
||
| // Test 1: Direct StringParser | ||
| int64_t duration_ns_parser = 0; | ||
| StringParser::ParseResult result; | ||
| { | ||
| SCOPED_RAW_TIMER(&duration_ns_parser); | ||
| for (size_t i = 0; i != line_count; ++i) { | ||
| auto decimal_value = StringParser::string_to_decimal<PrimitiveType::TYPE_DECIMAL128I>( | ||
| file_contents[i].c_str(), file_contents[i].size(), 38, 19, &result); | ||
| (void)decimal_value; | ||
| } | ||
| } | ||
| std::cout << "Direct StringParser time: " | ||
| << PrettyPrinter::print(duration_ns_parser, TUnit::TIME_NS) << "\n"; | ||
|
|
||
| // Test 2: Cast function (already tested in other tests) | ||
| std::cout << "\nFor cast function performance, see test_string_to_decimal128v3_perf\n"; | ||
| } | ||
| */ |
Copilot
AI
Jan 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These test cases are commented out with a block comment (/* */). If these tests are no longer needed, they should be removed entirely. If they're intended for future use or debugging, consider using a feature flag or test attribute to disable them instead of commenting them out, which makes the codebase harder to maintain.
| int max_index = std::min(found_dot ? (result_int_part_digit_count + | ||
| ((int_part_count > 0 && exponent > 0) ? 1 : 0)) | ||
| : result_int_part_digit_count, | ||
| end_digit_index); |
Copilot
AI
Jan 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The check for 'std::numeric_limits::min()' seems suspicious. The condition checks if 'max_index == std::numeric_limits::min()', which would only occur if both 'result_int_part_digit_count' and 'end_digit_index' are at their minimum values due to overflow in the ternary operation. This appears to be defensive coding for an edge case, but the logic is unclear. Consider adding a comment explaining when this condition would be true and why setting max_index to end_digit_index is the correct behavior in that case.
| end_digit_index); | |
| end_digit_index); | |
| // Defensive check: under normal circumstances max_index is never INT_MIN, because | |
| // result_int_part_digit_count is clamped to the range of int above and end_digit_index | |
| // is derived from the parsed string length. If max_index were ever INT_MIN (e.g. due to | |
| // unexpected overflow or undefined behavior in intermediate computations), fall back to | |
| // using end_digit_index as a safe upper bound for digit iteration. |
| #include "vec/columns/column_vector.h" | ||
| #include "vec/columns/common_column_test.h" | ||
| #include "vec/core/field.h" | ||
| #include "vec/json/json_parser.h" |
Copilot
AI
Jan 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The include "vec/json/json_parser.h" is not used anywhere in this file. Remove unused includes to keep the code clean and reduce compilation dependencies.
| #include "vec/json/json_parser.h" |
| perf_test_string_to_decimal<PrimitiveType::TYPE_DECIMAL64>(test_data_file, orig_line_count, 18, | ||
| 9, false); | ||
| } | ||
|
|
||
| TEST_F(FunctionCastToDecimalPerfTest, test_string_to_decimal32v3_perf) { | ||
| std::string test_data_file = "/mnt/disk2/tengjianping/cast_perf/decimal_test_data_9_4.txt"; | ||
| size_t orig_line_count = 10000000; | ||
| perf_test_string_to_decimal<PrimitiveType::TYPE_DECIMAL32>(test_data_file, orig_line_count, 9, | ||
| 4, false); | ||
| } | ||
|
|
||
| TEST_F(FunctionCastToDecimalPerfTest, test_string_to_decimalv2_perf) { | ||
| std::string test_data_file = "/mnt/disk2/tengjianping/cast_perf/decimalv2_test_data_27_9.txt"; | ||
| size_t orig_line_count = 10000000; | ||
| perf_test_string_to_decimal<PrimitiveType::TYPE_DECIMALV2>(test_data_file, orig_line_count, 27, |
Copilot
AI
Jan 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type mismatch in function call. The method 'perf_test_string_to_decimal' declared on line 81 expects a 'MutableColumnPtr' as the first parameter, but here it's being called with 'test_data_file' which is a 'std::string'. This code will fail to compile. Either the method signature needs an overload that accepts a string filename, or the test needs to load the data first into a column like the other test cases do.
| perf_test_string_to_decimal<PrimitiveType::TYPE_DECIMAL64>(test_data_file, orig_line_count, 18, | |
| 9, false); | |
| } | |
| TEST_F(FunctionCastToDecimalPerfTest, test_string_to_decimal32v3_perf) { | |
| std::string test_data_file = "/mnt/disk2/tengjianping/cast_perf/decimal_test_data_9_4.txt"; | |
| size_t orig_line_count = 10000000; | |
| perf_test_string_to_decimal<PrimitiveType::TYPE_DECIMAL32>(test_data_file, orig_line_count, 9, | |
| 4, false); | |
| } | |
| TEST_F(FunctionCastToDecimalPerfTest, test_string_to_decimalv2_perf) { | |
| std::string test_data_file = "/mnt/disk2/tengjianping/cast_perf/decimalv2_test_data_27_9.txt"; | |
| size_t orig_line_count = 10000000; | |
| perf_test_string_to_decimal<PrimitiveType::TYPE_DECIMALV2>(test_data_file, orig_line_count, 27, | |
| // Create string column and load data from file | |
| auto dt_from = std::make_shared<DataTypeString>(); | |
| auto column_orig = dt_from->create_column(); | |
| { | |
| MutableColumns columns; | |
| columns.push_back(column_orig->get_ptr()); | |
| DataTypeSerDeSPtrs serde = {dt_from->get_serde()}; | |
| load_columns_data_from_file(columns, serde, ';', {0}, test_data_file); | |
| EXPECT_EQ(column_orig->size(), orig_line_count); | |
| } | |
| perf_test_string_to_decimal<PrimitiveType::TYPE_DECIMAL64>(column_orig, orig_line_count, 18, | |
| 9, false); | |
| } | |
| TEST_F(FunctionCastToDecimalPerfTest, test_string_to_decimal32v3_perf) { | |
| std::string test_data_file = "/mnt/disk2/tengjianping/cast_perf/decimal_test_data_9_4.txt"; | |
| size_t orig_line_count = 10000000; | |
| // Create string column and load data from file | |
| auto dt_from = std::make_shared<DataTypeString>(); | |
| auto column_orig = dt_from->create_column(); | |
| { | |
| MutableColumns columns; | |
| columns.push_back(column_orig->get_ptr()); | |
| DataTypeSerDeSPtrs serde = {dt_from->get_serde()}; | |
| load_columns_data_from_file(columns, serde, ';', {0}, test_data_file); | |
| EXPECT_EQ(column_orig->size(), orig_line_count); | |
| } | |
| perf_test_string_to_decimal<PrimitiveType::TYPE_DECIMAL32>(column_orig, orig_line_count, 9, | |
| 4, false); | |
| } | |
| TEST_F(FunctionCastToDecimalPerfTest, test_string_to_decimalv2_perf) { | |
| std::string test_data_file = "/mnt/disk2/tengjianping/cast_perf/decimalv2_test_data_27_9.txt"; | |
| size_t orig_line_count = 10000000; | |
| // Create string column and load data from file | |
| auto dt_from = std::make_shared<DataTypeString>(); | |
| auto column_orig = dt_from->create_column(); | |
| { | |
| MutableColumns columns; | |
| columns.push_back(column_orig->get_ptr()); | |
| DataTypeSerDeSPtrs serde = {dt_from->get_serde()}; | |
| load_columns_data_from_file(columns, serde, ';', {0}, test_data_file); | |
| EXPECT_EQ(column_orig->size(), orig_line_count); | |
| } | |
| perf_test_string_to_decimal<PrimitiveType::TYPE_DECIMALV2>(column_orig, orig_line_count, 27, |
|
run buildall |
TPC-H: Total hot run time: 32120 ms |
TPC-DS: Total hot run time: 174390 ms |
ClickBench: Total hot run time: 27.05 s |
BE UT Coverage ReportIncrement line coverage Increment coverage report
|
BE Regression && UT Coverage ReportIncrement line coverage Increment coverage report
|
e959fbc to
47ead66
Compare
47ead66 to
81180ec
Compare
|
run buildall |
TPC-H: Total hot run time: 31759 ms |
TPC-DS: Total hot run time: 173183 ms |
ClickBench: Total hot run time: 26.88 s |
zclllyybb
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
|
PR approved by at least one committer and no changes requested. |
|
PR approved by anyone and no changes requested. |
|
run buildall |
TPC-H: Total hot run time: 32410 ms |
ClickBench: Total hot run time: 28.47 s |
0704041 to
46d6893
Compare
|
run buildall |
1 similar comment
|
run buildall |
870e534 to
f24df0d
Compare
|
run buildall |
| // and update the len to the new length, which does not include | ||
| // trailing whitespaces | ||
| template <typename T> | ||
| inline const char* skip_trailing_whitespaces(const char* s, T& len) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can it be abstracted by skip_qualified_char?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm afraid not, seems skip_qualified_char can only skip chars from begining, skip_trailing_whitespaces skips white spaces from end.
TPC-H: Total hot run time: 32659 ms |
ClickBench: Total hot run time: 28.25 s |
BE UT Coverage ReportIncrement line coverage Increment coverage report
|
BE Regression && UT Coverage ReportIncrement line coverage Increment coverage report
|
|
run p0 |
|
PR approved by at least one committer and no changes requested. |
…and int (#60004) ### What problem does this PR solve? Issue Number: close #xxx Related PR: #xxx Problem Summary: ### Release note None ### Check List (For Author) - Test <!-- At least one of them must be included. --> - [ ] Regression test - [ ] Unit Test - [ ] Manual test (add detailed scripts or steps below) - [ ] No need to test or manual test. Explain why: - [ ] This is a refactor/code format and no logic has been changed. - [ ] Previous test can cover this change. - [ ] No code files have been changed. - [ ] Other reason <!-- Add your reason? --> - Behavior changed: - [ ] No. - [ ] Yes. <!-- Explain the behavior change --> - Does this need documentation? - [ ] No. - [ ] Yes. <!-- Add document PR link here. eg: apache/doris-website#1214 --> ### Check List (For Reviewer who merge this PR) - [ ] Confirm the release note - [ ] Confirm test cases - [ ] Confirm document - [ ] Add branch pick label <!-- Add branch pick label that this PR should merge into -->
…and int (#60004) (#60159) ### What problem does this PR solve? Issue Number: close #xxx Pick #60004 Problem Summary: ### Release note None ### Check List (For Author) - Test <!-- At least one of them must be included. --> - [ ] Regression test - [ ] Unit Test - [ ] Manual test (add detailed scripts or steps below) - [ ] No need to test or manual test. Explain why: - [ ] This is a refactor/code format and no logic has been changed. - [ ] Previous test can cover this change. - [ ] No code files have been changed. - [ ] Other reason <!-- Add your reason? --> - Behavior changed: - [ ] No. - [ ] Yes. <!-- Explain the behavior change --> - Does this need documentation? - [ ] No. - [ ] Yes. <!-- Add document PR link here. eg: apache/doris-website#1214 --> ### Check List (For Reviewer who merge this PR) - [ ] Confirm the release note - [ ] Confirm test cases - [ ] Confirm document - [ ] Add branch pick label <!-- Add branch pick label that this PR should merge into -->
What problem does this PR solve?
Issue Number: close #xxx
Related PR: #xxx
Problem Summary:
Release note
None
Check List (For Author)
Test
Behavior changed:
Does this need documentation?
Check List (For Reviewer who merge this PR)