mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-12-01 20:12:02 +00:00
5f7a852b92
It was crashing when the argument type was Nullable(T) where T was any integral type other than Int32.
238 lines
8.3 KiB
C++
238 lines
8.3 KiB
C++
#include <Columns/ColumnNullable.h>
|
|
#include <Columns/ColumnString.h>
|
|
#include <Columns/ColumnVector.h>
|
|
#include <Columns/ColumnsNumber.h>
|
|
#include <Core/callOnTypeIndex.h>
|
|
#include <DataTypes/DataTypeString.h>
|
|
#include <DataTypes/DataTypeNullable.h>
|
|
#include <DataTypes/DataTypesNumber.h>
|
|
#include <DataTypes/IDataType.h>
|
|
#include <Functions/IFunctionImpl.h>
|
|
#include <Functions/FunctionFactory.h>
|
|
#include <Functions/GregorianDate.h>
|
|
#include <IO/WriteBufferFromVector.h>
|
|
#include <IO/WriteHelpers.h>
|
|
|
|
namespace DB
|
|
{
|
|
|
|
namespace ErrorCodes
|
|
{
|
|
extern const int CANNOT_FORMAT_DATETIME;
|
|
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
|
}
|
|
|
|
template <typename Name, typename FromDataType, bool nullOnErrors>
|
|
class ExecutableFunctionFromModifiedJulianDay : public IExecutableFunctionImpl
|
|
{
|
|
public:
|
|
String getName() const override
|
|
{
|
|
return Name::name;
|
|
}
|
|
|
|
ColumnPtr execute(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override
|
|
{
|
|
using ColVecType = typename FromDataType::ColumnType;
|
|
const ColVecType * col_from = checkAndGetColumn<ColVecType>(arguments[0].column.get());
|
|
const typename ColVecType::Container & vec_from = col_from->getData();
|
|
|
|
auto col_to = ColumnString::create();
|
|
ColumnString::Chars & data_to = col_to->getChars();
|
|
ColumnString::Offsets & offsets_to = col_to->getOffsets();
|
|
data_to.resize(input_rows_count * strlen("YYYY-MM-DD") + 1);
|
|
offsets_to.resize(input_rows_count);
|
|
|
|
ColumnUInt8::MutablePtr col_null_map_to;
|
|
ColumnUInt8::Container * vec_null_map_to [[maybe_unused]] = nullptr;
|
|
if constexpr (nullOnErrors)
|
|
{
|
|
col_null_map_to = ColumnUInt8::create(input_rows_count);
|
|
vec_null_map_to = &col_null_map_to->getData();
|
|
}
|
|
|
|
WriteBufferFromVector<ColumnString::Chars> write_buffer(data_to);
|
|
for (size_t i = 0; i < input_rows_count; ++i)
|
|
{
|
|
if constexpr (nullOnErrors)
|
|
{
|
|
try
|
|
{
|
|
const GregorianDate<> gd(vec_from[i]);
|
|
gd.write(write_buffer);
|
|
(*vec_null_map_to)[i] = false;
|
|
}
|
|
catch (const Exception & e)
|
|
{
|
|
if (e.code() == ErrorCodes::CANNOT_FORMAT_DATETIME)
|
|
(*vec_null_map_to)[i] = true;
|
|
else
|
|
throw;
|
|
}
|
|
writeChar(0, write_buffer);
|
|
offsets_to[i] = write_buffer.count();
|
|
}
|
|
else
|
|
{
|
|
const GregorianDate<> gd(vec_from[i]);
|
|
gd.write(write_buffer);
|
|
writeChar(0, write_buffer);
|
|
offsets_to[i] = write_buffer.count();
|
|
}
|
|
}
|
|
write_buffer.finalize();
|
|
|
|
if constexpr (nullOnErrors)
|
|
return ColumnNullable::create(std::move(col_to), std::move(col_null_map_to));
|
|
else
|
|
return col_to;
|
|
}
|
|
|
|
bool useDefaultImplementationForConstants() const override
|
|
{
|
|
return true;
|
|
}
|
|
};
|
|
|
|
template <typename Name, typename FromDataType, bool nullOnErrors>
|
|
class FunctionBaseFromModifiedJulianDay : public IFunctionBaseImpl
|
|
{
|
|
public:
|
|
explicit FunctionBaseFromModifiedJulianDay(DataTypes argument_types_, DataTypePtr return_type_)
|
|
: argument_types(std::move(argument_types_))
|
|
, return_type(std::move(return_type_)) {}
|
|
|
|
String getName() const override
|
|
{
|
|
return Name::name;
|
|
}
|
|
|
|
const DataTypes & getArgumentTypes() const override
|
|
{
|
|
return argument_types;
|
|
}
|
|
|
|
const DataTypePtr & getResultType() const override
|
|
{
|
|
return return_type;
|
|
}
|
|
|
|
ExecutableFunctionImplPtr prepare(const ColumnsWithTypeAndName &) const override
|
|
{
|
|
return std::make_unique<ExecutableFunctionFromModifiedJulianDay<Name, FromDataType, nullOnErrors>>();
|
|
}
|
|
|
|
bool isInjective(const ColumnsWithTypeAndName &) const override
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool hasInformationAboutMonotonicity() const override
|
|
{
|
|
return true;
|
|
}
|
|
|
|
Monotonicity getMonotonicityForRange(const IDataType &, const Field &, const Field &) const override
|
|
{
|
|
return Monotonicity(
|
|
true, // is_monotonic
|
|
true, // is_positive
|
|
true); // is_always_monotonic
|
|
}
|
|
|
|
private:
|
|
DataTypes argument_types;
|
|
DataTypePtr return_type;
|
|
};
|
|
|
|
template <typename Name, bool nullOnErrors>
|
|
class FromModifiedJulianDayOverloadResolver : public IFunctionOverloadResolverImpl
|
|
{
|
|
public:
|
|
static constexpr auto name = Name::name;
|
|
|
|
static FunctionOverloadResolverImplPtr create(const Context &)
|
|
{
|
|
return std::make_unique<FromModifiedJulianDayOverloadResolver<Name, nullOnErrors>>();
|
|
}
|
|
|
|
String getName() const override
|
|
{
|
|
return Name::name;
|
|
}
|
|
|
|
FunctionBaseImplPtr build(const ColumnsWithTypeAndName & arguments, const DataTypePtr & return_type) const override
|
|
{
|
|
const DataTypePtr & from_type = removeNullable(arguments[0].type);
|
|
DataTypes argument_types = { from_type };
|
|
FunctionBaseImplPtr base;
|
|
auto call = [&](const auto & types) -> bool
|
|
{
|
|
using Types = std::decay_t<decltype(types)>;
|
|
using FromIntType = typename Types::RightType;
|
|
using FromDataType = DataTypeNumber<FromIntType>;
|
|
|
|
base = std::make_unique<FunctionBaseFromModifiedJulianDay<Name, FromDataType, nullOnErrors>>(argument_types, return_type);
|
|
return true;
|
|
};
|
|
bool built = callOnBasicType<void, true, false, false, false>(from_type->getTypeId(), call);
|
|
if (built)
|
|
return base;
|
|
|
|
/* When the argument is a NULL constant, the resulting
|
|
* function base will not be actually called but it
|
|
* will still be inspected. Returning a NULL pointer
|
|
* here causes a SEGV. So we must somehow create a
|
|
* dummy implementation and return it.
|
|
*/
|
|
if (WhichDataType(from_type).isNothing()) // Nullable(Nothing)
|
|
return std::make_unique<FunctionBaseFromModifiedJulianDay<Name, DataTypeInt32, nullOnErrors>>(argument_types, return_type);
|
|
else
|
|
// Should not happen.
|
|
throw Exception(
|
|
"The argument of function " + getName() + " must be integral", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
|
}
|
|
|
|
DataTypePtr getReturnType(const DataTypes & arguments) const override
|
|
{
|
|
if (!isInteger(arguments[0]))
|
|
{
|
|
throw Exception(
|
|
"The argument of function " + getName() + " must be integral", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
|
}
|
|
|
|
DataTypePtr base_type = std::make_shared<DataTypeString>();
|
|
if constexpr (nullOnErrors)
|
|
return std::make_shared<DataTypeNullable>(base_type);
|
|
else
|
|
return base_type;
|
|
}
|
|
|
|
size_t getNumberOfArguments() const override
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
bool isInjective(const ColumnsWithTypeAndName &) const override
|
|
{
|
|
return true;
|
|
}
|
|
};
|
|
|
|
struct NameFromModifiedJulianDay
|
|
{
|
|
static constexpr auto name = "fromModifiedJulianDay";
|
|
};
|
|
|
|
struct NameFromModifiedJulianDayOrNull
|
|
{
|
|
static constexpr auto name = "fromModifiedJulianDayOrNull";
|
|
};
|
|
|
|
void registerFunctionFromModifiedJulianDay(FunctionFactory & factory)
|
|
{
|
|
factory.registerFunction<FromModifiedJulianDayOverloadResolver<NameFromModifiedJulianDay, false>>();
|
|
factory.registerFunction<FromModifiedJulianDayOverloadResolver<NameFromModifiedJulianDayOrNull, true>>();
|
|
}
|
|
}
|