ClickHouse

Форк
0
/
toDecimalString.cpp 
279 строк · 12.1 Кб
1
#include <Functions/FunctionFactory.h>
2
#include <Functions/IFunction.h>
3
#include <Core/Types.h>
4
#include <Functions/FunctionHelpers.h>
5
#include <Columns/ColumnString.h>
6
#include <Columns/ColumnVector.h>
7
#include <Columns/ColumnDecimal.h>
8
#include <DataTypes/DataTypeString.h>
9
#include <DataTypes/DataTypesNumber.h>
10
#include <IO/WriteBufferFromVector.h>
11
#include <IO/WriteHelpers.h>
12
#include <Interpreters/Context_fwd.h>
13

14

15
namespace DB
16
{
17

18
namespace ErrorCodes
19
{
20
    extern const int ILLEGAL_COLUMN;
21
    extern const int CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER;
22
}
23

24
namespace
25
{
26

27
class FunctionToDecimalString : public IFunction
28
{
29
public:
30
    static constexpr auto name = "toDecimalString";
31
    static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionToDecimalString>(); }
32

33
    String getName() const override { return name; }
34

35
    bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return true; }
36

37
    size_t getNumberOfArguments() const override { return 2; }
38

39
    DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
40
    {
41
        FunctionArgumentDescriptors mandatory_args = {
42
            {"Value", static_cast<FunctionArgumentDescriptor::TypeValidator>(&isNumber), nullptr, "Number"},
43
            {"precision", static_cast<FunctionArgumentDescriptor::TypeValidator>(&isNativeInteger), &isColumnConst, "const Integer"}
44
        };
45

46
        validateFunctionArgumentTypes(*this, arguments, mandatory_args, {});
47

48
        return std::make_shared<DataTypeString>();
49
    }
50

51
    bool useDefaultImplementationForConstants() const override { return true; }
52

53
private:
54
    /// For operations with Integer/Float
55
    template <typename FromVectorType>
56
    void vectorConstant(const FromVectorType & vec_from, UInt8 precision,
57
                        ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets) const
58
    {
59
        size_t input_rows_count = vec_from.size();
60
        result_offsets.resize(input_rows_count);
61

62
        /// Buffer is used here and in functions below because resulting size cannot be precisely anticipated,
63
        /// and buffer resizes on-the-go. Also, .count() provided by buffer is convenient in this case.
64
        WriteBufferFromVector<ColumnString::Chars> buf_to(vec_to);
65

66
        for (size_t i = 0; i < input_rows_count; ++i)
67
        {
68
            format(vec_from[i], buf_to, precision);
69
            result_offsets[i] = buf_to.count();
70
        }
71

72
        buf_to.finalize();
73
    }
74

75
    template <typename FirstArgVectorType>
76
    void vectorVector(const FirstArgVectorType & vec_from, const ColumnVector<UInt8>::Container & vec_precision,
77
                      ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets) const
78
    {
79
        size_t input_rows_count = vec_from.size();
80
        result_offsets.resize(input_rows_count);
81

82
        WriteBufferFromVector<ColumnString::Chars> buf_to(vec_to);
83

84
        constexpr size_t max_digits = std::numeric_limits<UInt256>::digits10;
85

86
        for (size_t i = 0; i < input_rows_count; ++i)
87
        {
88
            if (vec_precision[i] > max_digits)
89
                throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER,
90
                                    "Too many fractional digits requested, shall not be more than {}", max_digits);
91
            format(vec_from[i], buf_to, vec_precision[i]);
92
            result_offsets[i] = buf_to.count();
93
        }
94

95
        buf_to.finalize();
96
    }
97

98
    /// For operations with Decimal
99
    template <typename FirstArgVectorType>
100
    void vectorConstant(const FirstArgVectorType & vec_from, UInt8 precision,
101
                        ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets, UInt8 from_scale) const
