|
20 | 20 |
|
21 | 21 | #pragma once |
22 | 22 |
|
| 23 | +#include <libdivide.h> |
| 24 | + |
23 | 25 | #include <cmath> |
24 | 26 | #include <cstdint> |
25 | 27 |
|
26 | 28 | #include "common/status.h" |
| 29 | +#include "runtime/define_primitive_type.h" |
27 | 30 | #include "runtime/primitive_type.h" |
28 | 31 | #include "util/binary_cast.hpp" |
| 32 | +#include "vec/columns/column_decimal.h" |
29 | 33 | #include "vec/columns/column_nullable.h" |
30 | 34 | #include "vec/columns/column_string.h" |
31 | 35 | #include "vec/columns/column_vector.h" |
@@ -591,6 +595,139 @@ class FunctionTimeFormat : public IFunction { |
591 | 595 | } |
592 | 596 | }; |
593 | 597 |
|
| 598 | +// Base template for optimized time field(HOUR, MINUTE, SECOND, MS) extraction from Unix timestamp |
| 599 | +// Uses lookup_offset to avoid expensive civil_second construction |
| 600 | +template <typename Impl> |
| 601 | +class FunctionTimeFieldFromUnixtime : public IFunction { |
| 602 | +public: |
| 603 | + static constexpr auto name = Impl::name; |
| 604 | + static FunctionPtr create() { return std::make_shared<FunctionTimeFieldFromUnixtime<Impl>>(); } |
| 605 | + |
| 606 | + String get_name() const override { return name; } |
| 607 | + |
| 608 | + size_t get_number_of_arguments() const override { return 1; } |
| 609 | + |
| 610 | + DataTypePtr get_return_type_impl(const ColumnsWithTypeAndName& arguments) const override { |
| 611 | + // microsecond_from_unixtime returns Int32, others (hour/minute/second) return Int8 |
| 612 | + if constexpr (Impl::ArgType == PrimitiveType::TYPE_DECIMAL64) { |
| 613 | + return make_nullable(std::make_shared<DataTypeInt32>()); |
| 614 | + } else { |
| 615 | + return make_nullable(std::make_shared<DataTypeInt8>()); |
| 616 | + } |
| 617 | + } |
| 618 | + |
| 619 | + // (UTC 9999-12-31 23:59:59) - 24 * 3600 |
| 620 | + static const int64_t TIMESTAMP_VALID_MAX = 253402243199L; |
| 621 | + |
| 622 | + Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments, |
| 623 | + uint32_t result, size_t input_rows_count) const override { |
| 624 | + using ArgColType = PrimitiveTypeTraits<Impl::ArgType>::ColumnType; |
| 625 | + using ResColType = std::conditional_t<Impl::ArgType == PrimitiveType::TYPE_DECIMAL64, |
| 626 | + ColumnInt32, ColumnInt8>; |
| 627 | + using ResItemType = typename ResColType::value_type; |
| 628 | + auto res = ResColType::create(); |
| 629 | + |
| 630 | + const auto* ts_col = |
| 631 | + assert_cast<const ArgColType*>(block.get_by_position(arguments[0]).column.get()); |
| 632 | + if constexpr (Impl::ArgType == PrimitiveType::TYPE_DECIMAL64) { |
| 633 | + // microsecond_from_unixtime only |
| 634 | + const auto scale = static_cast<int32_t>(ts_col->get_scale()); |
| 635 | + |
| 636 | + for (int i = 0; i < input_rows_count; ++i) { |
| 637 | + const auto seconds = ts_col->get_intergral_part(i); |
| 638 | + const auto fraction = ts_col->get_fractional_part(i); |
| 639 | + |
| 640 | + if (seconds < 0 || seconds > TIMESTAMP_VALID_MAX) { |
| 641 | + return Status::InvalidArgument( |
| 642 | + "The input value of TimeFiled(from_unixtime()) must between 0 and " |
| 643 | + "253402243199L"); |
| 644 | + } |
| 645 | + |
| 646 | + ResItemType value = Impl::extract_field(fraction, scale); |
| 647 | + res->insert_value(value); |
| 648 | + } |
| 649 | + } else { |
| 650 | + auto ctz = context->state()->timezone_obj(); |
| 651 | + for (int i = 0; i < input_rows_count; ++i) { |
| 652 | + auto date = ts_col->get_element(i); |
| 653 | + |
| 654 | + if (date < 0 || date > TIMESTAMP_VALID_MAX) { |
| 655 | + return Status::InvalidArgument( |
| 656 | + "The input value of TimeFiled(from_unixtime()) must between 0 and " |
| 657 | + "253402243199L"); |
| 658 | + } |
| 659 | + |
| 660 | + ResItemType value = Impl::extract_field(date, ctz); |
| 661 | + res->insert_value(value); |
| 662 | + } |
| 663 | + } |
| 664 | + block.replace_by_position(result, std::move(res)); |
| 665 | + return Status::OK(); |
| 666 | + } |
| 667 | +}; |
| 668 | + |
| 669 | +struct HourFromUnixtimeImpl { |
| 670 | + static constexpr PrimitiveType ArgType = PrimitiveType::TYPE_BIGINT; |
| 671 | + static constexpr auto name = "hour_from_unixtime"; |
| 672 | + |
| 673 | + static int8_t extract_field(int64_t local_time, const cctz::time_zone& ctz) { |
| 674 | + static const auto epoch = std::chrono::time_point_cast<cctz::sys_seconds>( |
| 675 | + std::chrono::system_clock::from_time_t(0)); |
| 676 | + cctz::time_point<cctz::sys_seconds> t = epoch + cctz::seconds(local_time); |
| 677 | + int offset = ctz.lookup_offset(t).offset; |
| 678 | + local_time += offset; |
| 679 | + |
| 680 | + static const libdivide::divider<int64_t> fast_div_3600(3600); |
| 681 | + static const libdivide::divider<int64_t> fast_div_86400(86400); |
| 682 | + |
| 683 | + int64_t remainder; |
| 684 | + if (LIKELY(local_time >= 0)) { |
| 685 | + remainder = local_time - local_time / fast_div_86400 * 86400; |
| 686 | + } else { |
| 687 | + remainder = local_time % 86400; |
| 688 | + if (remainder < 0) { |
| 689 | + remainder += 86400; |
| 690 | + } |
| 691 | + } |
| 692 | + return static_cast<int8_t>(remainder / fast_div_3600); |
| 693 | + } |
| 694 | +}; |
| 695 | + |
| 696 | +struct MinuteFromUnixtimeImpl { |
| 697 | + static constexpr PrimitiveType ArgType = PrimitiveType::TYPE_BIGINT; |
| 698 | + static constexpr auto name = "minute_from_unixtime"; |
| 699 | + |
| 700 | + static int8_t extract_field(int64_t local_time, const cctz::time_zone& /*ctz*/) { |
| 701 | + static const libdivide::divider<int64_t> fast_div_60(60); |
| 702 | + static const libdivide::divider<int64_t> fast_div_3600(3600); |
| 703 | + |
| 704 | + local_time = local_time - local_time / fast_div_3600 * 3600; |
| 705 | + |
| 706 | + return static_cast<int8_t>(local_time / fast_div_60); |
| 707 | + } |
| 708 | +}; |
| 709 | + |
| 710 | +struct SecondFromUnixtimeImpl { |
| 711 | + static constexpr PrimitiveType ArgType = PrimitiveType::TYPE_BIGINT; |
| 712 | + static constexpr auto name = "second_from_unixtime"; |
| 713 | + |
| 714 | + static int8_t extract_field(int64_t local_time, const cctz::time_zone& /*ctz*/) { |
| 715 | + return static_cast<int8_t>(local_time % 60); |
| 716 | + } |
| 717 | +}; |
| 718 | + |
| 719 | +struct MicrosecondFromUnixtimeImpl { |
| 720 | + static constexpr PrimitiveType ArgType = PrimitiveType::TYPE_DECIMAL64; |
| 721 | + static constexpr auto name = "microsecond_from_unixtime"; |
| 722 | + |
| 723 | + static int32_t extract_field(int64_t fraction, int scale) { |
| 724 | + if (scale < 6) { |
| 725 | + fraction *= common::exp10_i64(6 - scale); |
| 726 | + } |
| 727 | + return static_cast<int32_t>(fraction); |
| 728 | + } |
| 729 | +}; |
| 730 | + |
594 | 731 | #include "common/compile_check_end.h" |
595 | 732 | } // namespace doris::vectorized |
596 | 733 |
|
|
0 commit comments