ClickHouse
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
15namespace DB16{
17
18namespace ErrorCodes19{
20extern const int ILLEGAL_COLUMN;21extern const int CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER;22}
23
24namespace
25{
26
27class FunctionToDecimalString : public IFunction28{
29public:30static constexpr auto name = "toDecimalString";31static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionToDecimalString>(); }32
33String getName() const override { return name; }34
35bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return true; }36
37size_t getNumberOfArguments() const override { return 2; }38
39DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override40{41FunctionArgumentDescriptors mandatory_args = {42{"Value", static_cast<FunctionArgumentDescriptor::TypeValidator>(&isNumber), nullptr, "Number"},43{"precision", static_cast<FunctionArgumentDescriptor::TypeValidator>(&isNativeInteger), &isColumnConst, "const Integer"}44};45
46validateFunctionArgumentTypes(*this, arguments, mandatory_args, {});47
48return std::make_shared<DataTypeString>();49}50
51bool useDefaultImplementationForConstants() const override { return true; }52
53private:54/// For operations with Integer/Float55template <typename FromVectorType>56void vectorConstant(const FromVectorType & vec_from, UInt8 precision,57ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets) const58{59size_t input_rows_count = vec_from.size();60result_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.64WriteBufferFromVector<ColumnString::Chars> buf_to(vec_to);65
66for (size_t i = 0; i < input_rows_count; ++i)67{68format(vec_from[i], buf_to, precision);69result_offsets[i] = buf_to.count();70}71
72buf_to.finalize();73}74
75template <typename FirstArgVectorType>76void vectorVector(const FirstArgVectorType & vec_from, const ColumnVector<UInt8>::Container & vec_precision,77ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets) const78{79size_t input_rows_count = vec_from.size();80result_offsets.resize(input_rows_count);81
82WriteBufferFromVector<ColumnString::Chars> buf_to(vec_to);83
84constexpr size_t max_digits = std::numeric_limits<UInt256>::digits10;85
86for (size_t i = 0; i < input_rows_count; ++i)87{88if (vec_precision[i] > max_digits)89throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER,90"Too many fractional digits requested, shall not be more than {}", max_digits);91format(vec_from[i], buf_to, vec_precision[i]);92result_offsets[i] = buf_to.count();93}94
95buf_to.finalize();96}97
98/// For operations with Decimal99template <typename FirstArgVectorType>100void vectorConstant(const FirstArgVectorType & vec_from, UInt8 precision,101ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets, UInt8 from_scale) const102{103/// There are no more than 77 meaning digits (as it is the max length of UInt256). So we can limit it with 77.104constexpr size_t max_digits = std::numeric_limits<UInt256>::digits10;105if (precision > max_digits)106throw 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
109WriteBufferFromVector<ColumnString::Chars> buf_to(vec_to);110size_t input_rows_count = vec_from.size();111result_offsets.resize(input_rows_count);112
113for (size_t i = 0; i < input_rows_count; ++i)114{115writeText(vec_from[i], from_scale, buf_to, true, true, precision);116writeChar(0, buf_to);117result_offsets[i] = buf_to.count();118}119buf_to.finalize();120}121
122template <typename FirstArgVectorType>123void vectorVector(const FirstArgVectorType & vec_from, const ColumnVector<UInt8>::Container & vec_precision,124ColumnString::Chars & vec_to, ColumnString::Offsets & result_offsets, UInt8 from_scale) const125{126size_t input_rows_count = vec_from.size();127result_offsets.resize(input_rows_count);128
129WriteBufferFromVector<ColumnString::Chars> buf_to(vec_to);130
131constexpr size_t max_digits = std::numeric_limits<UInt256>::digits10;132
133for (size_t i = 0; i < input_rows_count; ++i)134{135if (vec_precision[i] > max_digits)136throw 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);138writeText(vec_from[i], from_scale, buf_to, true, true, vec_precision[i]);139writeChar(0, buf_to);140result_offsets[i] = buf_to.count();141}142buf_to.finalize();143}144
145template <is_floating_point T>146static 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.150if (precision > 60)151throw 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
154DB::DoubleConverter<false>::BufferType buffer;155double_conversion::StringBuilder builder{buffer, sizeof(buffer)};156
157const auto result = DB::DoubleConverter<false>::instance().ToFixed(value, precision, &builder);158
159if (!result)160throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER, "Error processing number: {}", value);161
162out.write(buffer, builder.position());163writeChar(0, out);164}165
166template <is_integer T>167static 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).170constexpr size_t max_digits = std::numeric_limits<UInt256>::digits10;171if (precision > max_digits)172throw DB::Exception(DB::ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER,173"Too many fractional digits requested, shall not be more than {}", max_digits);174writeText(value, out);175if (precision > 0) [[likely]]176{177writeChar('.', out);178for (int i = 0; i < precision; ++i)179writeChar('0', out);180writeChar(0, out);181}182}183
184public:185ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override186{187switch (arguments[0].type->getTypeId())188{189case TypeIndex::UInt8: return executeType<UInt8>(arguments);190case TypeIndex::UInt16: return executeType<UInt16>(arguments);191case TypeIndex::UInt32: return executeType<UInt32>(arguments);192case TypeIndex::UInt64: return executeType<UInt64>(arguments);193case TypeIndex::UInt128: return executeType<UInt128>(arguments);194case TypeIndex::UInt256: return executeType<UInt256>(arguments);195case TypeIndex::Int8: return executeType<Int8>(arguments);196case TypeIndex::Int16: return executeType<Int16>(arguments);197case TypeIndex::Int32: return executeType<Int32>(arguments);198case TypeIndex::Int64: return executeType<Int64>(arguments);199case TypeIndex::Int128: return executeType<Int128>(arguments);200case TypeIndex::Int256: return executeType<Int256>(arguments);201case TypeIndex::Float32: return executeType<Float32>(arguments);202case TypeIndex::Float64: return executeType<Float64>(arguments);203case TypeIndex::Decimal32: return executeType<Decimal32>(arguments);204case TypeIndex::Decimal64: return executeType<Decimal64>(arguments);205case TypeIndex::Decimal128: return executeType<Decimal128>(arguments);206case TypeIndex::Decimal256: return executeType<Decimal256>(arguments);207default:208throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of argument of function {}",209arguments[0].column->getName(), getName());210}211}212
213private:214template <typename T>215ColumnPtr executeType(const ColumnsWithTypeAndName & arguments) const216{217const auto * precision_col = checkAndGetColumn<ColumnVector<UInt8>>(arguments[1].column.get());218const auto * precision_col_const = checkAndGetColumnConst<ColumnVector<UInt8>>(arguments[1].column.get());219
220auto result_col = ColumnString::create();221auto * result_col_string = assert_cast<ColumnString *>(result_col.get());222ColumnString::Chars & result_chars = result_col_string->getChars();223ColumnString::Offsets & result_offsets = result_col_string->getOffsets();224
225if constexpr (is_decimal<T>)226{227const auto * from_col = checkAndGetColumn<ColumnDecimal<T>>(arguments[0].column.get());228
229if (from_col)230{231UInt8 from_scale = from_col->getScale();232if (precision_col_const)233vectorConstant(from_col->getData(), precision_col_const->template getValue<UInt8>(), result_chars, result_offsets, from_scale);234else if (precision_col)235vectorVector(from_col->getData(), precision_col->getData(), result_chars, result_offsets, from_scale);236else237throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of second argument of function formatDecimal", arguments[1].column->getName());238}239else240throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of first argument of function formatDecimal", arguments[0].column->getName());241}242else243{244const auto * from_col = checkAndGetColumn<ColumnVector<T>>(arguments[0].column.get());245if (from_col)246{247if (precision_col_const)248vectorConstant(from_col->getData(), precision_col_const->template getValue<UInt8>(), result_chars, result_offsets);249else if (precision_col)250vectorVector(from_col->getData(), precision_col->getData(), result_chars, result_offsets);251else252throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of second argument of function formatDecimal", arguments[1].column->getName());253
254}255else256throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of first argument of function formatDecimal", arguments[0].column->getName());257}258
259return result_col;260}261};262
263}
264
265REGISTER_FUNCTION(ToDecimalString)266{
267factory.registerFunction<FunctionToDecimalString>(268FunctionDocumentation{269.description=R"(270Returns string representation of a number. First argument is the number of any numeric type,
271second 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