Support different error handlings

This commit is contained in:
Francisco Javier Jurado Moreno 2024-05-27 18:39:18 +02:00
parent c5d5c32ee1
commit 3f9d330180

View File

@ -1,12 +1,15 @@
#include <base/types.h>
#include <boost/algorithm/string/case_conv.hpp>
#include <Columns/ColumnNullable.h>
#include <Columns/ColumnsNumber.h>
#include <DataTypes/DataTypesNumber.h>
#include <Functions/FunctionHelpers.h>
#include <Functions/IFunction.h>
#include <IO/ReadBufferFromString.h>
#include <IO/ReadHelpers.h>
#include "Common/Exception.h"
#include "DataTypes/DataTypeNullable.h"
#include <string_view>
namespace DB
@ -18,13 +21,20 @@ namespace ErrorCodes
extern const int CANNOT_PARSE_INPUT_ASSERTION_FAILED;
}
template <typename Impl>
enum class ErrorHandling : uint8_t
{
Exception,
Zero,
Null
};
template <typename Impl, ErrorHandling error_handling>
class FunctionFromReadable : public IFunction
{
public:
static constexpr auto name = Impl::name;
static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionFromReadable<Impl>>(); }
static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionFromReadable<Impl, error_handling>>(); }
String getName() const override { return name; }
bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return true; }
@ -38,52 +48,77 @@ public:
{"readable_size", static_cast<FunctionArgumentDescriptor::TypeValidator>(&isString), nullptr, "String"},
};
validateFunctionArgumentTypes(*this, arguments, args);
return std::make_shared<DataTypeFloat64>();
DataTypePtr return_type = std::make_shared<DataTypeFloat64>();
if (error_handling == ErrorHandling::Null) {
return std::make_shared<DataTypeNullable>(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 <typename Arg>
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;
}
};
}