#include #include #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int ILLEGAL_COLUMN; } namespace { /// Returns number of decimal digits you need to represent the value. /// For Decimal values takes in account their scales: calculates result over underlying int type which is (value * scale). /// countDigits(42) = 2, countDigits(42.000) = 5, countDigits(0.04200) = 4. /// I.e. you may check decimal overflow for Decimal64 with 'countDecimal(x) > 18'. It's a slow variant of isDecimalOverflow(). class FunctionCountDigits : public IFunction { public: static constexpr auto name = "countDigits"; static FunctionPtr create(ContextPtr) { return std::make_shared(); } String getName() const override { return name; } bool useDefaultImplementationForConstants() const override { return true; } size_t getNumberOfArguments() const override { return 1; } bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override { WhichDataType which_first(arguments[0]->getTypeId()); if (!which_first.isInt() && !which_first.isUInt() && !which_first.isDecimal()) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of argument of function {}", arguments[0]->getName(), getName()); return std::make_shared(); /// Up to 255 decimal digits. } ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override { const auto & src_column = arguments[0]; if (!src_column.column) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal column while execute function {}", getName()); auto result_column = ColumnUInt8::create(); auto call = [&](const auto & types) -> bool { using Types = std::decay_t; using Type = typename Types::RightType; using ColVecType = ColumnVectorOrDecimal; if (const ColVecType * col_vec = checkAndGetColumn(src_column.column.get())) { execute(*col_vec, *result_column, input_rows_count); return true; } throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal column while execute function {}", getName()); }; TypeIndex dec_type_idx = src_column.type->getTypeId(); if (!callOnBasicType(dec_type_idx, call)) throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Wrong call for {} with {}", getName(), src_column.type->getName()); return result_column; } private: template static void execute(const ColVecType & col, ColumnUInt8 & result_column, size_t rows_count) { using NativeT = make_unsigned_t>; const auto & src_data = col.getData(); auto & dst_data = result_column.getData(); dst_data.resize(rows_count); for (size_t i = 0; i < rows_count; ++i) { if constexpr (is_decimal) { auto value = src_data[i].value; if (unlikely(value < 0)) dst_data[i] = digits10(-static_cast(value)); else dst_data[i] = digits10(value); } else { auto value = src_data[i]; if (unlikely(value < 0)) dst_data[i] = digits10(-static_cast(value)); else dst_data[i] = digits10(value); } } } }; } REGISTER_FUNCTION(CountDigits) { factory.registerFunction(); } }