|
59 | 59 | #include "vec/functions/datetime_errors.h" |
60 | 60 | #include "vec/functions/function.h" |
61 | 61 | #include "vec/functions/function_helpers.h" |
| 62 | +#include "vec/functions/function_needs_to_handle_null.h" |
62 | 63 | #include "vec/runtime/time_value.h" |
63 | 64 | #include "vec/runtime/vdatetime_value.h" |
64 | 65 | #include "vec/utils/util.hpp" |
@@ -1418,5 +1419,205 @@ class FunctionTime : public IFunction { |
1418 | 1419 | return Status::OK(); |
1419 | 1420 | } |
1420 | 1421 | }; |
| 1422 | + |
| 1423 | +class FunctionGetFormat : public IFunction { |
| 1424 | +public: |
| 1425 | + static constexpr auto name = "get_format"; |
| 1426 | + static FunctionPtr create() { return std::make_shared<FunctionGetFormat>(); } |
| 1427 | + String get_name() const override { return name; } |
| 1428 | + size_t get_number_of_arguments() const override { return 2; } |
| 1429 | + DataTypePtr get_return_type_impl(const DataTypes& arguments) const override { |
| 1430 | + return make_nullable(std::make_shared<DataTypeString>()); |
| 1431 | + } |
| 1432 | + |
| 1433 | + Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments, |
| 1434 | + uint32_t result, size_t input_rows_count) const override { |
| 1435 | + const auto& [left_col_ptr, left_is_const] = |
| 1436 | + unpack_if_const(block.get_by_position(arguments[0]).column); |
| 1437 | + const auto& [right_col_ptr, right_is_const] = |
| 1438 | + unpack_if_const(block.get_by_position(arguments[1]).column); |
| 1439 | + |
| 1440 | + const auto* left_col = assert_cast<const ColumnString*>(left_col_ptr.get()); |
| 1441 | + const auto* right_col = assert_cast<const ColumnString*>(right_col_ptr.get()); |
| 1442 | + |
| 1443 | + auto type_ref = left_col->get_data_at(0); |
| 1444 | + std::string type_str(type_ref.data, type_ref.size); |
| 1445 | + |
| 1446 | + auto res_col = ColumnString::create(); |
| 1447 | + auto res_null_map = ColumnUInt8::create(input_rows_count, 0); |
| 1448 | + auto& res_data = res_col->get_chars(); |
| 1449 | + auto& res_offsets = res_col->get_offsets(); |
| 1450 | + |
| 1451 | + if (type_str == DATE_NAME) { |
| 1452 | + execute_format_type<DateFormatImpl>(res_data, res_offsets, res_null_map->get_data(), |
| 1453 | + input_rows_count, right_col); |
| 1454 | + } else if (type_str == DATETIME_NAME) { |
| 1455 | + execute_format_type<DateTimeFormatImpl>(res_data, res_offsets, res_null_map->get_data(), |
| 1456 | + input_rows_count, right_col); |
| 1457 | + } else if (type_str == TIME_NAME) { |
| 1458 | + execute_format_type<TimeFormatImpl>(res_data, res_offsets, res_null_map->get_data(), |
| 1459 | + input_rows_count, right_col); |
| 1460 | + } else { |
| 1461 | + return Status::InvalidArgument( |
| 1462 | + "Function GET_FORMAT only support DATE, DATETIME or TIME"); |
| 1463 | + } |
| 1464 | + |
| 1465 | + block.replace_by_position( |
| 1466 | + result, ColumnNullable::create(std::move(res_col), std::move(res_null_map))); |
| 1467 | + return Status::OK(); |
| 1468 | + } |
| 1469 | + |
| 1470 | +private: |
| 1471 | + template <typename Impl> |
| 1472 | + static void execute_format_type(ColumnString::Chars& res_data, |
| 1473 | + ColumnString::Offsets& res_offsets, |
| 1474 | + PaddedPODArray<UInt8>& res_null_map, size_t input_rows_count, |
| 1475 | + const ColumnString* right_col) { |
| 1476 | + res_data.reserve(input_rows_count * Impl::ESTIMATE_SIZE); |
| 1477 | + res_offsets.reserve(input_rows_count); |
| 1478 | + |
| 1479 | + for (int i = 0; i < input_rows_count; ++i) { |
| 1480 | + StringRef format_ref = right_col->get_data_at(i); |
| 1481 | + std::string format_str(format_ref.data, format_ref.size); |
| 1482 | + std::transform(format_str.begin(), format_str.end(), format_str.begin(), ::toupper); |
| 1483 | + |
| 1484 | + std::string_view format_res; |
| 1485 | + if (format_str == "USA") { |
| 1486 | + format_res = Impl::USA; |
| 1487 | + } else if (format_str == "JIS" || format_str == "ISO") { |
| 1488 | + format_res = Impl::JIS_ISO; |
| 1489 | + } else if (format_str == "EUR") { |
| 1490 | + format_res = Impl::EUR; |
| 1491 | + } else if (format_str == "INTERNAL") { |
| 1492 | + format_res = Impl::INTERNAL; |
| 1493 | + } else { |
| 1494 | + res_null_map[i] = 1; |
| 1495 | + res_offsets.push_back(res_data.size()); |
| 1496 | + continue; |
| 1497 | + } |
| 1498 | + |
| 1499 | + res_data.insert(format_res.data(), format_res.data() + format_res.size()); |
| 1500 | + res_offsets.push_back(res_data.size()); |
| 1501 | + } |
| 1502 | + } |
| 1503 | + |
| 1504 | + struct DateFormatImpl { |
| 1505 | + static constexpr auto USA = "%m.%d.%Y"; |
| 1506 | + static constexpr auto JIS_ISO = "%Y-%m-%d"; |
| 1507 | + static constexpr auto EUR = "%d.%m.%Y"; |
| 1508 | + static constexpr auto INTERNAL = "%Y%m%d"; |
| 1509 | + static constexpr size_t ESTIMATE_SIZE = 8; |
| 1510 | + }; |
| 1511 | + |
| 1512 | + struct DateTimeFormatImpl { |
| 1513 | + static constexpr auto USA = "%Y-%m-%d %H.%i.%s"; |
| 1514 | + static constexpr auto JIS_ISO = "%Y-%m-%d %H:%i:%s"; |
| 1515 | + static constexpr auto EUR = "%Y-%m-%d %H.%i.%s"; |
| 1516 | + static constexpr auto INTERNAL = "%Y%m%d%H%i%s"; |
| 1517 | + static constexpr size_t ESTIMATE_SIZE = 17; |
| 1518 | + }; |
| 1519 | + |
| 1520 | + struct TimeFormatImpl { |
| 1521 | + static constexpr auto USA = "%h:%i:%s %p"; |
| 1522 | + static constexpr auto JIS_ISO = "%H:%i:%s"; |
| 1523 | + static constexpr auto EUR = "%H.%i.%s"; |
| 1524 | + static constexpr auto INTERNAL = "%H%i%s"; |
| 1525 | + static constexpr size_t ESTIMATE_SIZE = 11; |
| 1526 | + }; |
| 1527 | + |
| 1528 | + static constexpr auto DATE_NAME = "DATE"; |
| 1529 | + static constexpr auto DATETIME_NAME = "DATETIME"; |
| 1530 | + static constexpr auto TIME_NAME = "TIME"; |
| 1531 | +}; |
| 1532 | + |
| 1533 | +class PeriodHelper { |
| 1534 | +public: |
| 1535 | + // For two digit year, 70-99 -> 1970-1999, 00-69 -> 2000-2069 |
| 1536 | + // this rule is same as MySQL |
| 1537 | + static constexpr int YY_PART_YEAR = 70; |
| 1538 | + static Status valid_period(int64_t period) { |
| 1539 | + if (period <= 0 || (period % 100) == 0 || (period % 100) > 12) { |
| 1540 | + return Status::InvalidArgument("Period function got invalid period: {}", period); |
| 1541 | + } |
| 1542 | + return Status::OK(); |
| 1543 | + } |
| 1544 | + |
| 1545 | + static int64_t check_and_convert_period_to_month(uint64_t period) { |
| 1546 | + THROW_IF_ERROR(valid_period(period)); |
| 1547 | + uint64_t year = period / 100; |
| 1548 | + if (year < 100) { |
| 1549 | + year += (year >= YY_PART_YEAR) ? 1900 : 2000; |
| 1550 | + } |
| 1551 | + return year * 12LL + (period % 100) - 1; |
| 1552 | + } |
| 1553 | + |
| 1554 | + static int64_t convert_month_to_period(uint64_t month) { |
| 1555 | + uint64_t year = month / 12; |
| 1556 | + if (year < 100) { |
| 1557 | + year += (year >= YY_PART_YEAR) ? 1900 : 2000; |
| 1558 | + } |
| 1559 | + return year * 100 + month % 12 + 1; |
| 1560 | + } |
| 1561 | +}; |
| 1562 | + |
| 1563 | +class PeriodAddImpl { |
| 1564 | +public: |
| 1565 | + static constexpr auto name = "period_add"; |
| 1566 | + static size_t get_number_of_arguments() { return 2; } |
| 1567 | + static DataTypePtr get_return_type_impl(const DataTypes& arguments) { |
| 1568 | + return std::make_shared<DataTypeInt64>(); |
| 1569 | + } |
| 1570 | + |
| 1571 | + static void execute(const std::vector<ColumnWithConstAndNullMap>& cols_info, |
| 1572 | + ColumnInt64::MutablePtr& res_col, PaddedPODArray<UInt8>& res_null_map_data, |
| 1573 | + size_t input_rows_count) { |
| 1574 | + const auto& left_data = |
| 1575 | + assert_cast<const ColumnInt64*>(cols_info[0].nested_col)->get_data(); |
| 1576 | + const auto& right_data = |
| 1577 | + assert_cast<const ColumnInt64*>(cols_info[1].nested_col)->get_data(); |
| 1578 | + for (size_t i = 0; i < input_rows_count; ++i) { |
| 1579 | + if (cols_info[0].is_null_at(i) || cols_info[1].is_null_at(i)) { |
| 1580 | + res_col->insert_default(); |
| 1581 | + res_null_map_data[i] = 1; |
| 1582 | + continue; |
| 1583 | + } |
| 1584 | + |
| 1585 | + int64_t period = left_data[index_check_const(i, cols_info[0].is_const)]; |
| 1586 | + int64_t months = right_data[index_check_const(i, cols_info[1].is_const)]; |
| 1587 | + res_col->insert_value(PeriodHelper::convert_month_to_period( |
| 1588 | + PeriodHelper::check_and_convert_period_to_month(period) + months)); |
| 1589 | + } |
| 1590 | + } |
| 1591 | +}; |
| 1592 | +class PeriodDiffImpl { |
| 1593 | +public: |
| 1594 | + static constexpr auto name = "period_diff"; |
| 1595 | + static size_t get_number_of_arguments() { return 2; } |
| 1596 | + static DataTypePtr get_return_type_impl(const DataTypes& arguments) { |
| 1597 | + return std::make_shared<DataTypeInt64>(); |
| 1598 | + } |
| 1599 | + |
| 1600 | + static void execute(const std::vector<ColumnWithConstAndNullMap>& cols_info, |
| 1601 | + ColumnInt64::MutablePtr& res_col, PaddedPODArray<UInt8>& res_null_map_data, |
| 1602 | + size_t input_rows_count) { |
| 1603 | + const auto& left_data = |
| 1604 | + assert_cast<const ColumnInt64*>(cols_info[0].nested_col)->get_data(); |
| 1605 | + const auto& right_data = |
| 1606 | + assert_cast<const ColumnInt64*>(cols_info[1].nested_col)->get_data(); |
| 1607 | + for (size_t i = 0; i < input_rows_count; ++i) { |
| 1608 | + if (cols_info[0].is_null_at(i) || cols_info[1].is_null_at(i)) { |
| 1609 | + res_col->insert_default(); |
| 1610 | + res_null_map_data[i] = 1; |
| 1611 | + continue; |
| 1612 | + } |
| 1613 | + |
| 1614 | + int64_t period1 = left_data[index_check_const(i, cols_info[0].is_const)]; |
| 1615 | + int64_t period2 = right_data[index_check_const(i, cols_info[1].is_const)]; |
| 1616 | + res_col->insert_value(PeriodHelper::check_and_convert_period_to_month(period1) - |
| 1617 | + PeriodHelper::check_and_convert_period_to_month(period2)); |
| 1618 | + } |
| 1619 | + } |
| 1620 | +}; |
| 1621 | + |
1421 | 1622 | #include "common/compile_check_avoid_end.h" |
1422 | 1623 | } // namespace doris::vectorized |
0 commit comments