aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/clickhouse/src/Functions/IFunctionDateOrDateTime.h
blob: c22754eecd76ebd97184f1fdd5bf199823e4bbd8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#pragma once
#include <DataTypes/DataTypeDate.h>
#include <DataTypes/DataTypeDate32.h>
#include <DataTypes/DataTypeDateTime.h>
#include <DataTypes/DataTypeDateTime64.h>
#include <DataTypes/DataTypeLowCardinality.h>

#include <Functions/IFunction.h>
#include <Functions/extractTimeZoneFromFunctionArguments.h>
#include <Functions/DateTimeTransforms.h>
#include <Functions/TransformDateTime64.h>
#include <IO/WriteHelpers.h>
#include <Interpreters/Context.h>


namespace DB
{

namespace ErrorCodes
{
    extern const int ILLEGAL_TYPE_OF_ARGUMENT;
    extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
}

template <typename Transform>
class IFunctionDateOrDateTime : public IFunction
{
public:
    static constexpr auto name = Transform::name;
    String getName() const override { return name; }

    bool isVariadic() const override { return true; }

    bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; }

    size_t getNumberOfArguments() const override { return 0; }

    bool useDefaultImplementationForConstants() const override { return true; }

    ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1}; }

    bool hasInformationAboutMonotonicity() const override
    {
        return true;
    }

    Monotonicity getMonotonicityForRange(const IDataType & type, const Field & left, const Field & right) const override
    {
        if constexpr (std::is_same_v<typename Transform::FactorTransform, ZeroTransform>)
            return { .is_monotonic = true, .is_always_monotonic = true };
        else
        {
            const IFunction::Monotonicity is_monotonic = { .is_monotonic = true };
            const IFunction::Monotonicity is_not_monotonic;

            const DateLUTImpl * date_lut = &DateLUT::instance();
            if (const auto * timezone = dynamic_cast<const TimezoneMixin *>(&type))
                date_lut = &timezone->getTimeZone();

            if (left.isNull() || right.isNull())
                return is_not_monotonic;

            const auto * type_ptr = &type;

            if (const auto * lc_type = checkAndGetDataType<DataTypeLowCardinality>(type_ptr))
                type_ptr = lc_type->getDictionaryType().get();

            if (const auto * nullable_type = checkAndGetDataType<DataTypeNullable>(type_ptr))
                type_ptr = nullable_type->getNestedType().get();

            /// The function is monotonous on the [left, right] segment, if the factor transformation returns the same values for them.

            if (checkAndGetDataType<DataTypeDate>(type_ptr))
            {
                return Transform::FactorTransform::execute(UInt16(left.get<UInt64>()), *date_lut)
                    == Transform::FactorTransform::execute(UInt16(right.get<UInt64>()), *date_lut)
                    ? is_monotonic : is_not_monotonic;
            }
            else if (checkAndGetDataType<DataTypeDate32>(type_ptr))
            {
                return Transform::FactorTransform::execute(Int32(left.get<UInt64>()), *date_lut)
                       == Transform::FactorTransform::execute(Int32(right.get<UInt64>()), *date_lut)
                       ? is_monotonic : is_not_monotonic;
            }
            else if (checkAndGetDataType<DataTypeDateTime>(type_ptr))
            {
                return Transform::FactorTransform::execute(UInt32(left.get<UInt64>()), *date_lut)
                    == Transform::FactorTransform::execute(UInt32(right.get<UInt64>()), *date_lut)
                    ? is_monotonic : is_not_monotonic;
            }
            else
            {
                assert(checkAndGetDataType<DataTypeDateTime64>(type_ptr));

                const auto & left_date_time = left.get<DateTime64>();
                TransformDateTime64<typename Transform::FactorTransform> transformer_left(left_date_time.getScale());

                const auto & right_date_time = right.get<DateTime64>();
                TransformDateTime64<typename Transform::FactorTransform> transformer_right(right_date_time.getScale());

                return transformer_left.execute(left_date_time.getValue(), *date_lut)
                    == transformer_right.execute(right_date_time.getValue(), *date_lut)
                    ? is_monotonic : is_not_monotonic;
            }
        }
    }

protected:
    void checkArguments(const ColumnsWithTypeAndName & arguments, bool is_result_type_date_or_date32) const
    {
        if (arguments.size() == 1)
        {
            if (!isDateOrDate32(arguments[0].type) && !isDateTime(arguments[0].type) && !isDateTime64(arguments[0].type))
                throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
                    "Illegal type {} of argument of function {}. Should be Date, Date32, DateTime or DateTime64",
                    arguments[0].type->getName(), getName());
        }
        else if (arguments.size() == 2)
        {
            if (!isDateOrDate32(arguments[0].type) && !isDateTime(arguments[0].type) && !isDateTime64(arguments[0].type))
                throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
                    "Illegal type {} of argument of function {}. Should be Date, Date32, DateTime or DateTime64",
                    arguments[0].type->getName(), getName());
            if (!isString(arguments[1].type))
                throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
                    "Function {} supports 1 or 2 arguments. The optional 2nd argument must be "
                    "a constant string with a timezone name",
                    getName());
            if (isDateOrDate32(arguments[0].type) && is_result_type_date_or_date32)
                throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
                    "The timezone argument of function {} is allowed only when the 1st argument has the type DateTime or DateTime64",
                    getName());
        }
        else
            throw Exception(ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH,
                "Number of arguments for function {} doesn't match: passed {}, should be 1 or 2",
                getName(), arguments.size());
    }
};

}