diff --git a/src/Functions/fromReadable.h b/src/Functions/fromReadable.h index 74c4a8958df..86d64dfa04b 100644 --- a/src/Functions/fromReadable.h +++ b/src/Functions/fromReadable.h @@ -1,12 +1,15 @@ #include #include +#include #include #include #include #include #include #include +#include "Common/Exception.h" +#include "DataTypes/DataTypeNullable.h" #include namespace DB @@ -18,13 +21,20 @@ namespace ErrorCodes extern const int CANNOT_PARSE_INPUT_ASSERTION_FAILED; } -template +enum class ErrorHandling : uint8_t +{ + Exception, + Zero, + Null +}; + +template class FunctionFromReadable : public IFunction { public: static constexpr auto name = Impl::name; - static FunctionPtr create(ContextPtr) { return std::make_shared>(); } + static FunctionPtr create(ContextPtr) { return std::make_shared>(); } String getName() const override { return name; } bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return true; } @@ -38,52 +48,77 @@ public: {"readable_size", static_cast(&isString), nullptr, "String"}, }; validateFunctionArgumentTypes(*this, arguments, args); - - return std::make_shared(); + DataTypePtr return_type = std::make_shared(); + if (error_handling == ErrorHandling::Null) { + return std::make_shared(return_type); + } else { + return return_type; + } + } ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override { - auto col_to = ColumnFloat64::create(); - auto & res_data = col_to->getData(); + auto col_res = ColumnFloat64::create(); + auto & res_data = col_res->getData(); + + ColumnUInt8::MutablePtr col_null_map; + if constexpr (error_handling == ErrorHandling::Null) + col_null_map = ColumnUInt8::create(input_rows_count, 0); for (size_t i = 0; i < input_rows_count; ++i) { std::string_view str = arguments[0].column->getDataAt(i).toView(); - ReadBufferFromString buf(str); - // tryReadFloatText does seem to not raise any error when there is leading whitespace so we check it explicitly - skipWhitespaceIfAny(buf); - if (buf.getPosition() > 0) - throw_exception(ErrorCodes::CANNOT_PARSE_INPUT_ASSERTION_FAILED, "Leading whitespace is not allowed", str); - - Float64 base = 0; - if (!tryReadFloatTextPrecise(base, buf)) // If we use the default (fast) tryReadFloatText this returns True on garbage input - throw_exception(ErrorCodes::CANNOT_PARSE_NUMBER, "Unable to parse readable size numeric component", str); - - skipWhitespaceIfAny(buf); - - String unit; - readStringUntilWhitespace(unit, buf); - if (!buf.eof()) - throw_exception(ErrorCodes::UNEXPECTED_DATA_AFTER_PARSED_VALUE, "Found trailing characters after readable size string", str); - boost::algorithm::to_lower(unit); - Float64 scale_factor = Impl::getScaleFactorForUnit(unit); - Float64 num_bytes = base * scale_factor; - - res_data.emplace_back(num_bytes); + try + { + auto num_bytes = parseReadableFormat(str); + res_data.emplace_back(num_bytes); + } + catch (...) + { + if constexpr (error_handling == ErrorHandling::Exception) + throw; + res_data[i] = 0; + if constexpr (error_handling == ErrorHandling::Null) + col_null_map->getData()[i] = 1; + } } - - return col_to; + if constexpr (error_handling == ErrorHandling::Null) + return ColumnNullable::create(std::move(col_res), std::move(col_null_map)); + else + return col_res; } private: template - void throw_exception(const int code, const String & msg, Arg arg) const + void throwException(const int code, const String & msg, Arg arg) const { throw Exception(code, "Invalid expression for function {} - {} (\"{}\")", getName(), msg, arg); } + + Float64 parseReadableFormat(const std::string_view & str) const + { + ReadBufferFromString buf(str); + // tryReadFloatText does seem to not raise any error when there is leading whitespace so we check it explicitly + skipWhitespaceIfAny(buf); + if (buf.getPosition() > 0) + throwException(ErrorCodes::CANNOT_PARSE_INPUT_ASSERTION_FAILED, "Leading whitespace is not allowed", str); + + Float64 base = 0; + if (!tryReadFloatTextPrecise(base, buf)) // If we use the default (fast) tryReadFloatText this returns True on garbage input + throwException(ErrorCodes::CANNOT_PARSE_NUMBER, "Unable to parse readable size numeric component", str); + skipWhitespaceIfAny(buf); + + String unit; + readStringUntilWhitespace(unit, buf); + if (!buf.eof()) + throwException(ErrorCodes::UNEXPECTED_DATA_AFTER_PARSED_VALUE, "Found trailing characters after readable size string", str); + boost::algorithm::to_lower(unit); + Float64 scale_factor = Impl::getScaleFactorForUnit(unit); + return base * scale_factor; + } }; }