102
    {
103
        /// There are no more than 77 meaning digits (as it is the max length of UInt256). So we can limit it with 77.
104
        constexpr size_t max_digits = std::numeric_limits<UInt256>::digits10;
105
        if (precision > max_digits)
106
            throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER,
107
                                "Too many fractional digits requested for Decimal, must not be more than {}", max_digits);
108

109
        WriteBufferFromVector<ColumnString::Chars> buf_to(vec_to);
110
        size_t input_rows_count = vec_from.size();
111
        result_offsets.resize(input_rows_count);
112

113
        for (size_t i = 0; i < input_rows_count; ++i)
114
        {
115
            writeText(vec_from[i], from_scale, buf_to, true, true, precision);
116
            writeChar(0, buf_to);
117
            result_offsets[i] = buf_to.count();
118
        }
119
        buf_to.finalize();
120
    }
121

122
    template <typename FirstArgVectorType>
123
    void vectorVector(const FirstArgVectorType & vec_from, const ColumnVector<UInt8>::Container & vec_precision,
124
                      ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets, UInt8 from_scale) const
125
    {
126
        size_t input_rows_count = vec_from.size();
127
        result_offsets.resize(input_rows_count);
128

129
        WriteBufferFromVector<ColumnString::Chars> buf_to(vec_to);
130

131
        constexpr size_t max_digits = std::numeric_limits<UInt256>::digits10;
132

133
        for (size_t i = 0; i < input_rows_count; ++i)
134
        {
135
            if (vec_precision[i] > max_digits)
136
                throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER,
137
                                    "Too many fractional digits requested for Decimal, must not be more than {}", max_digits);
138
            writeText(vec_from[i], from_scale, buf_to, true, true, vec_precision[i]);
139
            writeChar(0, buf_to);
140
            result_offsets[i] = buf_to.count();
141
        }
142
        buf_to.finalize();
143
    }
144

145
    template <is_floating_point T>
146
    static void format(T value, DB::WriteBuffer & out, UInt8 precision)
147
    {
148
        /// Maximum of 60 is hard-coded in 'double-conversion/double-conversion.h' for floating point values,
149
        /// Catch this here to give user a more reasonable error.
150
        if (precision > 60)
151
            throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER,
152
                                "Too high precision requested for Float, must not be more than 60, got {}", Int8(precision));
153

154
        DB::DoubleConverter<false>::BufferType buffer;
155
        double_conversion::StringBuilder builder{buffer, sizeof(buffer)};
156

157
        const auto result = DB::DoubleConverter<false>::instance().ToFixed(value, precision, &builder);
158

159
        if (!result)
160
            throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, "Error processing number: {}", value);
161

162
        out.write(buffer, builder.position());
163
        writeChar(0, out);
164
    }
165

166
    template <is_integer T>
167
    static void format(T value, DB::WriteBuffer & out, UInt8 precision)
168
    {
169
        /// Fractional part for Integer is just trailing zeros. Let's limit it with 77 (like with Decimals).
170
        constexpr size_t max_digits = std::numeric_limits<UInt256>::digits10;
171
        if (precision > max_digits)
172
            throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER,
173
                                "Too many fractional digits requested, shall not be more than {}", max_digits);
174
        writeText(value, out);
175
        if (precision > 0) [[likely]]
176
        {
177
            writeChar('.', out);
178
            for (int i = 0; i < precision; ++i)
179
                writeChar('0', out);
180
            writeChar(0, out);
181
        }
182
    }
183

184
public:
185
    ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override
