Simplifications

This commit is contained in:
Alexey Milovidov 2020-10-11 01:03:03 +03:00
parent 7541be86cd
commit 452a1b5218

View File

@ -15,7 +15,7 @@ namespace ErrorCodes
{
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
extern const int ILLEGAL_COLUMN;
extern const int BAD_ARGUMENTS;
}
namespace
@ -67,163 +67,122 @@ public:
bool useDefaultImplementationForConstants() const override { return true; }
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) const override
enum Unit
{
if (!(executeType<UInt8>(block, arguments, result)
|| executeType<UInt16>(block, arguments, result)
|| executeType<UInt32>(block, arguments, result)
|| executeType<UInt64>(block, arguments, result)
|| executeType<Int8>(block, arguments, result)
|| executeType<Int16>(block, arguments, result)
|| executeType<Int32>(block, arguments, result)
|| executeType<Int64>(block, arguments, result)
|| executeType<Float32>(block, arguments, result)
|| executeType<Float64>(block, arguments, result)))
throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName()
+ " of argument of function " + getName(),
ErrorCodes::ILLEGAL_COLUMN);
}
Seconds,
Minutes,
Hours,
Days,
Months,
Years
};
private:
void formatReadableTimeDelta(double value, String maximum_unit, DB::WriteBuffer & out) const
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) const override
{
// 60 SECONDS = 1 MINUTE
// 3600 SECONDS = 1 HOUR
// 86400 SECONDS = 1 DAY
// 2635200 SECONDS = 30.5 DAYS = 1 MONTH
// 31536000 SECONDS = 365 DAYS = 1 YEAR
int8_t sig = value < 0 ? -1 : 1;
int maximum_unit_int = 6;
if (maximum_unit == "seconds")
maximum_unit_int = 1;
else if (maximum_unit == "minutes")
maximum_unit_int = 2;
else if (maximum_unit == "hours")
maximum_unit_int = 3;
else if (maximum_unit == "days")
maximum_unit_int = 4;
else if (maximum_unit == "months")
maximum_unit_int = 5;
else if (maximum_unit == "years")
maximum_unit_int = 6;
value *= sig;
double aux = 0;
long long int years = maximum_unit_int < 6 ? 0 : value / 31536000;
aux += years * 31536000;
long long int months = maximum_unit_int < 5 ? 0 : (value - aux) / 2635200;
aux += months * 2635200;
long long int days = maximum_unit_int < 4 ? 0 : (value - aux) / 86400;
aux += days * 86400;
long long int hours = maximum_unit_int < 3 ? 0 : (value - aux) / 3600;
aux += hours * 3600;
long long int minutes = maximum_unit_int < 2 ? 0 : (value - aux) / 60;
aux += minutes * 60;
double seconds = value - aux;
std::vector<String> parts;
/* If value is bigger than 2**64 (292471208677 years) overflow happens
To prevent wrong results the function shows only the year
and maximum_unit is ignored
*/
if (value > 9223372036854775808.0)
{
std::string years_str = std::to_string(value/31536000.0);
years_str.erase(years_str.find('.'), std::string::npos);
parts.push_back(years_str + " years");
}
else
{
if (years)
{
parts.push_back(std::to_string(years) + (years == 1 ? " year" : " years"));
}
if (months)
{
parts.push_back(std::to_string(months) + (months == 1 ? "month" : " months"));
}
if (days)
{
parts.push_back(std::to_string(days) + (days == 1 ? " day" : " days"));
}
if (hours)
{
parts.push_back(std::to_string(hours) + (hours == 1 ? " hour" : " hours"));
}
if (minutes)
{
parts.push_back(std::to_string(minutes) + (minutes == 1 ? " minute" : " minutes"));
}
if (seconds)
{
std::string seconds_str = std::to_string(seconds);
seconds_str.erase(seconds_str.find_last_not_of('0') + 1, std::string::npos);
seconds_str.erase(seconds_str.find_last_not_of('.') + 1, std::string::npos);
parts.push_back(seconds_str + (seconds==1?" second":" seconds"));
}
}
String str_value;
for (size_t i = 0, parts_size = parts.size(); i < parts_size; ++i)
{
if (!str_value.empty())
{
if (i == parts.size() - 1)
str_value += " and ";
else
str_value += ", ";
}
str_value += parts[i];
}
if (sig < 0)
str_value = "- " + str_value;
writeCString(str_value.c_str(), out);
}
template <typename T>
bool executeType(Block & block, const ColumnNumbers & arguments, size_t result) const
{
String maximum_unit = "";
StringRef maximum_unit_str;
if (arguments.size() == 2)
{
const ColumnPtr & maximum_unit_column = block.getByPosition(arguments[1]).column;
const ColumnConst * maximum_unit_const_col = checkAndGetColumnConstStringOrFixedString(maximum_unit_column.get());
if (maximum_unit_const_col)
maximum_unit = maximum_unit_const_col->getValue<String>();
maximum_unit_str = maximum_unit_const_col->getDataColumn().getDataAt(0);
}
if (const ColumnVector<T> * col_from = checkAndGetColumn<ColumnVector<T>>(block.getByPosition(arguments[0]).column.get()))
Unit max_unit;
if (maximum_unit_str.size == 0 || maximum_unit_str == "years")
max_unit = Years;
else if (maximum_unit_str == "months")
max_unit = Months;
else if (maximum_unit_str == "days")
max_unit = Days;
else if (maximum_unit_str == "hours")
max_unit = Hours;
else if (maximum_unit_str == "minutes")
max_unit = Minutes;
else if (maximum_unit_str == "seconds")
max_unit = Seconds;
else
throw Exception(ErrorCodes::BAD_ARGUMENTS,
"Unexpected value of maximum unit argument ({}) for function {}, the only allowed values are:"
" 'seconds', 'minutes', 'hours', 'days', 'months', 'years'.",
maximum_unit_str.toString(), getName());
auto col_to = ColumnString::create();
ColumnString::Chars & data_to = col_to->getChars();
ColumnString::Offsets & offsets_to = col_to->getOffsets();
offsets_to.resize(input_rows_count);
WriteBufferFromVector<ColumnString::Chars> buf_to(data_to);
for (size_t i = 0; i < input_rows_count; ++i)
{
auto col_to = ColumnString::create();
const typename ColumnVector<T>::Container & vec_from = col_from->getData();
ColumnString::Chars & data_to = col_to->getChars();
ColumnString::Offsets & offsets_to = col_to->getOffsets();
size_t size = vec_from.size();
data_to.resize(size * 2);
offsets_to.resize(size);
WriteBufferFromVector<ColumnString::Chars> buf_to(data_to);
for (size_t i = 0; i < size; ++i)
Float64 value = block.getByPosition(arguments[0]).column->getFloat64(i);
bool is_negative = value < 0;
if (is_negative)
{
formatReadableTimeDelta(static_cast<double>(vec_from[i]), maximum_unit, buf_to);
writeChar(0, buf_to);
offsets_to[i] = buf_to.count();
writeChar('-', buf_to);
value = -value;
}
buf_to.finalize();
block.getByPosition(result).column = std::move(col_to);
return true;
bool has_output = false;
switch (max_unit)
{
case Years: processUnit(365 * 24 * 3600, " year", 5, value, buf_to, has_output); [[fallthrough]];
case Months: processUnit(30.5 * 24 * 3600, " month", 6, value, buf_to, has_output); [[fallthrough]];
case Days: processUnit(24 * 3600, " day", 4, value, buf_to, has_output); [[fallthrough]];
case Hours: processUnit(3600, " hour", 5, value, buf_to, has_output); [[fallthrough]];
case Minutes: processUnit(60, " minute", 7, value, buf_to, has_output); [[fallthrough]];
case Seconds: processUnit(1, " second", 7, value, buf_to, has_output);
}
writeChar(0, buf_to);
offsets_to[i] = buf_to.count();
}
return false;
buf_to.finalize();
block.getByPosition(result).column = std::move(col_to);
}
static void processUnit(
UInt64 unit_size, const char * unit_name, size_t unit_name_size,
Float64 & value, WriteBuffer & buf_to, bool & has_output)
{
if (unlikely(value + 1.0 == value))
{
writeText(std::floor(value / unit_size), buf_to);
buf_to.write(unit_name, unit_name_size);
writeChar('s', buf_to);
has_output = true;
value = 0;
return;
}
UInt64 num_units = value / unit_size;
if (!num_units)
{
if (unit_size > 1 || has_output)
return;
}
value -= num_units * unit_size;
if (has_output)
{
if (value < 1)
writeCString(" and ", buf_to);
else
writeCString(", ", buf_to);
}
writeText(num_units, buf_to);
buf_to.write(unit_name, unit_name_size);
if (num_units != 1)
writeChar('s', buf_to);
has_output = true;
}
};