mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-22 23:52:03 +00:00
Merge pull request #51367 from ClickHouse/remove-wrong-code
Attempt to remove wrong code (catch/throw in Functions)
This commit is contained in:
commit
fbcc06685b
@ -1183,15 +1183,9 @@ public:
|
||||
|| (left_tuple && right_tuple && left_tuple->getElements().size() == right_tuple->getElements().size())
|
||||
|| (arguments[0]->equals(*arguments[1]))))
|
||||
{
|
||||
try
|
||||
{
|
||||
getLeastSupertype(arguments);
|
||||
}
|
||||
catch (const Exception &)
|
||||
{
|
||||
if (!tryGetLeastSupertype(arguments))
|
||||
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal types of arguments ({}, {})"
|
||||
" of function {}", arguments[0]->getName(), arguments[1]->getName(), getName());
|
||||
}
|
||||
}
|
||||
|
||||
if (left_tuple && right_tuple)
|
||||
|
376
src/Functions/GregorianDate.cpp
Normal file
376
src/Functions/GregorianDate.cpp
Normal file
@ -0,0 +1,376 @@
|
||||
#include <Functions/GregorianDate.h>
|
||||
|
||||
#include <Common/Exception.h>
|
||||
#include <IO/ReadBuffer.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
#include <IO/WriteBufferFromString.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int CANNOT_PARSE_INPUT_ASSERTION_FAILED;
|
||||
extern const int CANNOT_PARSE_DATE;
|
||||
extern const int CANNOT_FORMAT_DATETIME;
|
||||
extern const int LOGICAL_ERROR;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
inline constexpr bool is_leap_year(int32_t year)
|
||||
{
|
||||
return (year % 4 == 0) && ((year % 400 == 0) || (year % 100 != 0));
|
||||
}
|
||||
|
||||
inline constexpr uint8_t monthLength(bool is_leap_year, uint8_t month)
|
||||
{
|
||||
switch (month)
|
||||
{
|
||||
case 1: return 31;
|
||||
case 2: return is_leap_year ? 29 : 28;
|
||||
case 3: return 31;
|
||||
case 4: return 30;
|
||||
case 5: return 31;
|
||||
case 6: return 30;
|
||||
case 7: return 31;
|
||||
case 8: return 31;
|
||||
case 9: return 30;
|
||||
case 10: return 31;
|
||||
case 11: return 30;
|
||||
case 12: return 31;
|
||||
default:
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
|
||||
/** Integer division truncated toward negative infinity.
|
||||
*/
|
||||
template <typename I, typename J>
|
||||
inline constexpr I div(I x, J y)
|
||||
{
|
||||
const auto y_cast = static_cast<I>(y);
|
||||
if (x > 0 && y_cast < 0)
|
||||
return ((x - 1) / y_cast) - 1;
|
||||
else if (x < 0 && y_cast > 0)
|
||||
return ((x + 1) / y_cast) - 1;
|
||||
else
|
||||
return x / y_cast;
|
||||
}
|
||||
|
||||
/** Integer modulus, satisfying div(x, y)*y + mod(x, y) == x.
|
||||
*/
|
||||
template <typename I, typename J>
|
||||
inline constexpr I mod(I x, J y)
|
||||
{
|
||||
const auto y_cast = static_cast<I>(y);
|
||||
const auto r = x % y_cast;
|
||||
if ((x > 0 && y_cast < 0) || (x < 0 && y_cast > 0))
|
||||
return r == 0 ? static_cast<I>(0) : r + y_cast;
|
||||
else
|
||||
return r;
|
||||
}
|
||||
|
||||
/** Like std::min(), but the type of operands may differ.
|
||||
*/
|
||||
template <typename I, typename J>
|
||||
inline constexpr I min(I x, J y)
|
||||
{
|
||||
const auto y_cast = static_cast<I>(y);
|
||||
return x < y_cast ? x : y_cast;
|
||||
}
|
||||
|
||||
inline char readDigit(ReadBuffer & in)
|
||||
{
|
||||
char c;
|
||||
if (!in.read(c))
|
||||
throw Exception(ErrorCodes::CANNOT_PARSE_INPUT_ASSERTION_FAILED, "Cannot parse input: expected a digit at the end of stream");
|
||||
else if (c < '0' || c > '9')
|
||||
throw Exception(ErrorCodes::CANNOT_PARSE_INPUT_ASSERTION_FAILED, "Cannot read input: expected a digit but got something else");
|
||||
else
|
||||
return c - '0';
|
||||
}
|
||||
|
||||
inline bool tryReadDigit(ReadBuffer & in, char & c)
|
||||
{
|
||||
if (in.read(c) && c >= '0' && c <= '9')
|
||||
{
|
||||
c -= '0';
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void GregorianDate::init(ReadBuffer & in)
|
||||
{
|
||||
year_ = readDigit(in) * 1000
|
||||
+ readDigit(in) * 100
|
||||
+ readDigit(in) * 10
|
||||
+ readDigit(in);
|
||||
|
||||
assertChar('-', in);
|
||||
|
||||
month_ = readDigit(in) * 10
|
||||
+ readDigit(in);
|
||||
|
||||
assertChar('-', in);
|
||||
|
||||
day_of_month_ = readDigit(in) * 10
|
||||
+ readDigit(in);
|
||||
|
||||
assertEOF(in);
|
||||
|
||||
if (month_ < 1 || month_ > 12 || day_of_month_ < 1 || day_of_month_ > monthLength(is_leap_year(year_), month_))
|
||||
throw Exception(ErrorCodes::CANNOT_PARSE_DATE, "Invalid date, out of range (year: {}, month: {}, day_of_month: {}).");
|
||||
}
|
||||
|
||||
bool GregorianDate::tryInit(ReadBuffer & in)
|
||||
{
|
||||
char c[8];
|
||||
|
||||
if ( !tryReadDigit(in, c[0])
|
||||
|| !tryReadDigit(in, c[1])
|
||||
|| !tryReadDigit(in, c[2])
|
||||
|| !tryReadDigit(in, c[3])
|
||||
|| !checkChar('-', in)
|
||||
|| !tryReadDigit(in, c[4])
|
||||
|| !tryReadDigit(in, c[5])
|
||||
|| !checkChar('-', in)
|
||||
|| !tryReadDigit(in, c[6])
|
||||
|| !tryReadDigit(in, c[7])
|
||||
|| !in.eof())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
year_ = c[0] * 1000 + c[1] * 100 + c[2] * 10 + c[3];
|
||||
month_ = c[4] * 10 + c[5];
|
||||
day_of_month_ = c[6] * 10 + c[7];
|
||||
|
||||
if (month_ < 1 || month_ > 12 || day_of_month_ < 1 || day_of_month_ > monthLength(is_leap_year(year_), month_))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GregorianDate::GregorianDate(ReadBuffer & in)
|
||||
{
|
||||
init(in);
|
||||
}
|
||||
|
||||
void GregorianDate::init(int64_t modified_julian_day)
|
||||
{
|
||||
const OrdinalDate ord(modified_julian_day);
|
||||
const MonthDay md(is_leap_year(ord.year()), ord.dayOfYear());
|
||||
|
||||
year_ = ord.year();
|
||||
month_ = md.month();
|
||||
day_of_month_ = md.dayOfMonth();
|
||||
}
|
||||
|
||||
bool GregorianDate::tryInit(int64_t modified_julian_day)
|
||||
{
|
||||
OrdinalDate ord;
|
||||
if (!ord.tryInit(modified_julian_day))
|
||||
return false;
|
||||
|
||||
MonthDay md(is_leap_year(ord.year()), ord.dayOfYear());
|
||||
|
||||
year_ = ord.year();
|
||||
month_ = md.month();
|
||||
day_of_month_ = md.dayOfMonth();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GregorianDate::GregorianDate(int64_t modified_julian_day)
|
||||
{
|
||||
init(modified_julian_day);
|
||||
}
|
||||
|
||||
int64_t GregorianDate::toModifiedJulianDay() const
|
||||
{
|
||||
const MonthDay md(month_, day_of_month_);
|
||||
|
||||
const auto day_of_year = md.dayOfYear(is_leap_year(year_));
|
||||
|
||||
const OrdinalDate ord(year_, day_of_year);
|
||||
return ord.toModifiedJulianDay();
|
||||
}
|
||||
|
||||
bool GregorianDate::tryToModifiedJulianDay(int64_t & res) const
|
||||
{
|
||||
const MonthDay md(month_, day_of_month_);
|
||||
const auto day_of_year = md.dayOfYear(is_leap_year(year_));
|
||||
OrdinalDate ord;
|
||||
|
||||
if (!ord.tryInit(year_, day_of_year))
|
||||
return false;
|
||||
|
||||
res = ord.toModifiedJulianDay();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ReturnType>
|
||||
ReturnType GregorianDate::writeImpl(WriteBuffer & buf) const
|
||||
{
|
||||
if (year_ < 0 || year_ > 9999)
|
||||
{
|
||||
if constexpr (std::is_same_v<ReturnType, void>)
|
||||
throw Exception(ErrorCodes::CANNOT_FORMAT_DATETIME,
|
||||
"Impossible to stringify: year too big or small: {}", year_);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto y = year_;
|
||||
writeChar('0' + y / 1000, buf); y %= 1000;
|
||||
writeChar('0' + y / 100, buf); y %= 100;
|
||||
writeChar('0' + y / 10, buf); y %= 10;
|
||||
writeChar('0' + y , buf);
|
||||
|
||||
writeChar('-', buf);
|
||||
|
||||
auto m = month_;
|
||||
writeChar('0' + m / 10, buf); m %= 10;
|
||||
writeChar('0' + m , buf);
|
||||
|
||||
writeChar('-', buf);
|
||||
|
||||
auto d = day_of_month_;
|
||||
writeChar('0' + d / 10, buf); d %= 10;
|
||||
writeChar('0' + d , buf);
|
||||
}
|
||||
|
||||
return ReturnType(true);
|
||||
}
|
||||
|
||||
std::string GregorianDate::toString() const
|
||||
{
|
||||
WriteBufferFromOwnString buf;
|
||||
write(buf);
|
||||
return buf.str();
|
||||
}
|
||||
|
||||
void OrdinalDate::init(int32_t year, uint16_t day_of_year)
|
||||
{
|
||||
year_ = year;
|
||||
day_of_year_ = day_of_year;
|
||||
|
||||
if (day_of_year < 1 || day_of_year > (is_leap_year(year) ? 366 : 365))
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Invalid ordinal date: {}-{}", year, day_of_year);
|
||||
}
|
||||
|
||||
bool OrdinalDate::tryInit(int32_t year, uint16_t day_of_year)
|
||||
{
|
||||
year_ = year;
|
||||
day_of_year_ = day_of_year;
|
||||
|
||||
return !(day_of_year < 1 || day_of_year > (is_leap_year(year) ? 366 : 365));
|
||||
}
|
||||
|
||||
void OrdinalDate::init(int64_t modified_julian_day)
|
||||
{
|
||||
if (!tryInit(modified_julian_day))
|
||||
throw Exception(
|
||||
ErrorCodes::CANNOT_FORMAT_DATETIME,
|
||||
"Value cannot be represented as date because it's out of range");
|
||||
}
|
||||
|
||||
bool OrdinalDate::tryInit(int64_t modified_julian_day)
|
||||
{
|
||||
/// This function supports day number from -678941 to 2973119 (which represent 0000-01-01 and 9999-12-31 respectively).
|
||||
|
||||
if (modified_julian_day < -678941)
|
||||
return false;
|
||||
|
||||
if (modified_julian_day > 2973119)
|
||||
return false;
|
||||
|
||||
const auto a = modified_julian_day + 678575;
|
||||
const auto quad_cent = div(a, 146097);
|
||||
const auto b = mod(a, 146097);
|
||||
const auto cent = min(div(b, 36524), 3);
|
||||
const auto c = b - cent * 36524;
|
||||
const auto quad = div(c, 1461);
|
||||
const auto d = mod(c, 1461);
|
||||
const auto y = min(div(d, 365), 3);
|
||||
|
||||
day_of_year_ = d - y * 365 + 1;
|
||||
year_ = static_cast<int32_t>(quad_cent * 400 + cent * 100 + quad * 4 + y + 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
OrdinalDate::OrdinalDate(int32_t year, uint16_t day_of_year)
|
||||
{
|
||||
init(year, day_of_year);
|
||||
}
|
||||
|
||||
OrdinalDate::OrdinalDate(int64_t modified_julian_day)
|
||||
{
|
||||
init(modified_julian_day);
|
||||
}
|
||||
|
||||
int64_t OrdinalDate::toModifiedJulianDay() const noexcept
|
||||
{
|
||||
const auto y = year_ - 1;
|
||||
|
||||
return day_of_year_
|
||||
+ 365 * y
|
||||
+ div(y, 4)
|
||||
- div(y, 100)
|
||||
+ div(y, 400)
|
||||
- 678576;
|
||||
}
|
||||
|
||||
MonthDay::MonthDay(uint8_t month, uint8_t day_of_month)
|
||||
: month_(month)
|
||||
, day_of_month_(day_of_month)
|
||||
{
|
||||
if (month < 1 || month > 12)
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Invalid month: {}", month);
|
||||
/* We can't validate day_of_month here, because we don't know if
|
||||
* it's a leap year. */
|
||||
}
|
||||
|
||||
MonthDay::MonthDay(bool is_leap_year, uint16_t day_of_year)
|
||||
{
|
||||
if (day_of_year < 1 || day_of_year > (is_leap_year ? 366 : 365))
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Invalid day of year: {}{}",
|
||||
(is_leap_year ? "leap, " : "non-leap, "), day_of_year);
|
||||
|
||||
month_ = 1;
|
||||
uint16_t d = day_of_year;
|
||||
while (true)
|
||||
{
|
||||
const auto len = monthLength(is_leap_year, month_);
|
||||
if (d <= len)
|
||||
break;
|
||||
++month_;
|
||||
d -= len;
|
||||
}
|
||||
day_of_month_ = d;
|
||||
}
|
||||
|
||||
uint16_t MonthDay::dayOfYear(bool is_leap_year) const
|
||||
{
|
||||
if (day_of_month_ < 1 || day_of_month_ > monthLength(is_leap_year, month_))
|
||||
{
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Invalid day of month: {}{}-{}",
|
||||
(is_leap_year ? "leap, " : "non-leap, "), month_, day_of_month_);
|
||||
}
|
||||
const auto k = month_ <= 2 ? 0 : is_leap_year ? -1 :-2;
|
||||
return (367 * month_ - 362) / 12 + k + day_of_month_;
|
||||
}
|
||||
|
||||
template void GregorianDate::writeImpl<void>(WriteBuffer & buf) const;
|
||||
template bool GregorianDate::writeImpl<bool>(WriteBuffer & buf) const;
|
||||
|
||||
}
|
@ -1,408 +1,155 @@
|
||||
#pragma once
|
||||
|
||||
#include <base/extended_types.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <Core/Types.h>
|
||||
#include <IO/ReadBuffer.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
#include <IO/WriteBufferFromString.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int CANNOT_PARSE_INPUT_ASSERTION_FAILED;
|
||||
extern const int CANNOT_PARSE_DATE;
|
||||
extern const int CANNOT_FORMAT_DATETIME;
|
||||
extern const int LOGICAL_ERROR;
|
||||
}
|
||||
|
||||
/** Proleptic Gregorian calendar date. YearT is an integral type
|
||||
class ReadBuffer;
|
||||
class WriteBuffer;
|
||||
|
||||
/// Proleptic Gregorian calendar date.
|
||||
class GregorianDate
|
||||
{
|
||||
public:
|
||||
GregorianDate() {}
|
||||
|
||||
void init(ReadBuffer & in);
|
||||
bool tryInit(ReadBuffer & in);
|
||||
|
||||
/** Construct from date in text form 'YYYY-MM-DD' by reading from
|
||||
* ReadBuffer.
|
||||
*/
|
||||
explicit GregorianDate(ReadBuffer & in);
|
||||
|
||||
void init(int64_t modified_julian_day);
|
||||
bool tryInit(int64_t modified_julian_day);
|
||||
|
||||
/** Construct from Modified Julian Day. The type T is an
|
||||
* integral type which should be at least 32 bits wide, and
|
||||
* should preferably signed.
|
||||
*/
|
||||
explicit GregorianDate(int64_t modified_julian_day);
|
||||
|
||||
/** Convert to Modified Julian Day. The type T is an integral type
|
||||
* which should be at least 32 bits wide, and should preferably
|
||||
* be signed.
|
||||
*/
|
||||
template <typename YearT = int32_t>
|
||||
class GregorianDate
|
||||
* signed.
|
||||
*/
|
||||
int64_t toModifiedJulianDay() const;
|
||||
bool tryToModifiedJulianDay(int64_t & res) const;
|
||||
|
||||
/** Write the date in text form 'YYYY-MM-DD' to a buffer.
|
||||
*/
|
||||
void write(WriteBuffer & buf) const
|
||||
{
|
||||
public:
|
||||
/** Construct from date in text form 'YYYY-MM-DD' by reading from
|
||||
* ReadBuffer.
|
||||
*/
|
||||
explicit GregorianDate(ReadBuffer & in);
|
||||
writeImpl<void>(buf);
|
||||
}
|
||||
|
||||
/** Construct from Modified Julian Day. The type T is an
|
||||
* integral type which should be at least 32 bits wide, and
|
||||
* should preferably signed.
|
||||
*/
|
||||
explicit GregorianDate(is_integer auto modified_julian_day);
|
||||
|
||||
/** Convert to Modified Julian Day. The type T is an integral type
|
||||
* which should be at least 32 bits wide, and should preferably
|
||||
* signed.
|
||||
*/
|
||||
template <is_integer T>
|
||||
T toModifiedJulianDay() const;
|
||||
|
||||
/** Write the date in text form 'YYYY-MM-DD' to a buffer.
|
||||
*/
|
||||
void write(WriteBuffer & buf) const;
|
||||
|
||||
/** Convert to a string in text form 'YYYY-MM-DD'.
|
||||
*/
|
||||
std::string toString() const;
|
||||
|
||||
YearT year() const noexcept
|
||||
{
|
||||
return year_;
|
||||
}
|
||||
|
||||
uint8_t month() const noexcept
|
||||
{
|
||||
return month_;
|
||||
}
|
||||
|
||||
uint8_t day_of_month() const noexcept /// NOLINT
|
||||
{
|
||||
return day_of_month_;
|
||||
}
|
||||
|
||||
private:
|
||||
YearT year_; /// NOLINT
|
||||
uint8_t month_; /// NOLINT
|
||||
uint8_t day_of_month_; /// NOLINT
|
||||
};
|
||||
|
||||
/** ISO 8601 Ordinal Date. YearT is an integral type which should
|
||||
* be at least 32 bits wide, and should preferably signed.
|
||||
*/
|
||||
template <typename YearT = int32_t>
|
||||
class OrdinalDate
|
||||
bool tryWrite(WriteBuffer & buf) const
|
||||
{
|
||||
public:
|
||||
OrdinalDate(YearT year, uint16_t day_of_year);
|
||||
return writeImpl<bool>(buf);
|
||||
}
|
||||
|
||||
/** Construct from Modified Julian Day. The type T is an
|
||||
* integral type which should be at least 32 bits wide, and
|
||||
* should preferably signed.
|
||||
*/
|
||||
template <is_integer DayT>
|
||||
explicit OrdinalDate(DayT modified_julian_day);
|
||||
/** Convert to a string in text form 'YYYY-MM-DD'.
|
||||
*/
|
||||
std::string toString() const;
|
||||
|
||||
/** Convert to Modified Julian Day. The type T is an integral
|
||||
* type which should be at least 32 bits wide, and should
|
||||
* preferably be signed.
|
||||
*/
|
||||
template <is_integer T>
|
||||
T toModifiedJulianDay() const noexcept;
|
||||
|
||||
YearT year() const noexcept
|
||||
{
|
||||
return year_;
|
||||
}
|
||||
|
||||
uint16_t dayOfYear() const noexcept
|
||||
{
|
||||
return day_of_year_;
|
||||
}
|
||||
|
||||
private:
|
||||
YearT year_; /// NOLINT
|
||||
uint16_t day_of_year_; /// NOLINT
|
||||
};
|
||||
|
||||
class MonthDay
|
||||
int32_t year() const noexcept
|
||||
{
|
||||
public:
|
||||
/** Construct from month and day. */
|
||||
MonthDay(uint8_t month, uint8_t day_of_month);
|
||||
return year_;
|
||||
}
|
||||
|
||||
/** Construct from day of year in Gregorian or Julian
|
||||
* calendars to month and day.
|
||||
*/
|
||||
MonthDay(bool is_leap_year, uint16_t day_of_year);
|
||||
uint8_t month() const noexcept
|
||||
{
|
||||
return month_;
|
||||
}
|
||||
|
||||
/** Convert month and day in Gregorian or Julian calendars to
|
||||
* day of year.
|
||||
*/
|
||||
uint16_t dayOfYear(bool is_leap_year) const;
|
||||
uint8_t dayOfMonth() const noexcept
|
||||
{
|
||||
return day_of_month_;
|
||||
}
|
||||
|
||||
uint8_t month() const noexcept
|
||||
{
|
||||
return month_;
|
||||
}
|
||||
private:
|
||||
int32_t year_ = 0;
|
||||
uint8_t month_ = 0;
|
||||
uint8_t day_of_month_ = 0;
|
||||
|
||||
uint8_t day_of_month() const noexcept /// NOLINT
|
||||
{
|
||||
return day_of_month_;
|
||||
}
|
||||
template <typename ReturnType>
|
||||
ReturnType writeImpl(WriteBuffer & buf) const;
|
||||
};
|
||||
|
||||
private:
|
||||
uint8_t month_; /// NOLINT
|
||||
uint8_t day_of_month_; /// NOLINT
|
||||
};
|
||||
}
|
||||
|
||||
/* Implementation */
|
||||
|
||||
namespace gd
|
||||
/** ISO 8601 Ordinal Date.
|
||||
*/
|
||||
class OrdinalDate
|
||||
{
|
||||
using namespace DB;
|
||||
public:
|
||||
OrdinalDate() {}
|
||||
|
||||
template <typename YearT>
|
||||
static inline constexpr bool is_leap_year(YearT year)
|
||||
{
|
||||
return (year % 4 == 0) && ((year % 400 == 0) || (year % 100 != 0));
|
||||
}
|
||||
void init(int32_t year, uint16_t day_of_year);
|
||||
bool tryInit(int32_t year, uint16_t day_of_year);
|
||||
|
||||
static inline constexpr uint8_t monthLength(bool is_leap_year, uint8_t month)
|
||||
{
|
||||
switch (month)
|
||||
{
|
||||
case 1: return 31;
|
||||
case 2: return is_leap_year ? 29 : 28;
|
||||
case 3: return 31;
|
||||
case 4: return 30;
|
||||
case 5: return 31;
|
||||
case 6: return 30;
|
||||
case 7: return 31;
|
||||
case 8: return 31;
|
||||
case 9: return 30;
|
||||
case 10: return 31;
|
||||
case 11: return 30;
|
||||
case 12: return 31;
|
||||
default:
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
void init(int64_t modified_julian_day);
|
||||
bool tryInit(int64_t modified_julian_day);
|
||||
|
||||
/** Integer division truncated toward negative infinity.
|
||||
OrdinalDate(int32_t year, uint16_t day_of_year);
|
||||
|
||||
/** Construct from Modified Julian Day. The type T is an
|
||||
* integral type which should be at least 32 bits wide, and
|
||||
* should preferably signed.
|
||||
*/
|
||||
template <typename I, typename J>
|
||||
static inline constexpr I div(I x, J y)
|
||||
{
|
||||
const auto y_cast = static_cast<I>(y);
|
||||
if (x > 0 && y_cast < 0)
|
||||
return ((x - 1) / y_cast) - 1;
|
||||
else if (x < 0 && y_cast > 0)
|
||||
return ((x + 1) / y_cast) - 1;
|
||||
else
|
||||
return x / y_cast;
|
||||
}
|
||||
explicit OrdinalDate(int64_t modified_julian_day);
|
||||
|
||||
/** Integer modulus, satisfying div(x, y)*y + mod(x, y) == x.
|
||||
/** Convert to Modified Julian Day. The type T is an integral
|
||||
* type which should be at least 32 bits wide, and should
|
||||
* preferably be signed.
|
||||
*/
|
||||
template <typename I, typename J>
|
||||
static inline constexpr I mod(I x, J y)
|
||||
int64_t toModifiedJulianDay() const noexcept;
|
||||
|
||||
int32_t year() const noexcept
|
||||
{
|
||||
const auto y_cast = static_cast<I>(y);
|
||||
const auto r = x % y_cast;
|
||||
if ((x > 0 && y_cast < 0) || (x < 0 && y_cast > 0))
|
||||
return r == 0 ? static_cast<I>(0) : r + y_cast;
|
||||
else
|
||||
return r;
|
||||
return year_;
|
||||
}
|
||||
|
||||
/** Like std::min(), but the type of operands may differ.
|
||||
*/
|
||||
template <typename I, typename J>
|
||||
static inline constexpr I min(I x, J y)
|
||||
uint16_t dayOfYear() const noexcept
|
||||
{
|
||||
const auto y_cast = static_cast<I>(y);
|
||||
return x < y_cast ? x : y_cast;
|
||||
return day_of_year_;
|
||||
}
|
||||
|
||||
static inline char readDigit(ReadBuffer & in)
|
||||
{
|
||||
char c;
|
||||
if (!in.read(c))
|
||||
throw Exception(ErrorCodes::CANNOT_PARSE_INPUT_ASSERTION_FAILED, "Cannot parse input: expected a digit at the end of stream");
|
||||
else if (c < '0' || c > '9')
|
||||
throw Exception(ErrorCodes::CANNOT_PARSE_INPUT_ASSERTION_FAILED, "Cannot read input: expected a digit but got something else");
|
||||
else
|
||||
return c - '0';
|
||||
}
|
||||
}
|
||||
private:
|
||||
int32_t year_ = 0;
|
||||
uint16_t day_of_year_ = 0;
|
||||
};
|
||||
|
||||
namespace DB
|
||||
class MonthDay
|
||||
{
|
||||
template <typename YearT>
|
||||
GregorianDate<YearT>::GregorianDate(ReadBuffer & in)
|
||||
public:
|
||||
/** Construct from month and day. */
|
||||
MonthDay(uint8_t month, uint8_t day_of_month);
|
||||
|
||||
/** Construct from day of year in Gregorian or Julian
|
||||
* calendars to month and day.
|
||||
*/
|
||||
MonthDay(bool is_leap_year, uint16_t day_of_year);
|
||||
|
||||
/** Convert month and day in Gregorian or Julian calendars to
|
||||
* day of year.
|
||||
*/
|
||||
uint16_t dayOfYear(bool is_leap_year) const;
|
||||
|
||||
uint8_t month() const noexcept
|
||||
{
|
||||
year_ = gd::readDigit(in) * 1000
|
||||
+ gd::readDigit(in) * 100
|
||||
+ gd::readDigit(in) * 10
|
||||
+ gd::readDigit(in);
|
||||
|
||||
assertChar('-', in);
|
||||
|
||||
month_ = gd::readDigit(in) * 10
|
||||
+ gd::readDigit(in);
|
||||
|
||||
assertChar('-', in);
|
||||
|
||||
day_of_month_ = gd::readDigit(in) * 10
|
||||
+ gd::readDigit(in);
|
||||
|
||||
assertEOF(in);
|
||||
|
||||
if (month_ < 1 || month_ > 12 || day_of_month_ < 1 || day_of_month_ > gd::monthLength(gd::is_leap_year(year_), month_))
|
||||
throw Exception(ErrorCodes::CANNOT_PARSE_DATE, "Invalid date: {}", toString());
|
||||
return month_;
|
||||
}
|
||||
|
||||
template <typename YearT>
|
||||
GregorianDate<YearT>::GregorianDate(is_integer auto modified_julian_day)
|
||||
uint8_t dayOfMonth() const noexcept
|
||||
{
|
||||
const OrdinalDate<YearT> ord(modified_julian_day);
|
||||
const MonthDay md(gd::is_leap_year(ord.year()), ord.dayOfYear());
|
||||
year_ = ord.year();
|
||||
month_ = md.month();
|
||||
day_of_month_ = md.day_of_month();
|
||||
return day_of_month_;
|
||||
}
|
||||
|
||||
template <typename YearT>
|
||||
template <is_integer T>
|
||||
T GregorianDate<YearT>::toModifiedJulianDay() const
|
||||
{
|
||||
const MonthDay md(month_, day_of_month_);
|
||||
const auto day_of_year = md.dayOfYear(gd::is_leap_year(year_));
|
||||
const OrdinalDate<YearT> ord(year_, day_of_year);
|
||||
return ord.template toModifiedJulianDay<T>();
|
||||
}
|
||||
private:
|
||||
uint8_t month_ = 0;
|
||||
uint8_t day_of_month_ = 0;
|
||||
};
|
||||
|
||||
template <typename YearT>
|
||||
void GregorianDate<YearT>::write(WriteBuffer & buf) const
|
||||
{
|
||||
if (year_ < 0 || year_ > 9999)
|
||||
{
|
||||
throw Exception(ErrorCodes::CANNOT_FORMAT_DATETIME,
|
||||
"Impossible to stringify: year too big or small: {}", DB::toString(year_));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto y = year_;
|
||||
writeChar('0' + y / 1000, buf); y %= 1000;
|
||||
writeChar('0' + y / 100, buf); y %= 100;
|
||||
writeChar('0' + y / 10, buf); y %= 10;
|
||||
writeChar('0' + y , buf);
|
||||
|
||||
writeChar('-', buf);
|
||||
|
||||
auto m = month_;
|
||||
writeChar('0' + m / 10, buf); m %= 10;
|
||||
writeChar('0' + m , buf);
|
||||
|
||||
writeChar('-', buf);
|
||||
|
||||
auto d = day_of_month_;
|
||||
writeChar('0' + d / 10, buf); d %= 10;
|
||||
writeChar('0' + d , buf);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename YearT>
|
||||
std::string GregorianDate<YearT>::toString() const
|
||||
{
|
||||
WriteBufferFromOwnString buf;
|
||||
write(buf);
|
||||
return buf.str();
|
||||
}
|
||||
|
||||
template <typename YearT>
|
||||
OrdinalDate<YearT>::OrdinalDate(YearT year, uint16_t day_of_year)
|
||||
: year_(year)
|
||||
, day_of_year_(day_of_year)
|
||||
{
|
||||
if (day_of_year < 1 || day_of_year > (gd::is_leap_year(year) ? 366 : 365))
|
||||
{
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Invalid ordinal date: {}-{}", toString(year), toString(day_of_year));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename YearT>
|
||||
template <is_integer DayT>
|
||||
OrdinalDate<YearT>::OrdinalDate(DayT modified_julian_day)
|
||||
{
|
||||
/// This function supports day number from -678941 to 2973119 (which represent 0000-01-01 and 9999-12-31 respectively).
|
||||
|
||||
if constexpr (is_signed_v<DayT> && std::numeric_limits<DayT>::lowest() < -678941)
|
||||
if (modified_julian_day < -678941)
|
||||
throw Exception(
|
||||
ErrorCodes::CANNOT_FORMAT_DATETIME,
|
||||
"Value cannot be represented as date because it's out of range");
|
||||
|
||||
if constexpr (std::numeric_limits<DayT>::max() > 2973119)
|
||||
if (modified_julian_day > 2973119)
|
||||
throw Exception(
|
||||
ErrorCodes::CANNOT_FORMAT_DATETIME,
|
||||
"Value cannot be represented as date because it's out of range");
|
||||
|
||||
const auto a = modified_julian_day + 678575;
|
||||
const auto quad_cent = gd::div(a, 146097);
|
||||
const auto b = gd::mod(a, 146097);
|
||||
const auto cent = gd::min(gd::div(b, 36524), 3);
|
||||
const auto c = b - cent * 36524;
|
||||
const auto quad = gd::div(c, 1461);
|
||||
const auto d = gd::mod(c, 1461);
|
||||
const auto y = gd::min(gd::div(d, 365), 3);
|
||||
|
||||
day_of_year_ = d - y * 365 + 1;
|
||||
year_ = static_cast<YearT>(quad_cent * 400 + cent * 100 + quad * 4 + y + 1);
|
||||
}
|
||||
|
||||
template <typename YearT>
|
||||
template <is_integer T>
|
||||
T OrdinalDate<YearT>::toModifiedJulianDay() const noexcept
|
||||
{
|
||||
const auto y = year_ - 1;
|
||||
return day_of_year_
|
||||
+ 365 * y
|
||||
+ gd::div(y, 4)
|
||||
- gd::div(y, 100)
|
||||
+ gd::div(y, 400)
|
||||
- 678576;
|
||||
}
|
||||
|
||||
inline MonthDay::MonthDay(uint8_t month, uint8_t day_of_month)
|
||||
: month_(month)
|
||||
, day_of_month_(day_of_month)
|
||||
{
|
||||
if (month < 1 || month > 12)
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Invalid month: {}", DB::toString(month));
|
||||
/* We can't validate day_of_month here, because we don't know if
|
||||
* it's a leap year. */
|
||||
}
|
||||
|
||||
inline MonthDay::MonthDay(bool is_leap_year, uint16_t day_of_year)
|
||||
{
|
||||
if (day_of_year < 1 || day_of_year > (is_leap_year ? 366 : 365))
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Invalid day of year: {}{}",
|
||||
(is_leap_year ? "leap, " : "non-leap, "), DB::toString(day_of_year));
|
||||
|
||||
month_ = 1;
|
||||
uint16_t d = day_of_year;
|
||||
while (true)
|
||||
{
|
||||
const auto len = gd::monthLength(is_leap_year, month_);
|
||||
if (d <= len)
|
||||
break;
|
||||
month_++;
|
||||
d -= len;
|
||||
}
|
||||
day_of_month_ = d;
|
||||
}
|
||||
|
||||
inline uint16_t MonthDay::dayOfYear(bool is_leap_year) const
|
||||
{
|
||||
if (day_of_month_ < 1 || day_of_month_ > gd::monthLength(is_leap_year, month_))
|
||||
{
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Invalid day of month: {}{}-{}",
|
||||
(is_leap_year ? "leap, " : "non-leap, "), DB::toString(month_), DB::toString(day_of_month_));
|
||||
}
|
||||
const auto k = month_ <= 2 ? 0 : is_leap_year ? -1 :-2;
|
||||
return (367 * month_ - 362) / 12 + k + day_of_month_;
|
||||
}
|
||||
}
|
||||
|
@ -13,12 +13,12 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
@ -56,25 +56,14 @@ namespace DB
|
||||
{
|
||||
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;
|
||||
}
|
||||
GregorianDate gd;
|
||||
(*vec_null_map_to)[i] = !(gd.tryInit(vec_from[i]) && gd.tryWrite(write_buffer));
|
||||
writeChar(0, write_buffer);
|
||||
offsets_to[i] = write_buffer.count();
|
||||
}
|
||||
else
|
||||
{
|
||||
const GregorianDate<> gd(vec_from[i]);
|
||||
GregorianDate gd(vec_from[i]);
|
||||
gd.write(write_buffer);
|
||||
writeChar(0, write_buffer);
|
||||
offsets_to[i] = write_buffer.count();
|
||||
|
@ -65,15 +65,7 @@ private:
|
||||
if (!arg_string)
|
||||
return argument.type;
|
||||
|
||||
try
|
||||
{
|
||||
DataTypePtr type = DataTypeFactory::instance().get(arg_string->getDataAt(0).toString());
|
||||
return type;
|
||||
}
|
||||
catch (const DB::Exception &)
|
||||
{
|
||||
return argument.type;
|
||||
}
|
||||
return DataTypeFactory::instance().get(arg_string->getDataAt(0).toString());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -398,7 +398,7 @@ namespace
|
||||
static Int32 daysSinceEpochFromDayOfYear(Int32 year_, Int32 day_of_year_)
|
||||
{
|
||||
if (!isDayOfYearValid(year_, day_of_year_))
|
||||
throw Exception(ErrorCodes::CANNOT_PARSE_DATETIME, "Invalid day of year, year:{} day of year:{}", year_, day_of_year_);
|
||||
throw Exception(ErrorCodes::CANNOT_PARSE_DATETIME, "Invalid day of year, out of range (year: {} day of year: {})", year_, day_of_year_);
|
||||
|
||||
Int32 res = daysSinceEpochFromDate(year_, 1, 1);
|
||||
res += day_of_year_ - 1;
|
||||
@ -408,7 +408,7 @@ namespace
|
||||
static Int32 daysSinceEpochFromDate(Int32 year_, Int32 month_, Int32 day_)
|
||||
{
|
||||
if (!isDateValid(year_, month_, day_))
|
||||
throw Exception(ErrorCodes::CANNOT_PARSE_DATETIME, "Invalid date, year:{} month:{} day:{}", year_, month_, day_);
|
||||
throw Exception(ErrorCodes::CANNOT_PARSE_DATETIME, "Invalid date, out of range (year: {} month: {} day_of_month: {})", year_, month_, day_);
|
||||
|
||||
Int32 res = cumulativeYearDays[year_ - 1970];
|
||||
res += isLeapYear(year_) ? cumulativeLeapDays[month_ - 1] : cumulativeDays[month_ - 1];
|
||||
|
@ -17,8 +17,6 @@ namespace DB
|
||||
{
|
||||
extern const int ILLEGAL_COLUMN;
|
||||
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
||||
extern const int CANNOT_PARSE_INPUT_ASSERTION_FAILED;
|
||||
extern const int CANNOT_PARSE_DATE;
|
||||
}
|
||||
|
||||
template <typename Name, typename ToDataType, bool nullOnErrors>
|
||||
@ -78,27 +76,18 @@ namespace DB
|
||||
|
||||
if constexpr (nullOnErrors)
|
||||
{
|
||||
try
|
||||
{
|
||||
const GregorianDate<> date(read_buffer);
|
||||
vec_to[i] = date.toModifiedJulianDay<typename ToDataType::FieldType>();
|
||||
vec_null_map_to[i] = false;
|
||||
}
|
||||
catch (const Exception & e)
|
||||
{
|
||||
if (e.code() == ErrorCodes::CANNOT_PARSE_INPUT_ASSERTION_FAILED || e.code() == ErrorCodes::CANNOT_PARSE_DATE)
|
||||
{
|
||||
vec_to[i] = static_cast<Int32>(0);
|
||||
vec_null_map_to[i] = true;
|
||||
}
|
||||
else
|
||||
throw;
|
||||
}
|
||||
GregorianDate date;
|
||||
|
||||
int64_t res = 0;
|
||||
bool success = date.tryInit(read_buffer) && date.tryToModifiedJulianDay(res);
|
||||
|
||||
vec_to[i] = static_cast<typename ToDataType::FieldType>(res);
|
||||
vec_null_map_to[i] = !success;
|
||||
}
|
||||
else
|
||||
{
|
||||
const GregorianDate<> date(read_buffer);
|
||||
vec_to[i] = date.toModifiedJulianDay<typename ToDataType::FieldType>();
|
||||
const GregorianDate date(read_buffer);
|
||||
vec_to[i] = static_cast<typename ToDataType::FieldType>(date.toModifiedJulianDay());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include <Columns/ColumnTuple.h>
|
||||
#include <Columns/ColumnVector.h>
|
||||
#include <DataTypes/DataTypeTuple.h>
|
||||
#include <DataTypes/DataTypesNumber.h>
|
||||
#include <Functions/FunctionFactory.h>
|
||||
@ -86,7 +85,7 @@ public:
|
||||
auto plus_elem = plus->build({left_type, right_type});
|
||||
res_type = plus_elem->getResultType();
|
||||
}
|
||||
catch (DB::Exception & e)
|
||||
catch (Exception & e)
|
||||
{
|
||||
e.addMessage("While executing function {} for tuple element {}", getName(), i);
|
||||
throw;
|
||||
|
@ -95,7 +95,7 @@ public:
|
||||
auto elem_func = func->build(ColumnsWithTypeAndName{left, right});
|
||||
types[i] = elem_func->getResultType();
|
||||
}
|
||||
catch (DB::Exception & e)
|
||||
catch (Exception & e)
|
||||
{
|
||||
e.addMessage("While executing function {} for tuple element {}", getName(), i);
|
||||
throw;
|
||||
@ -181,7 +181,7 @@ public:
|
||||
auto elem_negate = negate->build(ColumnsWithTypeAndName{cur});
|
||||
types[i] = elem_negate->getResultType();
|
||||
}
|
||||
catch (DB::Exception & e)
|
||||
catch (Exception & e)
|
||||
{
|
||||
e.addMessage("While executing function {} for tuple element {}", getName(), i);
|
||||
throw;
|
||||
@ -258,7 +258,7 @@ public:
|
||||
auto elem_func = func->build(ColumnsWithTypeAndName{cur, p_column});
|
||||
types[i] = elem_func->getResultType();
|
||||
}
|
||||
catch (DB::Exception & e)
|
||||
catch (Exception & e)
|
||||
{
|
||||
e.addMessage("While executing function {} for tuple element {}", getName(), i);
|
||||
throw;
|
||||
@ -363,7 +363,7 @@ public:
|
||||
auto plus_elem = plus->build({left_type, right_type});
|
||||
res_type = plus_elem->getResultType();
|
||||
}
|
||||
catch (DB::Exception & e)
|
||||
catch (Exception & e)
|
||||
{
|
||||
e.addMessage("While executing function {} for tuple element {}", getName(), i);
|
||||
throw;
|
||||
@ -467,7 +467,7 @@ public:
|
||||
auto plus_elem = plus->build({left, right});
|
||||
res_type = plus_elem->getResultType();
|
||||
}
|
||||
catch (DB::Exception & e)
|
||||
catch (Exception & e)
|
||||
{
|
||||
e.addMessage("While executing function {} for tuple element {}", getName(), i);
|
||||
throw;
|
||||
@ -740,7 +740,7 @@ public:
|
||||
auto plus_elem = plus->build({left_type, right_type});
|
||||
res_type = plus_elem->getResultType();
|
||||
}
|
||||
catch (DB::Exception & e)
|
||||
catch (Exception & e)
|
||||
{
|
||||
e.addMessage("While executing function {} for tuple element {}", getName(), i);
|
||||
throw;
|
||||
@ -842,7 +842,7 @@ public:
|
||||
auto plus_elem = plus->build({left_type, right_type});
|
||||
res_type = plus_elem->getResultType();
|
||||
}
|
||||
catch (DB::Exception & e)
|
||||
catch (Exception & e)
|
||||
{
|
||||
e.addMessage("While executing function {} for tuple element {}", getName(), i);
|
||||
throw;
|
||||
@ -993,7 +993,7 @@ public:
|
||||
auto max_elem = max->build({left_type, right_type});
|
||||
res_type = max_elem->getResultType();
|
||||
}
|
||||
catch (DB::Exception & e)
|
||||
catch (Exception & e)
|
||||
{
|
||||
e.addMessage("While executing function {} for tuple element {}", getName(), i);
|
||||
throw;
|
||||
@ -1103,7 +1103,7 @@ public:
|
||||
auto plus_elem = plus->build({left_type, right_type});
|
||||
res_type = plus_elem->getResultType();
|
||||
}
|
||||
catch (DB::Exception & e)
|
||||
catch (Exception & e)
|
||||
{
|
||||
e.addMessage("While executing function {} for tuple element {}", getName(), i);
|
||||
throw;
|
||||
|
@ -143,6 +143,7 @@ def test_string_functions(start_cluster):
|
||||
"position",
|
||||
"substring",
|
||||
"CAST",
|
||||
"getTypeSerializationStreams",
|
||||
# NOTE: no need to ignore now()/now64() since they will fail because they don't accept any argument
|
||||
# 22.8 Backward Incompatible Change: Extended range of Date32
|
||||
"toDate32OrZero",
|
||||
|
Loading…
Reference in New Issue
Block a user