186
    {
187
        switch (arguments[0].type->getTypeId())
188
        {
189
            case TypeIndex::UInt8:      return executeType<UInt8>(arguments);
190
            case TypeIndex::UInt16:     return executeType<UInt16>(arguments);
191
            case TypeIndex::UInt32:     return executeType<UInt32>(arguments);
192
            case TypeIndex::UInt64:     return executeType<UInt64>(arguments);
193
            case TypeIndex::UInt128:    return executeType<UInt128>(arguments);
194
            case TypeIndex::UInt256:    return executeType<UInt256>(arguments);
195
            case TypeIndex::Int8:       return executeType<Int8>(arguments);
196
            case TypeIndex::Int16:      return executeType<Int16>(arguments);
197
            case TypeIndex::Int32:      return executeType<Int32>(arguments);
198
            case TypeIndex::Int64:      return executeType<Int64>(arguments);
199
            case TypeIndex::Int128:     return executeType<Int128>(arguments);
200
            case TypeIndex::Int256:     return executeType<Int256>(arguments);
201
            case TypeIndex::Float32:    return executeType<Float32>(arguments);
202
            case TypeIndex::Float64:    return executeType<Float64>(arguments);
203
            case TypeIndex::Decimal32:  return executeType<Decimal32>(arguments);
204
            case TypeIndex::Decimal64:  return executeType<Decimal64>(arguments);
205
            case TypeIndex::Decimal128: return executeType<Decimal128>(arguments);
206
            case TypeIndex::Decimal256: return executeType<Decimal256>(arguments);
207
            default:
208
                throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of argument of function {}",
209
                                arguments[0].column->getName(), getName());
210
        }
211
    }
212

213
private:
214
    template <typename T>
215
    ColumnPtr executeType(const ColumnsWithTypeAndName & arguments) const
216
    {
217
        const auto * precision_col = checkAndGetColumn<ColumnVector<UInt8>>(arguments[1].column.get());
218
        const auto * precision_col_const = checkAndGetColumnConst<ColumnVector<UInt8>>(arguments[1].column.get());
219

220
        auto result_col = ColumnString::create();
221
        auto * result_col_string = assert_cast<ColumnString *>(result_col.get());
222
        ColumnString::Chars & result_chars = result_col_string->getChars();
223
        ColumnString::Offsets & result_offsets = result_col_string->getOffsets();
224

225
        if constexpr (is_decimal<T>)
226
        {
227
            const auto * from_col = checkAndGetColumn<ColumnDecimal<T>>(arguments[0].column.get());
228

229
            if (from_col)
230
            {
231
                UInt8 from_scale = from_col->getScale();
232
                if (precision_col_const)
233
                    vectorConstant(from_col->getData(), precision_col_const->template getValue<UInt8>(), result_chars, result_offsets, from_scale);
234
                else if (precision_col)
235
                    vectorVector(from_col->getData(), precision_col->getData(), result_chars, result_offsets, from_scale);
236
                else
237
                    throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of second argument of function formatDecimal", arguments[1].column->getName());
238
            }
239
            else
240
                throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of first argument of function formatDecimal", arguments[0].column->getName());
241
        }
242
        else
243
        {
244
            const auto * from_col = checkAndGetColumn<ColumnVector<T>>(arguments[0].column.get());
245
            if (from_col)
246
            {
247
                if (precision_col_const)
248
                    vectorConstant(from_col->getData(), precision_col_const->template getValue<UInt8>(), result_chars, result_offsets);
249
                else if (precision_col)
250
                    vectorVector(from_col->getData(), precision_col->getData(), result_chars, result_offsets);
251
                else
252
                    throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of second argument of function formatDecimal", arguments[1].column->getName());
253

254
            }
255
            else
256
                throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of first argument of function formatDecimal", arguments[0].column->getName());
257
        }
258

259
        return result_col;
260
    }
261
};
262

263
}
264

265
REGISTER_FUNCTION(ToDecimalString)
266
{
267
    factory.registerFunction<FunctionToDecimalString>(
268
        FunctionDocumentation{
269
            .description=R"(
270
Returns string representation of a number. First argument is the number of any numeric type,
271
second argument is the desired number of digits in fractional part. Returns String.
272

273
        )",
274
            .examples{{"toDecimalString", "SELECT toDecimalString(2.1456,2)", ""}},
275
            .categories{"String"}
276
        }, FunctionFactory::CaseInsensitive);
277
}
278

279
}
280

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.