Merge branch 'master' of https://github.com/ClickHouse/ClickHouse into support-apple-m1

This commit is contained in:
changvvb 2021-03-19 13:52:09 +08:00
commit b4e92f6b12
273 changed files with 4890 additions and 2667 deletions

View File

@ -1,4 +1,4 @@
## ClickHouse release 21.3 ## ClickHouse release 21.3 (LTS)
### ClickHouse release v21.3, 2021-03-12 ### ClickHouse release v21.3, 2021-03-12

View File

@ -152,7 +152,7 @@ const DateLUTImpl & DateLUT::getImplementation(const std::string & time_zone) co
auto it = impls.emplace(time_zone, nullptr).first; auto it = impls.emplace(time_zone, nullptr).first;
if (!it->second) if (!it->second)
it->second = std::make_unique<DateLUTImpl>(time_zone); it->second = std::unique_ptr<DateLUTImpl>(new DateLUTImpl(time_zone));
return *it->second; return *it->second;
} }

View File

@ -32,7 +32,6 @@ public:
return date_lut.getImplementation(time_zone); return date_lut.getImplementation(time_zone);
} }
static void setDefaultTimezone(const std::string & time_zone) static void setDefaultTimezone(const std::string & time_zone)
{ {
auto & date_lut = getInstance(); auto & date_lut = getInstance();

View File

@ -46,24 +46,41 @@ DateLUTImpl::DateLUTImpl(const std::string & time_zone_)
if (&inside_main) if (&inside_main)
assert(inside_main); assert(inside_main);
size_t i = 0;
time_t start_of_day = 0;
cctz::time_zone cctz_time_zone; cctz::time_zone cctz_time_zone;
if (!cctz::load_time_zone(time_zone, &cctz_time_zone)) if (!cctz::load_time_zone(time_zone, &cctz_time_zone))
throw Poco::Exception("Cannot load time zone " + time_zone_); throw Poco::Exception("Cannot load time zone " + time_zone_);
cctz::time_zone::absolute_lookup start_of_epoch_lookup = cctz_time_zone.lookup(std::chrono::system_clock::from_time_t(start_of_day)); constexpr cctz::civil_day epoch{1970, 1, 1};
offset_at_start_of_epoch = start_of_epoch_lookup.offset; constexpr cctz::civil_day lut_start{DATE_LUT_MIN_YEAR, 1, 1};
offset_is_whole_number_of_hours_everytime = true; time_t start_of_day;
cctz::civil_day date{1970, 1, 1}; /// Note: it's validated against all timezones in the system.
static_assert((epoch - lut_start) == daynum_offset_epoch);
offset_at_start_of_epoch = cctz_time_zone.lookup(cctz_time_zone.lookup(epoch).pre).offset;
offset_at_start_of_lut = cctz_time_zone.lookup(cctz_time_zone.lookup(lut_start).pre).offset;
offset_is_whole_number_of_hours_during_epoch = true;
cctz::civil_day date = lut_start;
UInt32 i = 0;
do do
{ {
cctz::time_zone::civil_lookup lookup = cctz_time_zone.lookup(date); cctz::time_zone::civil_lookup lookup = cctz_time_zone.lookup(date);
start_of_day = std::chrono::system_clock::to_time_t(lookup.pre); /// Ambiguity is possible. /// Ambiguity is possible if time was changed backwards at the midnight
/// or after midnight time has been changed back to midnight, for example one hour backwards at 01:00
/// or after midnight time has been changed to the previous day, for example two hours backwards at 01:00
/// Then midnight appears twice. Usually time change happens exactly at 00:00 or 01:00.
/// If transition did not involve previous day, we should use the first midnight as the start of the day,
/// otherwise it's better to use the second midnight.
std::chrono::time_point start_of_day_time_point = lookup.trans < lookup.post
? lookup.post /* Second midnight appears after transition, so there was a piece of previous day after transition */
: lookup.pre;
start_of_day = std::chrono::system_clock::to_time_t(start_of_day_time_point);
Values & values = lut[i]; Values & values = lut[i];
values.year = date.year(); values.year = date.year();
@ -72,7 +89,7 @@ DateLUTImpl::DateLUTImpl(const std::string & time_zone_)
values.day_of_week = getDayOfWeek(date); values.day_of_week = getDayOfWeek(date);
values.date = start_of_day; values.date = start_of_day;
assert(values.year >= DATE_LUT_MIN_YEAR && values.year <= DATE_LUT_MAX_YEAR); assert(values.year >= DATE_LUT_MIN_YEAR && values.year <= DATE_LUT_MAX_YEAR + 1);
assert(values.month >= 1 && values.month <= 12); assert(values.month >= 1 && values.month <= 12);
assert(values.day_of_month >= 1 && values.day_of_month <= 31); assert(values.day_of_month >= 1 && values.day_of_month <= 31);
assert(values.day_of_week >= 1 && values.day_of_week <= 7); assert(values.day_of_week >= 1 && values.day_of_week <= 7);
@ -85,50 +102,42 @@ DateLUTImpl::DateLUTImpl(const std::string & time_zone_)
else else
values.days_in_month = i != 0 ? lut[i - 1].days_in_month : 31; values.days_in_month = i != 0 ? lut[i - 1].days_in_month : 31;
values.time_at_offset_change = 0; values.time_at_offset_change_value = 0;
values.amount_of_offset_change = 0; values.amount_of_offset_change_value = 0;
if (start_of_day % 3600) if (offset_is_whole_number_of_hours_during_epoch && start_of_day > 0 && start_of_day % 3600)
offset_is_whole_number_of_hours_everytime = false; offset_is_whole_number_of_hours_during_epoch = false;
/// If UTC offset was changed in previous day. /// If UTC offset was changed this day.
if (i != 0) /// Change in time zone without transition is possible, e.g. Moscow 1991 Sun, 31 Mar, 02:00 MSK to EEST
cctz::time_zone::civil_transition transition{};
if (cctz_time_zone.next_transition(start_of_day_time_point - std::chrono::seconds(1), &transition)
&& (cctz::civil_day(transition.from) == date || cctz::civil_day(transition.to) == date)
&& transition.from != transition.to)
{ {
auto amount_of_offset_change_at_prev_day = 86400 - (lut[i].date - lut[i - 1].date); values.time_at_offset_change_value = (transition.from - cctz::civil_second(date)) / Values::OffsetChangeFactor;
if (amount_of_offset_change_at_prev_day) values.amount_of_offset_change_value = (transition.to - transition.from) / Values::OffsetChangeFactor;
{
lut[i - 1].amount_of_offset_change = amount_of_offset_change_at_prev_day;
const auto utc_offset_at_beginning_of_day = cctz_time_zone.lookup(std::chrono::system_clock::from_time_t(lut[i - 1].date)).offset; // std::cerr << time_zone << ", " << date << ": change from " << transition.from << " to " << transition.to << "\n";
// std::cerr << time_zone << ", " << date << ": change at " << values.time_at_offset_change() << " with " << values.amount_of_offset_change() << "\n";
/// Find a time (timestamp offset from beginning of day), /// We don't support too large changes.
/// when UTC offset was changed. Search is performed with 15-minute granularity, assuming it is enough. if (values.amount_of_offset_change_value > 24 * 4)
values.amount_of_offset_change_value = 24 * 4;
else if (values.amount_of_offset_change_value < -24 * 4)
values.amount_of_offset_change_value = -24 * 4;
time_t time_at_offset_change = 900; /// We don't support cases when time change results in switching to previous day.
while (time_at_offset_change < 86400) /// Shift the point of time change later.
{ if (values.time_at_offset_change_value + values.amount_of_offset_change_value < 0)
auto utc_offset_at_current_time = cctz_time_zone.lookup(std::chrono::system_clock::from_time_t( values.time_at_offset_change_value = -values.amount_of_offset_change_value;
lut[i - 1].date + time_at_offset_change)).offset;
if (utc_offset_at_current_time != utc_offset_at_beginning_of_day)
break;
time_at_offset_change += 900;
}
lut[i - 1].time_at_offset_change = time_at_offset_change;
/// We doesn't support cases when time change results in switching to previous day.
if (static_cast<int>(lut[i - 1].time_at_offset_change) + static_cast<int>(lut[i - 1].amount_of_offset_change) < 0)
lut[i - 1].time_at_offset_change = -lut[i - 1].amount_of_offset_change;
}
} }
/// Going to next day. /// Going to next day.
++date; ++date;
++i; ++i;
} }
while (start_of_day <= DATE_LUT_MAX && i <= DATE_LUT_MAX_DAY_NUM); while (i < DATE_LUT_SIZE && lut[i - 1].year <= DATE_LUT_MAX_YEAR);
/// Fill excessive part of lookup table. This is needed only to simplify handling of overflow cases. /// Fill excessive part of lookup table. This is needed only to simplify handling of overflow cases.
while (i < DATE_LUT_SIZE) while (i < DATE_LUT_SIZE)

File diff suppressed because it is too large Load Diff

View File

@ -7,3 +7,8 @@
* See DateLUTImpl for usage examples. * See DateLUTImpl for usage examples.
*/ */
STRONG_TYPEDEF(UInt16, DayNum) STRONG_TYPEDEF(UInt16, DayNum)
/** Represent number of days since 1970-01-01 but in extended range,
* for dates before 1970-01-01 and after 2105
*/
STRONG_TYPEDEF(Int32, ExtendedDayNum)

View File

@ -92,20 +92,10 @@ public:
LocalDate(const LocalDate &) noexcept = default; LocalDate(const LocalDate &) noexcept = default;
LocalDate & operator= (const LocalDate &) noexcept = default; LocalDate & operator= (const LocalDate &) noexcept = default;
LocalDate & operator= (time_t time)
{
init(time);
return *this;
}
operator time_t() const
{
return DateLUT::instance().makeDate(m_year, m_month, m_day);
}
DayNum getDayNum() const DayNum getDayNum() const
{ {
return DateLUT::instance().makeDayNum(m_year, m_month, m_day); const auto & lut = DateLUT::instance();
return DayNum(lut.makeDayNum(m_year, m_month, m_day).toUnderType());
} }
operator DayNum() const operator DayNum() const
@ -166,12 +156,3 @@ public:
}; };
static_assert(sizeof(LocalDate) == 4); static_assert(sizeof(LocalDate) == 4);
namespace std
{
inline string to_string(const LocalDate & date)
{
return date.toString();
}
}

View File

@ -29,29 +29,16 @@ private:
/// NOTE We may use attribute packed instead, but it is less portable. /// NOTE We may use attribute packed instead, but it is less portable.
unsigned char pad = 0; unsigned char pad = 0;
void init(time_t time) void init(time_t time, const DateLUTImpl & time_zone)
{ {
if (unlikely(time > DATE_LUT_MAX || time == 0)) DateLUTImpl::DateTimeComponents components = time_zone.toDateTimeComponents(time);
{
m_year = 0;
m_month = 0;
m_day = 0;
m_hour = 0;
m_minute = 0;
m_second = 0;
return; m_year = components.date.year;
} m_month = components.date.month;
m_day = components.date.day;
const auto & date_lut = DateLUT::instance(); m_hour = components.time.hour;
const auto & values = date_lut.getValues(time); m_minute = components.time.minute;
m_second = components.time.second;
m_year = values.year;
m_month = values.month;
m_day = values.day_of_month;
m_hour = date_lut.toHour(time);
m_minute = date_lut.toMinute(time);
m_second = date_lut.toSecond(time);
(void)pad; /// Suppress unused private field warning. (void)pad; /// Suppress unused private field warning.
} }
@ -73,9 +60,9 @@ private:
} }
public: public:
explicit LocalDateTime(time_t time) explicit LocalDateTime(time_t time, const DateLUTImpl & time_zone = DateLUT::instance())
{ {
init(time); init(time, time_zone);
} }
LocalDateTime(unsigned short year_, unsigned char month_, unsigned char day_, LocalDateTime(unsigned short year_, unsigned char month_, unsigned char day_,
@ -104,19 +91,6 @@ public:
LocalDateTime(const LocalDateTime &) noexcept = default; LocalDateTime(const LocalDateTime &) noexcept = default;
LocalDateTime & operator= (const LocalDateTime &) noexcept = default; LocalDateTime & operator= (const LocalDateTime &) noexcept = default;
LocalDateTime & operator= (time_t time)
{
init(time);
return *this;
}
operator time_t() const
{
return m_year == 0
? 0
: DateLUT::instance().makeDateTime(m_year, m_month, m_day, m_hour, m_minute, m_second);
}
unsigned short year() const { return m_year; } unsigned short year() const { return m_year; }
unsigned char month() const { return m_month; } unsigned char month() const { return m_month; }
unsigned char day() const { return m_day; } unsigned char day() const { return m_day; }
@ -132,8 +106,30 @@ public:
void second(unsigned char x) { m_second = x; } void second(unsigned char x) { m_second = x; }
LocalDate toDate() const { return LocalDate(m_year, m_month, m_day); } LocalDate toDate() const { return LocalDate(m_year, m_month, m_day); }
LocalDateTime toStartOfDate() const { return LocalDateTime(m_year, m_month, m_day, 0, 0, 0); }
LocalDateTime toStartOfDate() { return LocalDateTime(m_year, m_month, m_day, 0, 0, 0); } std::string toString() const
{
std::string s{"0000-00-00 00:00:00"};
s[0] += m_year / 1000;
s[1] += (m_year / 100) % 10;
s[2] += (m_year / 10) % 10;
s[3] += m_year % 10;
s[5] += m_month / 10;
s[6] += m_month % 10;
s[8] += m_day / 10;
s[9] += m_day % 10;
s[11] += m_hour / 10;
s[12] += m_hour % 10;
s[14] += m_minute / 10;
s[15] += m_minute % 10;
s[17] += m_second / 10;
s[18] += m_second % 10;
return s;
}
bool operator< (const LocalDateTime & other) const bool operator< (const LocalDateTime & other) const
{ {
@ -167,14 +163,3 @@ public:
}; };
static_assert(sizeof(LocalDateTime) == 8); static_assert(sizeof(LocalDateTime) == 8);
namespace std
{
inline string to_string(const LocalDateTime & datetime)
{
stringstream str;
str << datetime;
return str.str();
}
}

View File

@ -12,6 +12,7 @@ private:
T t; T t;
public: public:
using UnderlyingType = T;
template <class Enable = typename std::is_copy_constructible<T>::type> template <class Enable = typename std::is_copy_constructible<T>::type>
explicit StrongTypedef(const T & t_) : t(t_) {} explicit StrongTypedef(const T & t_) : t(t_) {}
template <class Enable = typename std::is_move_constructible<T>::type> template <class Enable = typename std::is_move_constructible<T>::type>

View File

@ -1,25 +1,2 @@
include (${ClickHouse_SOURCE_DIR}/cmake/add_check.cmake)
add_executable (date_lut2 date_lut2.cpp)
add_executable (date_lut3 date_lut3.cpp)
add_executable (date_lut_default_timezone date_lut_default_timezone.cpp)
add_executable (local_date_time_comparison local_date_time_comparison.cpp)
add_executable (realloc-perf allocator.cpp)
set(PLATFORM_LIBS ${CMAKE_DL_LIBS})
target_link_libraries (date_lut2 PRIVATE common ${PLATFORM_LIBS})
target_link_libraries (date_lut3 PRIVATE common ${PLATFORM_LIBS})
target_link_libraries (date_lut_default_timezone PRIVATE common ${PLATFORM_LIBS})
target_link_libraries (local_date_time_comparison PRIVATE common ${PLATFORM_LIBS})
target_link_libraries (realloc-perf PRIVATE common)
add_check(local_date_time_comparison)
if(USE_GTEST)
add_executable(unit_tests_libcommon gtest_json_test.cpp gtest_strong_typedef.cpp gtest_find_symbols.cpp)
target_link_libraries(unit_tests_libcommon PRIVATE common ${GTEST_MAIN_LIBRARIES} ${GTEST_LIBRARIES})
add_check(unit_tests_libcommon)
endif()
add_executable (dump_variable dump_variable.cpp) add_executable (dump_variable dump_variable.cpp)
target_link_libraries (dump_variable PRIVATE clickhouse_common_io) target_link_libraries (dump_variable PRIVATE clickhouse_common_io)

View File

@ -1,47 +0,0 @@
#include <cstdlib>
#include <cstring>
#include <vector>
#include <thread>
void thread_func()
{
for (size_t i = 0; i < 100; ++i)
{
size_t size = 4096;
void * buf = malloc(size);
if (!buf)
abort();
memset(buf, 0, size);
while (size < 1048576)
{
size_t next_size = size * 4;
void * new_buf = realloc(buf, next_size);
if (!new_buf)
abort();
buf = new_buf;
memset(reinterpret_cast<char*>(buf) + size, 0, next_size - size);
size = next_size;
}
free(buf);
}
}
int main(int, char **)
{
std::vector<std::thread> threads(16);
for (size_t i = 0; i < 1000; ++i)
{
for (auto & thread : threads)
thread = std::thread(thread_func);
for (auto & thread : threads)
thread.join();
}
return 0;
}

View File

@ -1,53 +0,0 @@
#include <iostream>
#include <cstring>
#include <common/DateLUT.h>
static std::string toString(time_t Value)
{
struct tm tm;
char buf[96];
localtime_r(&Value, &tm);
snprintf(buf, sizeof(buf), "%04d-%02d-%02d %02d:%02d:%02d",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
return buf;
}
static time_t orderedIdentifierToDate(unsigned value)
{
struct tm tm;
memset(&tm, 0, sizeof(tm));
tm.tm_year = value / 10000 - 1900;
tm.tm_mon = (value % 10000) / 100 - 1;
tm.tm_mday = value % 100;
tm.tm_isdst = -1;
return mktime(&tm);
}
void loop(time_t begin, time_t end, int step)
{
const auto & date_lut = DateLUT::instance();
for (time_t t = begin; t < end; t += step)
std::cout << toString(t)
<< ", " << toString(date_lut.toTime(t))
<< ", " << date_lut.toHour(t)
<< std::endl;
}
int main(int, char **)
{
loop(orderedIdentifierToDate(20101031), orderedIdentifierToDate(20101101), 15 * 60);
loop(orderedIdentifierToDate(20100328), orderedIdentifierToDate(20100330), 15 * 60);
loop(orderedIdentifierToDate(20141020), orderedIdentifierToDate(20141106), 15 * 60);
return 0;
}

View File

@ -1,62 +0,0 @@
#include <iostream>
#include <cstring>
#include <Poco/Exception.h>
#include <common/DateLUT.h>
static std::string toString(time_t Value)
{
struct tm tm;
char buf[96];
localtime_r(&Value, &tm);
snprintf(buf, sizeof(buf), "%04d-%02d-%02d %02d:%02d:%02d",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
return buf;
}
static time_t orderedIdentifierToDate(unsigned value)
{
struct tm tm;
memset(&tm, 0, sizeof(tm));
tm.tm_year = value / 10000 - 1900;
tm.tm_mon = (value % 10000) / 100 - 1;
tm.tm_mday = value % 100;
tm.tm_isdst = -1;
return mktime(&tm);
}
void loop(time_t begin, time_t end, int step)
{
const auto & date_lut = DateLUT::instance();
for (time_t t = begin; t < end; t += step)
{
time_t t2 = date_lut.makeDateTime(date_lut.toYear(t), date_lut.toMonth(t), date_lut.toDayOfMonth(t),
date_lut.toHour(t), date_lut.toMinute(t), date_lut.toSecond(t));
std::string s1 = toString(t);
std::string s2 = toString(t2);
std::cerr << s1 << ", " << s2 << std::endl;
if (s1 != s2)
throw Poco::Exception("Test failed.");
}
}
int main(int, char **)
{
loop(orderedIdentifierToDate(20101031), orderedIdentifierToDate(20101101), 15 * 60);
loop(orderedIdentifierToDate(20100328), orderedIdentifierToDate(20100330), 15 * 60);
return 0;
}

View File

@ -1,31 +0,0 @@
#include <iostream>
#include <common/DateLUT.h>
#include <Poco/Exception.h>
int main(int, char **)
{
try
{
const auto & date_lut = DateLUT::instance();
std::cout << "Detected default timezone: `" << date_lut.getTimeZone() << "'" << std::endl;
time_t now = time(nullptr);
std::cout << "Current time: " << date_lut.timeToString(now)
<< ", UTC: " << DateLUT::instance("UTC").timeToString(now) << std::endl;
}
catch (const Poco::Exception & e)
{
std::cerr << e.displayText() << std::endl;
return 1;
}
catch (std::exception & e)
{
std::cerr << "std::exception: " << e.what() << std::endl;
return 2;
}
catch (...)
{
std::cerr << "Some exception" << std::endl;
return 3;
}
return 0;
}

View File

@ -1,656 +0,0 @@
#include <vector>
#include <string>
#include <exception>
#include <common/JSON.h>
#include <boost/range/irange.hpp>
using namespace std::literals::string_literals;
#include <gtest/gtest.h>
enum class ResultType
{
Return,
Throw
};
struct GetStringTestRecord
{
const char * input;
ResultType result_type;
const char * result;
};
TEST(JSONSuite, SimpleTest)
{
std::vector<GetStringTestRecord> test_data =
{
{ R"("name")", ResultType::Return, "name" },
{ R"("Вафельница Vitek WX-1102 FL")", ResultType::Return, "Вафельница Vitek WX-1102 FL" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("184509")", ResultType::Return, "184509" },
{ R"("category")", ResultType::Return, "category" },
{ R"("Все для детей/Детская техника/Vitek")", ResultType::Return, "Все для детей/Детская техника/Vitek" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("В наличии")", ResultType::Return, "В наличии" },
{ R"("price")", ResultType::Return, "price" },
{ R"("2390.00")", ResultType::Return, "2390.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("Карточка")", ResultType::Return, "Карточка" },
{ R"("position")", ResultType::Return, "position" },
{ R"("detail")", ResultType::Return, "detail" },
{ R"("actionField")", ResultType::Return, "actionField" },
{ R"("list")", ResultType::Return, "list" },
{ R"("http://www.techport.ru/q/?t=вафельница&sort=price&sdim=asc")", ResultType::Return, "http://www.techport.ru/q/?t=вафельница&sort=price&sdim=asc" },
{ R"("action")", ResultType::Return, "action" },
{ R"("detail")", ResultType::Return, "detail" },
{ R"("products")", ResultType::Return, "products" },
{ R"("name")", ResultType::Return, "name" },
{ R"("Вафельница Vitek WX-1102 FL")", ResultType::Return, "Вафельница Vitek WX-1102 FL" },
{ R"("id")", ResultType::Return, "id" },
{ R"("184509")", ResultType::Return, "184509" },
{ R"("price")", ResultType::Return, "price" },
{ R"("2390.00")", ResultType::Return, "2390.00" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("Vitek")", ResultType::Return, "Vitek" },
{ R"("category")", ResultType::Return, "category" },
{ R"("Все для детей/Детская техника/Vitek")", ResultType::Return, "Все для детей/Детская техника/Vitek" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("В наличии")", ResultType::Return, "В наличии" },
{ R"("ru")", ResultType::Return, "ru" },
{ R"("experiments")", ResultType::Return, "experiments" },
{ R"("lang")", ResultType::Return, "lang" },
{ R"("ru")", ResultType::Return, "ru" },
{ R"("los_portal")", ResultType::Return, "los_portal" },
{ R"("los_level")", ResultType::Return, "los_level" },
{ R"("none")", ResultType::Return, "none" },
{ R"("isAuthorized")", ResultType::Return, "isAuthorized" },
{ R"("isSubscriber")", ResultType::Return, "isSubscriber" },
{ R"("postType")", ResultType::Return, "postType" },
{ R"("Новости")", ResultType::Return, "Новости" },
{ R"("experiments")", ResultType::Return, "experiments" },
{ R"("lang")", ResultType::Return, "lang" },
{ R"("ru")", ResultType::Return, "ru" },
{ R"("los_portal")", ResultType::Return, "los_portal" },
{ R"("los_level")", ResultType::Return, "los_level" },
{ R"("none")", ResultType::Return, "none" },
{ R"("lang")", ResultType::Return, "lang" },
{ R"("ru")", ResultType::Return, "ru" },
{ R"("Электроплита GEFEST Брест ЭПНД 5140-01 0001")", ResultType::Return, "Электроплита GEFEST Брест ЭПНД 5140-01 0001" },
{ R"("price")", ResultType::Return, "price" },
{ R"("currencyCode")", ResultType::Return, "currencyCode" },
{ R"("RUB")", ResultType::Return, "RUB" },
{ R"("lang")", ResultType::Return, "lang" },
{ R"("ru")", ResultType::Return, "ru" },
{ R"("experiments")", ResultType::Return, "experiments" },
{ R"("lang")", ResultType::Return, "lang" },
{ R"("ru")", ResultType::Return, "ru" },
{ R"("los_portal")", ResultType::Return, "los_portal" },
{ R"("los_level")", ResultType::Return, "los_level" },
{ R"("none")", ResultType::Return, "none" },
{ R"("trash_login")", ResultType::Return, "trash_login" },
{ R"("novikoff")", ResultType::Return, "novikoff" },
{ R"("trash_cat_link")", ResultType::Return, "trash_cat_link" },
{ R"("progs")", ResultType::Return, "progs" },
{ R"("trash_parent_link")", ResultType::Return, "trash_parent_link" },
{ R"("content")", ResultType::Return, "content" },
{ R"("trash_posted_parent")", ResultType::Return, "trash_posted_parent" },
{ R"("content.01.2016")", ResultType::Return, "content.01.2016" },
{ R"("trash_posted_cat")", ResultType::Return, "trash_posted_cat" },
{ R"("progs.01.2016")", ResultType::Return, "progs.01.2016" },
{ R"("trash_virus_count")", ResultType::Return, "trash_virus_count" },
{ R"("trash_is_android")", ResultType::Return, "trash_is_android" },
{ R"("trash_is_wp8")", ResultType::Return, "trash_is_wp8" },
{ R"("trash_is_ios")", ResultType::Return, "trash_is_ios" },
{ R"("trash_posted")", ResultType::Return, "trash_posted" },
{ R"("01.2016")", ResultType::Return, "01.2016" },
{ R"("experiments")", ResultType::Return, "experiments" },
{ R"("lang")", ResultType::Return, "lang" },
{ R"("ru")", ResultType::Return, "ru" },
{ R"("los_portal")", ResultType::Return, "los_portal" },
{ R"("los_level")", ResultType::Return, "los_level" },
{ R"("none")", ResultType::Return, "none" },
{ R"("merchantId")", ResultType::Return, "merchantId" },
{ R"("13694_49246")", ResultType::Return, "13694_49246" },
{ R"("cps-source")", ResultType::Return, "cps-source" },
{ R"("wargaming")", ResultType::Return, "wargaming" },
{ R"("cps_provider")", ResultType::Return, "cps_provider" },
{ R"("default")", ResultType::Return, "default" },
{ R"("errorReason")", ResultType::Return, "errorReason" },
{ R"("no errors")", ResultType::Return, "no errors" },
{ R"("scid")", ResultType::Return, "scid" },
{ R"("isAuthPayment")", ResultType::Return, "isAuthPayment" },
{ R"("lang")", ResultType::Return, "lang" },
{ R"("ru")", ResultType::Return, "ru" },
{ R"("rubric")", ResultType::Return, "rubric" },
{ R"("")", ResultType::Return, "" },
{ R"("rubric")", ResultType::Return, "rubric" },
{ R"("Мир")", ResultType::Return, "Мир" },
{ R"("lang")", ResultType::Return, "lang" },
{ R"("ru")", ResultType::Return, "ru" },
{ R"("experiments")", ResultType::Return, "experiments" },
{ R"("lang")", ResultType::Return, "lang" },
{ R"("ru")", ResultType::Return, "ru" },
{ R"("los_portal")", ResultType::Return, "los_portal" },
{ R"("los_level")", ResultType::Return, "los_level" },
{ R"("none")", ResultType::Return, "none" },
{ R"("lang")", ResultType::Return, "lang" },
{ R"("ru")", ResultType::Return, "ru" },
{ R"("__ym")", ResultType::Return, "__ym" },
{ R"("ecommerce")", ResultType::Return, "ecommerce" },
{ R"("impressions")", ResultType::Return, "impressions" },
{ R"("id")", ResultType::Return, "id" },
{ R"("863813")", ResultType::Return, "863813" },
{ R"("name")", ResultType::Return, "name" },
{ R"("Футболка детская 3D Happy, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Happy, возраст 1-2 года, трикотаж" },
{ R"("category")", ResultType::Return, "category" },
{ R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("")", ResultType::Return, "" },
{ R"("price")", ResultType::Return, "price" },
{ R"("390.00")", ResultType::Return, "390.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
{ R"("position")", ResultType::Return, "position" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
{ R"("id")", ResultType::Return, "id" },
{ R"("863839")", ResultType::Return, "863839" },
{ R"("name")", ResultType::Return, "name" },
{ R"("Футболка детская 3D Pretty kitten, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Pretty kitten, возраст 1-2 года, трикотаж" },
{ R"("category")", ResultType::Return, "category" },
{ R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("")", ResultType::Return, "" },
{ R"("price")", ResultType::Return, "price" },
{ R"("390.00")", ResultType::Return, "390.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
{ R"("position")", ResultType::Return, "position" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
{ R"("id")", ResultType::Return, "id" },
{ R"("863847")", ResultType::Return, "863847" },
{ R"("name")", ResultType::Return, "name" },
{ R"("Футболка детская 3D Little tiger, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Little tiger, возраст 1-2 года, трикотаж" },
{ R"("category")", ResultType::Return, "category" },
{ R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("")", ResultType::Return, "" },
{ R"("price")", ResultType::Return, "price" },
{ R"("390.00")", ResultType::Return, "390.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
{ R"("position")", ResultType::Return, "position" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
{ R"("id")", ResultType::Return, "id" },
{ R"("911480")", ResultType::Return, "911480" },
{ R"("name")", ResultType::Return, "name" },
{ R"("Футболка детская 3D Puppy, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Puppy, возраст 1-2 года, трикотаж" },
{ R"("category")", ResultType::Return, "category" },
{ R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("")", ResultType::Return, "" },
{ R"("price")", ResultType::Return, "price" },
{ R"("390.00")", ResultType::Return, "390.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
{ R"("position")", ResultType::Return, "position" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
{ R"("id")", ResultType::Return, "id" },
{ R"("911484")", ResultType::Return, "911484" },
{ R"("name")", ResultType::Return, "name" },
{ R"("Футболка детская 3D Little bears, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Little bears, возраст 1-2 года, трикотаж" },
{ R"("category")", ResultType::Return, "category" },
{ R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("")", ResultType::Return, "" },
{ R"("price")", ResultType::Return, "price" },
{ R"("390.00")", ResultType::Return, "390.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
{ R"("position")", ResultType::Return, "position" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
{ R"("id")", ResultType::Return, "id" },
{ R"("911489")", ResultType::Return, "911489" },
{ R"("name")", ResultType::Return, "name" },
{ R"("Футболка детская 3D Dolphin, возраст 2-4 года, трикотаж")", ResultType::Return, "Футболка детская 3D Dolphin, возраст 2-4 года, трикотаж" },
{ R"("category")", ResultType::Return, "category" },
{ R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("")", ResultType::Return, "" },
{ R"("price")", ResultType::Return, "price" },
{ R"("390.00")", ResultType::Return, "390.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
{ R"("position")", ResultType::Return, "position" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
{ R"("id")", ResultType::Return, "id" },
{ R"("911496")", ResultType::Return, "911496" },
{ R"("name")", ResultType::Return, "name" },
{ R"("Футболка детская 3D Pretty, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Pretty, возраст 1-2 года, трикотаж" },
{ R"("category")", ResultType::Return, "category" },
{ R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("")", ResultType::Return, "" },
{ R"("price")", ResultType::Return, "price" },
{ R"("390.00")", ResultType::Return, "390.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
{ R"("position")", ResultType::Return, "position" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
{ R"("id")", ResultType::Return, "id" },
{ R"("911504")", ResultType::Return, "911504" },
{ R"("name")", ResultType::Return, "name" },
{ R"("Футболка детская 3D Fairytale, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Fairytale, возраст 1-2 года, трикотаж" },
{ R"("category")", ResultType::Return, "category" },
{ R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("")", ResultType::Return, "" },
{ R"("price")", ResultType::Return, "price" },
{ R"("390.00")", ResultType::Return, "390.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
{ R"("position")", ResultType::Return, "position" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
{ R"("id")", ResultType::Return, "id" },
{ R"("911508")", ResultType::Return, "911508" },
{ R"("name")", ResultType::Return, "name" },
{ R"("Футболка детская 3D Kittens, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Kittens, возраст 1-2 года, трикотаж" },
{ R"("category")", ResultType::Return, "category" },
{ R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("")", ResultType::Return, "" },
{ R"("price")", ResultType::Return, "price" },
{ R"("390.00")", ResultType::Return, "390.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
{ R"("position")", ResultType::Return, "position" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
{ R"("id")", ResultType::Return, "id" },
{ R"("911512")", ResultType::Return, "911512" },
{ R"("name")", ResultType::Return, "name" },
{ R"("Футболка детская 3D Sunshine, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Sunshine, возраст 1-2 года, трикотаж" },
{ R"("category")", ResultType::Return, "category" },
{ R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("")", ResultType::Return, "" },
{ R"("price")", ResultType::Return, "price" },
{ R"("390.00")", ResultType::Return, "390.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
{ R"("position")", ResultType::Return, "position" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
{ R"("id")", ResultType::Return, "id" },
{ R"("911516")", ResultType::Return, "911516" },
{ R"("name")", ResultType::Return, "name" },
{ R"("Футболка детская 3D Dog in bag, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Dog in bag, возраст 1-2 года, трикотаж" },
{ R"("category")", ResultType::Return, "category" },
{ R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("")", ResultType::Return, "" },
{ R"("price")", ResultType::Return, "price" },
{ R"("390.00")", ResultType::Return, "390.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
{ R"("position")", ResultType::Return, "position" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
{ R"("id")", ResultType::Return, "id" },
{ R"("911520")", ResultType::Return, "911520" },
{ R"("name")", ResultType::Return, "name" },
{ R"("Футболка детская 3D Cute puppy, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Cute puppy, возраст 1-2 года, трикотаж" },
{ R"("category")", ResultType::Return, "category" },
{ R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("")", ResultType::Return, "" },
{ R"("price")", ResultType::Return, "price" },
{ R"("390.00")", ResultType::Return, "390.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
{ R"("position")", ResultType::Return, "position" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
{ R"("id")", ResultType::Return, "id" },
{ R"("911524")", ResultType::Return, "911524" },
{ R"("name")", ResultType::Return, "name" },
{ R"("Футболка детская 3D Rabbit, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Rabbit, возраст 1-2 года, трикотаж" },
{ R"("category")", ResultType::Return, "category" },
{ R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("")", ResultType::Return, "" },
{ R"("price")", ResultType::Return, "price" },
{ R"("390.00")", ResultType::Return, "390.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
{ R"("position")", ResultType::Return, "position" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
{ R"("id")", ResultType::Return, "id" },
{ R"("911528")", ResultType::Return, "911528" },
{ R"("name")", ResultType::Return, "name" },
{ R"("Футболка детская 3D Turtle, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Turtle, возраст 1-2 года, трикотаж" },
{ R"("category")", ResultType::Return, "category" },
{ R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("")", ResultType::Return, "" },
{ R"("price")", ResultType::Return, "price" },
{ R"("390.00")", ResultType::Return, "390.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
{ R"("position")", ResultType::Return, "position" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
{ R"("id")", ResultType::Return, "id" },
{ R"("888616")", ResultType::Return, "888616" },
{ R"("name")", ResultType::Return, "name" },
{ "\"3Д Футболка мужская \\\"Collorista\\\" Светлое завтра р-р XL(52-54), 100% хлопок, трикотаж\"", ResultType::Return, "3Д Футболка мужская \"Collorista\" Светлое завтра р-р XL(52-54), 100% хлопок, трикотаж" },
{ R"("category")", ResultType::Return, "category" },
{ R"("/Одежда и обувь/Мужская одежда/Футболки/")", ResultType::Return, "/Одежда и обувь/Мужская одежда/Футболки/" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("")", ResultType::Return, "" },
{ R"("price")", ResultType::Return, "price" },
{ R"("406.60")", ResultType::Return, "406.60" },
{ R"("list")", ResultType::Return, "list" },
{ R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
{ R"("position")", ResultType::Return, "position" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
{ R"("id")", ResultType::Return, "id" },
{ R"("913361")", ResultType::Return, "913361" },
{ R"("name")", ResultType::Return, "name" },
{ R"("3Д Футболка детская World р-р 8-10, 100% хлопок, трикотаж")", ResultType::Return, "3Д Футболка детская World р-р 8-10, 100% хлопок, трикотаж" },
{ R"("category")", ResultType::Return, "category" },
{ R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("")", ResultType::Return, "" },
{ R"("price")", ResultType::Return, "price" },
{ R"("470.00")", ResultType::Return, "470.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
{ R"("position")", ResultType::Return, "position" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
{ R"("id")", ResultType::Return, "id" },
{ R"("913364")", ResultType::Return, "913364" },
{ R"("name")", ResultType::Return, "name" },
{ R"("3Д Футболка детская Force р-р 8-10, 100% хлопок, трикотаж")", ResultType::Return, "3Д Футболка детская Force р-р 8-10, 100% хлопок, трикотаж" },
{ R"("category")", ResultType::Return, "category" },
{ R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("")", ResultType::Return, "" },
{ R"("price")", ResultType::Return, "price" },
{ R"("470.00")", ResultType::Return, "470.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
{ R"("position")", ResultType::Return, "position" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
{ R"("id")", ResultType::Return, "id" },
{ R"("913367")", ResultType::Return, "913367" },
{ R"("name")", ResultType::Return, "name" },
{ R"("3Д Футболка детская Winter tale р-р 8-10, 100% хлопок, трикотаж")", ResultType::Return, "3Д Футболка детская Winter tale р-р 8-10, 100% хлопок, трикотаж" },
{ R"("category")", ResultType::Return, "category" },
{ R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("")", ResultType::Return, "" },
{ R"("price")", ResultType::Return, "price" },
{ R"("470.00")", ResultType::Return, "470.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
{ R"("position")", ResultType::Return, "position" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
{ R"("id")", ResultType::Return, "id" },
{ R"("913385")", ResultType::Return, "913385" },
{ R"("name")", ResultType::Return, "name" },
{ R"("3Д Футболка детская Moonshine р-р 8-10, 100% хлопок, трикотаж")", ResultType::Return, "3Д Футболка детская Moonshine р-р 8-10, 100% хлопок, трикотаж" },
{ R"("category")", ResultType::Return, "category" },
{ R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("")", ResultType::Return, "" },
{ R"("price")", ResultType::Return, "price" },
{ R"("470.00")", ResultType::Return, "470.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
{ R"("position")", ResultType::Return, "position" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
{ R"("id")", ResultType::Return, "id" },
{ R"("913391")", ResultType::Return, "913391" },
{ R"("name")", ResultType::Return, "name" },
{ R"("3Д Футболка детская Shaman р-р 8-10, 100% хлопок, трикотаж")", ResultType::Return, "3Д Футболка детская Shaman р-р 8-10, 100% хлопок, трикотаж" },
{ R"("category")", ResultType::Return, "category" },
{ R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("")", ResultType::Return, "" },
{ R"("price")", ResultType::Return, "price" },
{ R"("470.00")", ResultType::Return, "470.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
{ R"("position")", ResultType::Return, "position" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
{ R"("usertype")", ResultType::Return, "usertype" },
{ R"("visitor")", ResultType::Return, "visitor" },
{ R"("lang")", ResultType::Return, "lang" },
{ R"("ru")", ResultType::Return, "ru" },
{ R"("__ym")", ResultType::Return, "__ym" },
{ R"("ecommerce")", ResultType::Return, "ecommerce" },
{ R"("impressions")", ResultType::Return, "impressions" },
{ R"("experiments")", ResultType::Return, "experiments" },
{ R"("lang")", ResultType::Return, "lang" },
{ R"("ru")", ResultType::Return, "ru" },
{ R"("los_portal")", ResultType::Return, "los_portal" },
{ R"("los_level")", ResultType::Return, "los_level" },
{ R"("none")", ResultType::Return, "none" },
{ R"("experiments")", ResultType::Return, "experiments" },
{ R"("lang")", ResultType::Return, "lang" },
{ R"("ru")", ResultType::Return, "ru" },
{ R"("los_portal")", ResultType::Return, "los_portal" },
{ R"("los_level")", ResultType::Return, "los_level" },
{ R"("none")", ResultType::Return, "none" },
{ R"("experiments")", ResultType::Return, "experiments" },
{ R"("lang")", ResultType::Return, "lang" },
{ R"("ru")", ResultType::Return, "ru" },
{ R"("los_portal")", ResultType::Return, "los_portal" },
{ R"("los_level")", ResultType::Return, "los_level" },
{ R"("none")", ResultType::Return, "none" },
{ R"("experiments")", ResultType::Return, "experiments" },
{ R"("lang")", ResultType::Return, "lang" },
{ R"("ru")", ResultType::Return, "ru" },
{ R"("los_portal")", ResultType::Return, "los_portal" },
{ R"("los_level")", ResultType::Return, "los_level" },
{ R"("none")", ResultType::Return, "none" },
{ R"("experiments")", ResultType::Return, "experiments" },
{ R"("lang")", ResultType::Return, "lang" },
{ R"("ru")", ResultType::Return, "ru" },
{ R"("los_portal")", ResultType::Return, "los_portal" },
{ R"("los_level")", ResultType::Return, "los_level" },
{ R"("none")", ResultType::Return, "none" },
{ R"("__ym")", ResultType::Return, "__ym" },
{ R"("ecommerce")", ResultType::Return, "ecommerce" },
{ R"("currencyCode")", ResultType::Return, "currencyCode" },
{ R"("RUR")", ResultType::Return, "RUR" },
{ R"("impressions")", ResultType::Return, "impressions" },
{ R"("name")", ResultType::Return, "name" },
{ R"("Чайник электрический Mystery MEK-1627, белый")", ResultType::Return, "Чайник электрический Mystery MEK-1627, белый" },
{ R"("brand")", ResultType::Return, "brand" },
{ R"("Mystery")", ResultType::Return, "Mystery" },
{ R"("id")", ResultType::Return, "id" },
{ R"("187180")", ResultType::Return, "187180" },
{ R"("category")", ResultType::Return, "category" },
{ R"("Мелкая бытовая техника/Мелкие кухонные приборы/Чайники электрические/Mystery")", ResultType::Return, "Мелкая бытовая техника/Мелкие кухонные приборы/Чайники электрические/Mystery" },
{ R"("variant")", ResultType::Return, "variant" },
{ R"("В наличии")", ResultType::Return, "В наличии" },
{ R"("price")", ResultType::Return, "price" },
{ R"("1630.00")", ResultType::Return, "1630.00" },
{ R"("list")", ResultType::Return, "list" },
{ R"("Карточка")", ResultType::Return, "Карточка" },
{ R"("position")", ResultType::Return, "position" },
{ R"("detail")", ResultType::Return, "detail" },
{ R"("actionField")", ResultType::Return, "actionField" },
{ R"("list")", ResultType::Return, "list" },
{ "\0\"", ResultType::Throw, "JSON: expected \", got \0" },
{ "\"/igrushki/konstruktory\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/1290414/komplekt-zhenskiy-dzhemper-plusbryuki-m-254-09-malina-plustemno-siniy-\0a", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/Творчество/Рисование/Инструменты и кра\0a", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Строительство и ремонт/Силовая техника/Зарядные устройства для автомобильных аккумуляторов/Пуско-зарядные устр\xD0\0a", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Строительство и ремонт/Силовая техника/Зарядные устройств\xD0\0t", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Строительство и ремонт/Силовая техника/Зарядные устройства для автомобиль\0k", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\0t", ResultType::Throw, "JSON: expected \", got \0" },
{ "\"/Хозтовары/Хранение вещей и организа\xD1\0t", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/Хозтовары/Товары для стир\0a", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"li\0a", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/734859/samolet-radioupravlyaemyy-istrebitel-rabotaet-o\0k", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/kosmetika-i-parfyum/parfyumeriya/mu\0t", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/ko\0\x04", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "", ResultType::Throw, "JSON: begin >= end." },
{ "\"/stroitelstvo-i-remont/stroit\0t", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/stroitelstvo-i-remont/stroitelnyy-instrument/av\0k", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/s\0a", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/Строительство и ремонт/Строительный инструмент/Изм\0e", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/avto/soputstvuy\0l", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/str\0\xD0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Отвертка 2 в 1 \\\"TUNDRA basic\\\" 5х75 мм (+,-) \0\xFF", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/stroitelstvo-i-remont/stroitelnyy-instrument/avtoinstrumen\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Мелкая бытовая техника/Мелки\xD0\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Пряжа \\\"Бамбук стрейч\\0\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Карандаш чёрнографитны\xD0\0\xD0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/Творчество/Рукоделие, аппликации/Пряжа и шерсть для \xD0\0l", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/1071547/karandash-chernografitnyy-volshebstvo-nv-kruglyy-d-7-2mm-dl-176mm-plast-tuba/\0e", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"ca\0e", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"ca\0e", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/1165424/chipbord-vyrubnoy-dlya-skrapbukinga-malyshi-mikki-maus-disney-bebi\0t", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/posuda/kuhonnye-prinadlezhnosti-i-i\0d", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/Канцтовары/Ежедневники и блокн\xD0\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/kanctovary/ezhednevniki-i-blok\0a", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Стакан \xD0\0a", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Набор бумаги для скрапбукинга \\\"Мои первый годик\\\": Микки Маус, Дисней бэби, 12 листов 29.5 х 29.5 см, 160\0\x80", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"c\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Органайзер для хранения аксессуаров, \0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"quantity\00", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Сменный блок для тетрадей на кольцах А5, 160 листов клетка, офсет \xE2\x84\0=", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/Сувениры/Ф\xD0\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"\0\"", ResultType::Return, "\0" },
{ "\"\0\x04", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"va\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"ca\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"В \0\x04", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/letnie-tovary/z\0\x04", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Посудомоечная машина Ha\0=", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Крупная бытов\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Полочная акустическая система Magnat Needl\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"brand\00", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"\0d", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"pos\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"c\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"var\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Телевизоры и видеотехника/Всё для домашних кинотеатр\0=", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Флеш-диск Transcend JetFlash 620 8GB (TS8GJF62\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Табурет Мег\0\xD0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"variant\0\x04", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Катал\xD0\0\"", ResultType::Return, "Катал\xD0\0" },
{ "\"К\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Полочная акустическая система Magnat Needl\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"brand\00", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"\0d", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"pos\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"c\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"17\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/igrushki/razvivayusc\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Ключница \\\"\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/Игр\xD1\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/Игрушки/Игрушки для девочек/Игровые модули дл\xD1\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Крупная бытовая техника/Стиральные машины/С фронт\xD0\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\0 ", ResultType::Throw, "JSON: expected \", got \0" },
{ "\"Светодиодная лента SMD3528, 5 м. IP33, 60LED, зеленый, 4,8W/мет\xD1\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Сантехника/Мебель для ванных комнат/Стол\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\0o", ResultType::Throw, "JSON: expected \", got \0" },
{ "\"/igrushki/konstruktory\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/posuda/kuhonnye-prinadlezhnosti-i-instrumenty/kuhonnye-pr\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/1290414/komplekt-zhenskiy-dzhemper-plusbryuki-m-254-09-malina-plustemno-siniy-\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/Творчество/Рисование/Инструменты и кра\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Строительство и ремонт/Силовая техника/Зарядные устройства для автомобильных аккумуляторов/Пуско-зарядные устр\xD0\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Строительство и ремонт/Силовая техника/Зарядные устройств\xD0\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Строительство и ремонт/Силовая техника/Зарядные устройства для автомобиль\0d", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\0 ", ResultType::Throw, "JSON: expected \", got \0" },
{ "\"/Хозтовары/Хранение вещей и организа\xD1\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/Хозтовары/Товары для стир\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"li\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/igrushki/igrus\0d", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/734859/samolet-radioupravlyaemyy-istrebitel-rabotaet-o\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/kosmetika-i-parfyum/parfyumeriya/mu\00", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/ko\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/avto/avtomobilnyy\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/stroitelstvo-i-remont/stroit\00", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/stroitelstvo-i-remont/stroitelnyy-instrument/av\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/s\0d", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/Строительство и ремонт/Строительный инструмент/Изм\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/avto/soputstvuy\0\"", ResultType::Return, "/avto/soputstvuy\0" },
{ "\"/str\0k", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Отвертка 2 в 1 \\\"TUNDRA basic\\\" 5х75 мм (+,-) \0\xD0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/stroitelstvo-i-remont/stroitelnyy-instrument/avtoinstrumen\0=", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Чайник электрический Vitesse\0=", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Мелкая бытовая техника/Мелки\xD0\0\xD0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Пряжа \\\"Бамбук стрейч\\0о", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Карандаш чёрнографитны\xD0\0k", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/Творчество/Рукоделие, аппликации/Пряжа и шерсть для \xD0\0\"", ResultType::Return, "/Творчество/Рукоделие, аппликации/Пряжа и шерсть для \xD0\0" },
{ "\"/1071547/karandash-chernografitnyy-volshebstvo-nv-kruglyy-d-7-2mm-dl-176mm-plast-tuba/\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"ca\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/Подаро\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Средство для прочис\xD1\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"i\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/p\0\"", ResultType::Return, "/p\0" },
{ "\"/Сувениры/Магниты, н\xD0\0k", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Дерев\xD0\0=", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/prazdniki/svadba/svadebnaya-c\0\xD0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/Канцт\0d", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/Праздники/То\xD0\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"v\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/Косметика \xD0\0d", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/Спорт и отдых/Настольные игры/Покер, руле\xD1\0\xD0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"categ\0=", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/retailr\0k", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/retailrocket\0k", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Ежедневник недат А5 140л кл,ляссе,обл пв\0=", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/432809/ezhednevnik-organayzer-sredniy-s-remeshkom-na-knopke-v-oblozhke-kalkulyator-kalendar-do-\0\xD0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/1165424/chipbord-vyrubnoy-dlya-skrapbukinga-malyshi-mikki-maus-disney-bebi\0d", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/posuda/kuhonnye-prinadlezhnosti-i-i\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/Канцтовары/Ежедневники и блокн\xD0\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"/kanctovary/ezhednevniki-i-blok\00", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Стакан \xD0\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"Набор бумаги для скрапбукинга \\\"Мои первый годик\\\": Микки Маус, Дисней бэби, 12 листов 29.5 х 29.5 см, 160\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
{ "\"c\0\"", ResultType::Return, "c\0" },
};
for (auto i : boost::irange(0, 1/*00000*/))
{
static_cast<void>(i);
for (auto & r : test_data)
{
try
{
JSON j(r.input, r.input + strlen(r.input));
ASSERT_EQ(j.getString(), r.result);
ASSERT_TRUE(r.result_type == ResultType::Return);
}
catch (JSONException & e)
{
ASSERT_TRUE(r.result_type == ResultType::Throw);
ASSERT_EQ(e.message(), r.result);
}
}
}
}

View File

@ -178,7 +178,7 @@ tail:
size -= padding; size -= padding;
} }
/// Aligned unrolled copy. We will use all available SSE registers. /// Aligned unrolled copy. We will use half of available SSE registers.
/// It's not possible to have both src and dst aligned. /// It's not possible to have both src and dst aligned.
/// So, we will use aligned stores and unaligned loads. /// So, we will use aligned stores and unaligned loads.
__m128i c0, c1, c2, c3, c4, c5, c6, c7; __m128i c0, c1, c2, c3, c4, c5, c6, c7;

View File

@ -1,5 +1,2 @@
add_executable (mysqlxx_test mysqlxx_test.cpp)
target_link_libraries (mysqlxx_test PRIVATE mysqlxx)
add_executable (mysqlxx_pool_test mysqlxx_pool_test.cpp) add_executable (mysqlxx_pool_test mysqlxx_pool_test.cpp)
target_link_libraries (mysqlxx_pool_test PRIVATE mysqlxx) target_link_libraries (mysqlxx_pool_test PRIVATE mysqlxx)

View File

@ -1,21 +0,0 @@
<?xml version = '1.0' encoding = 'utf-8'?>
<yandex>
<mysql_goals>
<port>3306</port>
<user>root</user>
<db>Metrica</db>
<password>qwerty</password>
<replica>
<host>example02t</host>
<priority>0</priority>
</replica>
<replica>
<host>example02t</host>
<port>3306</port>
<user>root</user>
<password>qwerty</password>
<db>Metrica</db>
<priority>1</priority>
</replica>
</mysql_goals>
</yandex>

View File

@ -1,77 +0,0 @@
#include <iostream>
#include <mysqlxx/mysqlxx.h>
int main(int, char **)
{
try
{
mysqlxx::Connection connection("test", "127.0.0.1", "root", "qwerty", 3306);
std::cerr << "Connected." << std::endl;
{
mysqlxx::Query query = connection.query();
query << "SELECT 1 x, '2010-01-01 01:01:01' d";
mysqlxx::UseQueryResult result = query.use();
std::cerr << "use() called." << std::endl;
while (mysqlxx::Row row = result.fetch())
{
std::cerr << "Fetched row." << std::endl;
std::cerr << row[0] << ", " << row["x"] << std::endl;
std::cerr << row[1] << ", " << row["d"]
<< ", " << row[1].getDate()
<< ", " << row[1].getDateTime()
<< ", " << row[1].getDate()
<< ", " << row[1].getDateTime()
<< std::endl
<< row[1].getDate() << ", " << row[1].getDateTime() << std::endl
<< row[1].getDate() << ", " << row[1].getDateTime() << std::endl
<< row[1].getDate() << ", " << row[1].getDateTime() << std::endl
<< row[1].getDate() << ", " << row[1].getDateTime() << std::endl
;
time_t t1 = row[0];
time_t t2 = row[1];
std::cerr << t1 << ", " << LocalDateTime(t1) << std::endl;
std::cerr << t2 << ", " << LocalDateTime(t2) << std::endl;
}
}
{
mysqlxx::UseQueryResult result = connection.query("SELECT 'abc\\\\def' x").use();
mysqlxx::Row row = result.fetch();
std::cerr << row << std::endl;
std::cerr << row << std::endl;
}
{
/// Копирование Query
mysqlxx::Query query1 = connection.query("SELECT");
mysqlxx::Query query2 = query1;
query2 << " 1";
std::cerr << query1.str() << ", " << query2.str() << std::endl;
}
{
/// NULL
mysqlxx::Null<int> x = mysqlxx::null;
std::cerr << (x == mysqlxx::null ? "Ok" : "Fail") << std::endl;
std::cerr << (x == 0 ? "Fail" : "Ok") << std::endl;
std::cerr << (x.isNull() ? "Ok" : "Fail") << std::endl;
x = 1;
std::cerr << (x == mysqlxx::null ? "Fail" : "Ok") << std::endl;
std::cerr << (x == 0 ? "Fail" : "Ok") << std::endl;
std::cerr << (x == 1 ? "Ok" : "Fail") << std::endl;
std::cerr << (x.isNull() ? "Fail" : "Ok") << std::endl;
}
}
catch (const mysqlxx::Exception & e)
{
std::cerr << e.code() << ", " << e.message() << std::endl;
throw;
}
return 0;
}

View File

@ -4,7 +4,9 @@
set -eux set -eux
set -o pipefail set -o pipefail
trap "exit" INT TERM trap "exit" INT TERM
trap 'kill $(jobs -pr) ||:' EXIT # The watchdog is in the separate process group, so we have to kill it separately
# if the script terminates earlier.
trap 'kill $(jobs -pr) ${watchdog_pid:-} ||:' EXIT
stage=${stage:-} stage=${stage:-}
script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
@ -14,35 +16,28 @@ BINARY_TO_DOWNLOAD=${BINARY_TO_DOWNLOAD:="clang-11_debug_none_bundled_unsplitted
function clone function clone
{ {
( # The download() function is dependent on CI binaries anyway, so we can take
# the repo from the CI as well. For local runs, start directly from the "fuzz"
# stage.
rm -rf ch ||: rm -rf ch ||:
mkdir ch mkdir ch ||:
cd ch wget -nv -nd -c "https://clickhouse-test-reports.s3.yandex.net/$PR_TO_TEST/$SHA_TO_TEST/repo/clickhouse_no_subs.tar.gz"
tar -C ch --strip-components=1 -xf clickhouse_no_subs.tar.gz
git init ls -lath ||:
git remote add origin https://github.com/ClickHouse/ClickHouse
# Network is unreliable. GitHub neither.
for _ in {1..100}; do git fetch --depth=100 origin "$SHA_TO_TEST" && break; sleep 1; done
# Used to obtain the list of modified or added tests
for _ in {1..100}; do git fetch --depth=100 origin master && break; sleep 1; done
# If not master, try to fetch pull/.../{head,merge}
if [ "$PR_TO_TEST" != "0" ]
then
for _ in {1..100}; do git fetch --depth=100 origin "refs/pull/$PR_TO_TEST/*:refs/heads/pull/$PR_TO_TEST/*" && break; sleep 1; done
fi
git checkout "$SHA_TO_TEST"
)
} }
function download function download
{ {
wget -nv -nd -c "https://clickhouse-builds.s3.yandex.net/$PR_TO_TEST/$SHA_TO_TEST/clickhouse_build_check/$BINARY_TO_DOWNLOAD/clickhouse" wget -nv -nd -c "https://clickhouse-builds.s3.yandex.net/$PR_TO_TEST/$SHA_TO_TEST/clickhouse_build_check/$BINARY_TO_DOWNLOAD/clickhouse" &
wget -nv -nd -c "https://clickhouse-test-reports.s3.yandex.net/$PR_TO_TEST/$SHA_TO_TEST/repo/ci-changed-files.txt" &
wait
chmod +x clickhouse chmod +x clickhouse
ln -s ./clickhouse ./clickhouse-server ln -s ./clickhouse ./clickhouse-server
ln -s ./clickhouse ./clickhouse-client ln -s ./clickhouse ./clickhouse-client
# clickhouse-server is in the current dir
export PATH="$PWD:$PATH"
} }
function configure function configure
@ -77,22 +72,21 @@ function watchdog
function fuzz function fuzz
{ {
# Obtain the list of newly added tests. They will be fuzzed in more extreme way than other tests. # Obtain the list of newly added tests. They will be fuzzed in more extreme way than other tests.
cd ch # Don't overwrite the NEW_TESTS_OPT so that it can be set from the environment.
NEW_TESTS=$(git diff --name-only "$(git merge-base origin/master "$SHA_TO_TEST"~)" "$SHA_TO_TEST" | grep -P 'tests/queries/0_stateless/.*\.sql' | sed -r -e 's!^!ch/!' | sort -R) NEW_TESTS="$(grep -P 'tests/queries/0_stateless/.*\.sql' ci-changed-files.txt | sed -r -e 's!^!ch/!' | sort -R)"
cd ..
if [[ -n "$NEW_TESTS" ]] if [[ -n "$NEW_TESTS" ]]
then then
NEW_TESTS_OPT="--interleave-queries-file ${NEW_TESTS}" NEW_TESTS_OPT="${NEW_TESTS_OPT:---interleave-queries-file ${NEW_TESTS}}"
else else
NEW_TESTS_OPT="" NEW_TESTS_OPT="${NEW_TESTS_OPT:-}"
fi fi
./clickhouse-server --config-file db/config.xml -- --path db 2>&1 | tail -100000 > server.log & clickhouse-server --config-file db/config.xml -- --path db 2>&1 | tail -100000 > server.log &
server_pid=$! server_pid=$!
kill -0 $server_pid kill -0 $server_pid
while ! ./clickhouse-client --query "select 1" && kill -0 $server_pid ; do echo . ; sleep 1 ; done while ! clickhouse-client --query "select 1" && kill -0 $server_pid ; do echo . ; sleep 1 ; done
./clickhouse-client --query "select 1" clickhouse-client --query "select 1"
kill -0 $server_pid kill -0 $server_pid
echo Server started echo Server started
@ -111,14 +105,14 @@ continue
# SC2012: Use find instead of ls to better handle non-alphanumeric filenames. They are all alphanumeric. # SC2012: Use find instead of ls to better handle non-alphanumeric filenames. They are all alphanumeric.
# SC2046: Quote this to prevent word splitting. Actually I need word splitting. # SC2046: Quote this to prevent word splitting. Actually I need word splitting.
# shellcheck disable=SC2012,SC2046 # shellcheck disable=SC2012,SC2046
./clickhouse-client --query-fuzzer-runs=1000 --queries-file $(ls -1 ch/tests/queries/0_stateless/*.sql | sort -R) $NEW_TESTS_OPT \ clickhouse-client --query-fuzzer-runs=1000 --queries-file $(ls -1 ch/tests/queries/0_stateless/*.sql | sort -R) $NEW_TESTS_OPT \
> >(tail -n 100000 > fuzzer.log) \ > >(tail -n 100000 > fuzzer.log) \
2>&1 \ 2>&1 \
|| fuzzer_exit_code=$? || fuzzer_exit_code=$?
echo "Fuzzer exit code is $fuzzer_exit_code" echo "Fuzzer exit code is $fuzzer_exit_code"
./clickhouse-client --query "select elapsed, query from system.processes" ||: clickhouse-client --query "select elapsed, query from system.processes" ||:
killall clickhouse-server ||: killall clickhouse-server ||:
for _ in {1..10} for _ in {1..10}
do do

View File

@ -11,3 +11,10 @@ services:
default: default:
aliases: aliases:
- postgre-sql.local - postgre-sql.local
postgres2:
image: postgres
restart: always
environment:
POSTGRES_PASSWORD: mysecretpassword
ports:
- 5441:5432

View File

@ -2,7 +2,9 @@
set -exu set -exu
set -o pipefail set -o pipefail
trap "exit" INT TERM trap "exit" INT TERM
trap 'kill $(jobs -pr) ||:' EXIT # The watchdog is in the separate process group, so we have to kill it separately
# if the script terminates earlier.
trap 'kill $(jobs -pr) ${watchdog_pid:-} ||:' EXIT
stage=${stage:-} stage=${stage:-}
script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

View File

@ -233,7 +233,7 @@ Google OSS-Fuzz can be found at `docker/fuzz`.
We also use simple fuzz test to generate random SQL queries and to check that the server doesnt die executing them. We also use simple fuzz test to generate random SQL queries and to check that the server doesnt die executing them.
You can find it in `00746_sql_fuzzy.pl`. This test should be run continuously (overnight and longer). You can find it in `00746_sql_fuzzy.pl`. This test should be run continuously (overnight and longer).
We also use sophisticated AST-based query fuzzer that is able to find huge amount of corner cases. It does random permutations and substitutions in queries AST. It remembers AST nodes from previous tests to use them for fuzzing of subsequent tests while processing them in random order. We also use sophisticated AST-based query fuzzer that is able to find huge amount of corner cases. It does random permutations and substitutions in queries AST. It remembers AST nodes from previous tests to use them for fuzzing of subsequent tests while processing them in random order. You can learn more about this fuzzer in [this blog article](https://clickhouse.tech/blog/en/2021/fuzzing-clickhouse/).
## Stress test ## Stress test

View File

@ -353,7 +353,7 @@ The `set` index can be used with all functions. Function subsets for other index
| Function (operator) / Index | primary key | minmax | ngrambf_v1 | tokenbf_v1 | bloom_filter | | Function (operator) / Index | primary key | minmax | ngrambf_v1 | tokenbf_v1 | bloom_filter |
|------------------------------------------------------------------------------------------------------------|-------------|--------|-------------|-------------|---------------| |------------------------------------------------------------------------------------------------------------|-------------|--------|-------------|-------------|---------------|
| [equals (=, ==)](../../../sql-reference/functions/comparison-functions.md#function-equals) | ✔ | ✔ | ✔ | ✔ | ✔ | | [equals (=, ==)](../../../sql-reference/functions/comparison-functions.md#function-equals) | ✔ | ✔ | ✔ | ✔ | ✔ |
| [notEquals(!=, \<\>)](../../../sql-reference/functions/comparison-functions.md#function-notequals) | ✔ | ✔ | ✔ | ✔ | ✔ | | [notEquals(!=, <>)](../../../sql-reference/functions/comparison-functions.md#function-notequals) | ✔ | ✔ | ✔ | ✔ | ✔ |
| [like](../../../sql-reference/functions/string-search-functions.md#function-like) | ✔ | ✔ | ✔ | ✔ | ✗ | | [like](../../../sql-reference/functions/string-search-functions.md#function-like) | ✔ | ✔ | ✔ | ✔ | ✗ |
| [notLike](../../../sql-reference/functions/string-search-functions.md#function-notlike) | ✔ | ✔ | ✔ | ✔ | ✗ | | [notLike](../../../sql-reference/functions/string-search-functions.md#function-notlike) | ✔ | ✔ | ✔ | ✔ | ✗ |
| [startsWith](../../../sql-reference/functions/string-functions.md#startswith) | ✔ | ✔ | ✔ | ✔ | ✗ | | [startsWith](../../../sql-reference/functions/string-functions.md#startswith) | ✔ | ✔ | ✔ | ✔ | ✗ |
@ -361,10 +361,10 @@ The `set` index can be used with all functions. Function subsets for other index
| [multiSearchAny](../../../sql-reference/functions/string-search-functions.md#function-multisearchany) | ✗ | ✗ | ✔ | ✗ | ✗ | | [multiSearchAny](../../../sql-reference/functions/string-search-functions.md#function-multisearchany) | ✗ | ✗ | ✔ | ✗ | ✗ |
| [in](../../../sql-reference/functions/in-functions.md#in-functions) | ✔ | ✔ | ✔ | ✔ | ✔ | | [in](../../../sql-reference/functions/in-functions.md#in-functions) | ✔ | ✔ | ✔ | ✔ | ✔ |
| [notIn](../../../sql-reference/functions/in-functions.md#in-functions) | ✔ | ✔ | ✔ | ✔ | ✔ | | [notIn](../../../sql-reference/functions/in-functions.md#in-functions) | ✔ | ✔ | ✔ | ✔ | ✔ |
| [less (\<)](../../../sql-reference/functions/comparison-functions.md#function-less) | ✔ | ✔ | ✗ | ✗ | ✗ | | [less (<)](../../../sql-reference/functions/comparison-functions.md#function-less) | ✔ | ✔ | ✗ | ✗ | ✗ |
| [greater (\>)](../../../sql-reference/functions/comparison-functions.md#function-greater) | ✔ | ✔ | ✗ | ✗ | ✗ | | [greater (>)](../../../sql-reference/functions/comparison-functions.md#function-greater) | ✔ | ✔ | ✗ | ✗ | ✗ |
| [lessOrEquals (\<=)](../../../sql-reference/functions/comparison-functions.md#function-lessorequals) | ✔ | ✔ | ✗ | ✗ | ✗ | | [lessOrEquals (<=)](../../../sql-reference/functions/comparison-functions.md#function-lessorequals) | ✔ | ✔ | ✗ | ✗ | ✗ |
| [greaterOrEquals (\>=)](../../../sql-reference/functions/comparison-functions.md#function-greaterorequals) | ✔ | ✔ | ✗ | ✗ | ✗ | | [greaterOrEquals (>=)](../../../sql-reference/functions/comparison-functions.md#function-greaterorequals) | ✔ | ✔ | ✗ | ✗ | ✗ |
| [empty](../../../sql-reference/functions/array-functions.md#function-empty) | ✔ | ✔ | ✗ | ✗ | ✗ | | [empty](../../../sql-reference/functions/array-functions.md#function-empty) | ✔ | ✔ | ✗ | ✗ | ✗ |
| [notEmpty](../../../sql-reference/functions/array-functions.md#function-notempty) | ✔ | ✔ | ✗ | ✗ | ✗ | | [notEmpty](../../../sql-reference/functions/array-functions.md#function-notempty) | ✔ | ✔ | ✗ | ✗ | ✗ |
| hasToken | ✗ | ✗ | ✗ | ✔ | ✗ | | hasToken | ✗ | ✗ | ✗ | ✔ | ✗ |
@ -529,7 +529,7 @@ CREATE TABLE table_for_aggregation
y Int y Int
) )
ENGINE = MergeTree ENGINE = MergeTree
ORDER BY k1, k2 ORDER BY (k1, k2)
TTL d + INTERVAL 1 MONTH GROUP BY k1, k2 SET x = max(x), y = min(y); TTL d + INTERVAL 1 MONTH GROUP BY k1, k2 SET x = max(x), y = min(y);
``` ```
@ -748,7 +748,6 @@ Configuration markup:
</proxy> </proxy>
<connect_timeout_ms>10000</connect_timeout_ms> <connect_timeout_ms>10000</connect_timeout_ms>
<request_timeout_ms>5000</request_timeout_ms> <request_timeout_ms>5000</request_timeout_ms>
<max_connections>100</max_connections>
<retry_attempts>10</retry_attempts> <retry_attempts>10</retry_attempts>
<min_bytes_for_seek>1000</min_bytes_for_seek> <min_bytes_for_seek>1000</min_bytes_for_seek>
<metadata_path>/var/lib/clickhouse/disks/s3/</metadata_path> <metadata_path>/var/lib/clickhouse/disks/s3/</metadata_path>
@ -771,7 +770,6 @@ Optional parameters:
- `proxy` — Proxy configuration for S3 endpoint. Each `uri` element inside `proxy` block should contain a proxy URL. - `proxy` — Proxy configuration for S3 endpoint. Each `uri` element inside `proxy` block should contain a proxy URL.
- `connect_timeout_ms` — Socket connect timeout in milliseconds. Default value is `10 seconds`. - `connect_timeout_ms` — Socket connect timeout in milliseconds. Default value is `10 seconds`.
- `request_timeout_ms` — Request timeout in milliseconds. Default value is `5 seconds`. - `request_timeout_ms` — Request timeout in milliseconds. Default value is `5 seconds`.
- `max_connections` — S3 connections pool size. Default value is `100`.
- `retry_attempts` — Number of retry attempts in case of failed request. Default value is `10`. - `retry_attempts` — Number of retry attempts in case of failed request. Default value is `10`.
- `min_bytes_for_seek` — Minimal number of bytes to use seek operation instead of sequential read. Default value is `1 Mb`. - `min_bytes_for_seek` — Minimal number of bytes to use seek operation instead of sequential read. Default value is `1 Mb`.
- `metadata_path` — Path on local FS to store metadata files for S3. Default value is `/var/lib/clickhouse/disks/<disk_name>/`. - `metadata_path` — Path on local FS to store metadata files for S3. Default value is `/var/lib/clickhouse/disks/<disk_name>/`.

View File

@ -167,4 +167,21 @@ Features:
[How to configure ClickHouse in Looker.](https://docs.looker.com/setup-and-management/database-config/clickhouse) [How to configure ClickHouse in Looker.](https://docs.looker.com/setup-and-management/database-config/clickhouse)
### SeekTable {#seektable}
[SeekTable](https://www.seektable.com) is a self-service BI tool for data exploration and operational reporting. SeekTable is available both as a cloud service and a self-hosted version. SeekTable reports may be embedded into any web-app.
Features:
- Business users-friendly reports builder.
- Powerful report parameters for SQL filtering and report-specific query customizations.
- Can connect to ClickHouse both with a native TCP/IP endpoint and a HTTP(S) interface (2 different drivers).
- It is possible to use all power of CH SQL dialect in dimensions/measures definitions
- [Web API](https://www.seektable.com/help/web-api-integration) for automated reports generation.
- Supports reports development flow with account data [backup/restore](https://www.seektable.com/help/self-hosted-backup-restore), data models (cubes) / reports configuration is a human-readable XML and can be stored under version control.
SeekTable is [free](https://www.seektable.com/help/cloud-pricing) for personal/individual usage.
[How to configure ClickHouse connection in SeekTable.](https://www.seektable.com/help/clickhouse-pivot-table)
[Original article](https://clickhouse.tech/docs/en/interfaces/third-party/gui/) <!--hide--> [Original article](https://clickhouse.tech/docs/en/interfaces/third-party/gui/) <!--hide-->

View File

@ -11,3 +11,5 @@ ClickHouse supports authenticating and managing users using external services.
The following external authenticators and directories are supported: The following external authenticators and directories are supported:
- [LDAP](./ldap.md#external-authenticators-ldap) [Authenticator](./ldap.md#ldap-external-authenticator) and [Directory](./ldap.md#ldap-external-user-directory) - [LDAP](./ldap.md#external-authenticators-ldap) [Authenticator](./ldap.md#ldap-external-authenticator) and [Directory](./ldap.md#ldap-external-user-directory)
[Original article](https://clickhouse.tech/docs/en/operations/external-authenticators/index/) <!--hide-->

View File

@ -2,14 +2,16 @@
LDAP server can be used to authenticate ClickHouse users. There are two different approaches for doing this: LDAP server can be used to authenticate ClickHouse users. There are two different approaches for doing this:
- use LDAP as an external authenticator for existing users, which are defined in `users.xml` or in local access control paths - Use LDAP as an external authenticator for existing users, which are defined in `users.xml` or in local access control paths.
- use LDAP as an external user directory and allow locally undefined users to be authenticated if they exist on the LDAP server - Use LDAP as an external user directory and allow locally undefined users to be authenticated if they exist on the LDAP server.
For both of these approaches, an internally named LDAP server must be defined in the ClickHouse config so that other parts of config are able to refer to it. For both of these approaches, an internally named LDAP server must be defined in the ClickHouse config so that other parts of the config can refer to it.
## LDAP Server Definition {#ldap-server-definition} ## LDAP Server Definition {#ldap-server-definition}
To define LDAP server you must add `ldap_servers` section to the `config.xml`. For example, To define LDAP server you must add `ldap_servers` section to the `config.xml`.
**Example**
```xml ```xml
<yandex> <yandex>
@ -35,38 +37,35 @@ To define LDAP server you must add `ldap_servers` section to the `config.xml`. F
Note, that you can define multiple LDAP servers inside the `ldap_servers` section using distinct names. Note, that you can define multiple LDAP servers inside the `ldap_servers` section using distinct names.
Parameters: **Parameters**
- `host` - LDAP server hostname or IP, this parameter is mandatory and cannot be empty. - `host` — LDAP server hostname or IP, this parameter is mandatory and cannot be empty.
- `port` - LDAP server port, default is `636` if `enable_tls` is set to `true`, `389` otherwise. - `port` — LDAP server port, default is `636` if `enable_tls` is set to `true`, `389` otherwise.
- `bind_dn` - template used to construct the DN to bind to. - `bind_dn` — Template used to construct the DN to bind to.
- The resulting DN will be constructed by replacing all `{user_name}` substrings of the - The resulting DN will be constructed by replacing all `{user_name}` substrings of the template with the actual user name during each authentication attempt.
template with the actual user name during each authentication attempt. - `verification_cooldown` — A period of time, in seconds, after a successful bind attempt, during which the user will be assumed to be successfully authenticated for all consecutive requests without contacting the LDAP server.
- `verification_cooldown` - a period of time, in seconds, after a successful bind attempt,
during which the user will be assumed to be successfully authenticated for all consecutive
requests without contacting the LDAP server.
- Specify `0` (the default) to disable caching and force contacting the LDAP server for each authentication request. - Specify `0` (the default) to disable caching and force contacting the LDAP server for each authentication request.
- `enable_tls` - flag to trigger use of secure connection to the LDAP server. - `enable_tls` — A flag to trigger the use of the secure connection to the LDAP server.
- Specify `no` for plain text `ldap://` protocol (not recommended). - Specify `no` for plain text `ldap://` protocol (not recommended).
- Specify `yes` for LDAP over SSL/TLS `ldaps://` protocol (recommended, the default). - Specify `yes` for LDAP over SSL/TLS `ldaps://` protocol (recommended, the default).
- Specify `starttls` for legacy StartTLS protocol (plain text `ldap://` protocol, upgraded to TLS). - Specify `starttls` for legacy StartTLS protocol (plain text `ldap://` protocol, upgraded to TLS).
- `tls_minimum_protocol_version` - the minimum protocol version of SSL/TLS. - `tls_minimum_protocol_version` — The minimum protocol version of SSL/TLS.
- Accepted values are: `ssl2`, `ssl3`, `tls1.0`, `tls1.1`, `tls1.2` (the default). - Accepted values are: `ssl2`, `ssl3`, `tls1.0`, `tls1.1`, `tls1.2` (the default).
- `tls_require_cert` - SSL/TLS peer certificate verification behavior. - `tls_require_cert` SSL/TLS peer certificate verification behavior.
- Accepted values are: `never`, `allow`, `try`, `demand` (the default). - Accepted values are: `never`, `allow`, `try`, `demand` (the default).
- `tls_cert_file` - path to certificate file. - `tls_cert_file` — Path to certificate file.
- `tls_key_file` - path to certificate key file. - `tls_key_file` — Path to certificate key file.
- `tls_ca_cert_file` - path to CA certificate file. - `tls_ca_cert_file` — Path to CA certificate file.
- `tls_ca_cert_dir` - path to the directory containing CA certificates. - `tls_ca_cert_dir` — Path to the directory containing CA certificates.
- `tls_cipher_suite` - allowed cipher suite (in OpenSSL notation). - `tls_cipher_suite` — Allowed cipher suite (in OpenSSL notation).
## LDAP External Authenticator {#ldap-external-authenticator} ## LDAP External Authenticator {#ldap-external-authenticator}
A remote LDAP server can be used as a method for verifying passwords for locally defined users (users defined in `users.xml` or in local access control paths). In order to achieve this, specify previously defined LDAP server name instead of `password` or similar sections in the user definition. A remote LDAP server can be used as a method for verifying passwords for locally defined users (users defined in `users.xml` or in local access control paths). To achieve this, specify previously defined LDAP server name instead of `password` or similar sections in the user definition.
At each login attempt, ClickHouse will try to "bind" to the specified DN defined by the `bind_dn` parameter in the [LDAP server definition](#ldap-server-definition) using the provided credentials, and if successful, the user will be considered authenticated. This is often called a "simple bind" method. At each login attempt, ClickHouse tries to "bind" to the specified DN defined by the `bind_dn` parameter in the [LDAP server definition](#ldap-server-definition) using the provided credentials, and if successful, the user is considered authenticated. This is often called a "simple bind" method.
For example, **Example**
```xml ```xml
<yandex> <yandex>
@ -85,19 +84,24 @@ For example,
Note, that user `my_user` refers to `my_ldap_server`. This LDAP server must be configured in the main `config.xml` file as described previously. Note, that user `my_user` refers to `my_ldap_server`. This LDAP server must be configured in the main `config.xml` file as described previously.
When SQL-driven [Access Control and Account Management](../access-rights.md#access-control) is enabled in ClickHouse, users that are authenticated by LDAP servers can also be created using the [CRATE USER](../../sql-reference/statements/create/user.md#create-user-statement) statement. When SQL-driven [Access Control and Account Management](../access-rights.md#access-control) is enabled, users that are authenticated by LDAP servers can also be created using the [CREATE USER](../../sql-reference/statements/create/user.md#create-user-statement) statement.
Query:
```sql ```sql
CREATE USER my_user IDENTIFIED WITH ldap SERVER 'my_ldap_server' CREATE USER my_user IDENTIFIED WITH ldap SERVER 'my_ldap_server';
``` ```
## LDAP Exernal User Directory {#ldap-external-user-directory} ## LDAP Exernal User Directory {#ldap-external-user-directory}
In addition to the locally defined users, a remote LDAP server can be used as a source of user definitions. In order to achieve this, specify previously defined LDAP server name (see [LDAP Server Definition](#ldap-server-definition)) in an `ldap` section inside the `users_directories` section of the `config.xml` file. In addition to the locally defined users, a remote LDAP server can be used as a source of user definitions. To achieve this, specify previously defined LDAP server name (see [LDAP Server Definition](#ldap-server-definition)) in the `ldap` section inside the `users_directories` section of the `config.xml` file.
At each login attempt, ClickHouse will try to find the user definition locally and authenticate it as usual, but if the user is not defined, ClickHouse will assume it exists in the external LDAP directory, and will try to "bind" to the specified DN at the LDAP server using the provided credentials. If successful, the user will be considered existing and authenticated. The user will be assigned roles from the list specified in the `roles` section. Additionally, LDAP "search" can be performed and results can be transformed and treated as role names and then be assigned to the user if the `role_mapping` section is also configured. All this implies that the SQL-driven [Access Control and Account Management](../access-rights.md#access-control) is enabled and roles are created using the [CREATE ROLE](../../sql-reference/statements/create/role.md#create-role-statement) statement. At each login attempt, ClickHouse tries to find the user definition locally and authenticate it as usual. If the user is not defined, ClickHouse will assume the definition exists in the external LDAP directory and will try to "bind" to the specified DN at the LDAP server using the provided credentials. If successful, the user will be considered existing and authenticated. The user will be assigned roles from the list specified in the `roles` section. Additionally, LDAP "search" can be performed and results can be transformed and treated as role names and then be assigned to the user if the `role_mapping` section is also configured. All this implies that the SQL-driven [Access Control and Account Management](../access-rights.md#access-control) is enabled and roles are created using the [CREATE ROLE](../../sql-reference/statements/create/role.md#create-role-statement) statement.
Example (goes into `config.xml`): **Example**
Goes into `config.xml`.
```xml ```xml
<yandex> <yandex>
@ -122,33 +126,24 @@ Example (goes into `config.xml`):
</yandex> </yandex>
``` ```
Note that `my_ldap_server` referred in the `ldap` section inside the `user_directories` section must be a previously Note that `my_ldap_server` referred in the `ldap` section inside the `user_directories` section must be a previously defined LDAP server that is configured in the `config.xml` (see [LDAP Server Definition](#ldap-server-definition)).
defined LDAP server that is configured in the `config.xml` (see [LDAP Server Definition](#ldap-server-definition)).
Parameters: **Parameters**
- `server` - one of LDAP server names defined in the `ldap_servers` config section above. - `server` — One of LDAP server names defined in the `ldap_servers` config section above. This parameter is mandatory and cannot be empty.
This parameter is mandatory and cannot be empty. - `roles` — Section with a list of locally defined roles that will be assigned to each user retrieved from the LDAP server.
- `roles` - section with a list of locally defined roles that will be assigned to each user retrieved from the LDAP server. - If no roles are specified here or assigned during role mapping (below), user will not be able to perform any actions after authentication.
- If no roles are specified here or assigned during role mapping (below), user will not be able - `role_mapping` — Section with LDAP search parameters and mapping rules.
to perform any actions after authentication. - When a user authenticates, while still bound to LDAP, an LDAP search is performed using `search_filter` and the name of the logged-in user. For each entry found during that search, the value of the specified attribute is extracted. For each attribute value that has the specified prefix, the prefix is removed, and the rest of the value becomes the name of a local role defined in ClickHouse, which is expected to be created beforehand by the [CREATE ROLE](../../sql-reference/statements/create/role.md#create-role-statement) statement.
- `role_mapping` - section with LDAP search parameters and mapping rules.
- When a user authenticates, while still bound to LDAP, an LDAP search is performed using `search_filter`
and the name of the logged in user. For each entry found during that search, the value of the specified
attribute is extracted. For each attribute value that has the specified prefix, the prefix is removed,
and the rest of the value becomes the name of a local role defined in ClickHouse,
which is expected to be created beforehand by the [CREATE ROLE](../../sql-reference/statements/create/role.md#create-role-statement) statement.
- There can be multiple `role_mapping` sections defined inside the same `ldap` section. All of them will be applied. - There can be multiple `role_mapping` sections defined inside the same `ldap` section. All of them will be applied.
- `base_dn` - template used to construct the base DN for the LDAP search. - `base_dn` — Template used to construct the base DN for the LDAP search.
- The resulting DN will be constructed by replacing all `{user_name}` and `{bind_dn}` - The resulting DN will be constructed by replacing all `{user_name}` and `{bind_dn}` substrings of the template with the actual user name and bind DN during each LDAP search.
substrings of the template with the actual user name and bind DN during each LDAP search. - `scope` — Scope of the LDAP search.
- `scope` - scope of the LDAP search.
- Accepted values are: `base`, `one_level`, `children`, `subtree` (the default). - Accepted values are: `base`, `one_level`, `children`, `subtree` (the default).
- `search_filter` - template used to construct the search filter for the LDAP search. - `search_filter` — Template used to construct the search filter for the LDAP search.
- The resulting filter will be constructed by replacing all `{user_name}`, `{bind_dn}`, and `{base_dn}` - The resulting filter will be constructed by replacing all `{user_name}`, `{bind_dn}` and `{base_dn}` substrings of the template with the actual user name, bind DN and base DN during each LDAP search.
substrings of the template with the actual user name, bind DN, and base DN during each LDAP search.
- Note, that the special characters must be escaped properly in XML. - Note, that the special characters must be escaped properly in XML.
- `attribute` - attribute name whose values will be returned by the LDAP search. - `attribute` — Attribute name whose values will be returned by the LDAP search.
- `prefix` - prefix, that will be expected to be in front of each string in the original - `prefix` — Prefix, that will be expected to be in front of each string in the original list of strings returned by the LDAP search. The prefix will be removed from the original strings and the resulting strings will be treated as local role names. Empty by default.
list of strings returned by the LDAP search. Prefix will be removed from the original
strings and resulting strings will be treated as local role names. Empty, by default. [Original article](https://clickhouse.tech/docs/en/operations/external-authenticators/ldap/) <!--hide-->

View File

@ -7,11 +7,15 @@ Columns:
- `name` ([String](../../sql-reference/data-types/string.md)) — name of the error (`errorCodeToName`). - `name` ([String](../../sql-reference/data-types/string.md)) — name of the error (`errorCodeToName`).
- `code` ([Int32](../../sql-reference/data-types/int-uint.md)) — code number of the error. - `code` ([Int32](../../sql-reference/data-types/int-uint.md)) — code number of the error.
- `value` ([UInt64](../../sql-reference/data-types/int-uint.md)) — the number of times this error has been happened. - `value` ([UInt64](../../sql-reference/data-types/int-uint.md)) — the number of times this error has been happened.
- `last_error_time` ([DateTime](../../sql-reference/data-types/datetime.md)) — time when the last error happened.
- `last_error_message` ([String](../../sql-reference/data-types/string.md)) — message for the last error.
- `last_error_stacktrace` ([String](../../sql-reference/data-types/string.md)) — stacktrace for the last error.
- `remote` ([UInt8](../../sql-reference/data-types/int-uint.md)) — remote exception (i.e. received during one of the distributed query).
**Example** **Example**
``` sql ``` sql
SELECT * SELECT name, code, value
FROM system.errors FROM system.errors
WHERE value > 0 WHERE value > 0
ORDER BY code ASC ORDER BY code ASC

View File

@ -9,7 +9,7 @@ toc_title: Machine Learning
Prediction using fitted regression models uses `evalMLMethod` function. See link in `linearRegression`. Prediction using fitted regression models uses `evalMLMethod` function. See link in `linearRegression`.
## stochasticLinearRegressionn {#stochastic-linear-regression} ## stochasticLinearRegression {#stochastic-linear-regression}
The [stochasticLinearRegression](../../sql-reference/aggregate-functions/reference/stochasticlinearregression.md#agg_functions-stochasticlinearregression) aggregate function implements stochastic gradient descent method using linear model and MSE loss function. Uses `evalMLMethod` to predict on new data. The [stochasticLinearRegression](../../sql-reference/aggregate-functions/reference/stochasticlinearregression.md#agg_functions-stochasticlinearregression) aggregate function implements stochastic gradient descent method using linear model and MSE loss function. Uses `evalMLMethod` to predict on new data.

View File

@ -191,7 +191,7 @@ Renames an existing column.
Syntax: Syntax:
```sql ```sql
ALTER TABLE table_name RENAME COLUMN column_name TO new_column_name; ALTER TABLE table_name RENAME COLUMN column_name TO new_column_name
``` ```
**Example** **Example**

View File

@ -17,4 +17,8 @@ If the table was previously detached ([DETACH](../../sql-reference/statements/de
ATTACH TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] ATTACH TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster]
``` ```
This query is used when starting the server. The server stores table metadata as files with `ATTACH` queries, which it simply runs at launch (with the exception of system tables, which are explicitly created on the server). This query is used when starting the server. The server stores table metadata as files with `ATTACH` queries, which it simply runs at launch (with the exception of some system tables, which are explicitly created on the server).
If the table was detached permanently, it won't be reattached at the server start, so you need to use `ATTACH` query explicitly.
[Original article](https://clickhouse.tech/docs/en/sql-reference/statements/attach/) <!--hide-->

View File

@ -5,12 +5,66 @@ toc_title: DETACH
# DETACH Statement {#detach} # DETACH Statement {#detach}
Deletes information about the name table from the server. The server stops knowing about the tables existence. Makes the server "forget" about the existence of the table or materialized view.
Syntax:
``` sql ``` sql
DETACH TABLE [IF EXISTS] [db.]name [ON CLUSTER cluster] DETACH TABLE|VIEW [IF EXISTS] [db.]name [PERMANENTLY] [ON CLUSTER cluster]
``` ```
This does not delete the tables data or metadata. On the next server launch, the server will read the metadata and find out about the table again. Detaching does not delete the data or metadata for the table or materialized view. If the table or view was not detached `PERMANENTLY`, on the next server launch the server will read the metadata and recall the table/view again. If the table or view was detached `PERMANENTLY`, there will be no automatic recall.
Similarly, a “detached” table can be re-attached using the `ATTACH` query (with the exception of system tables, which do not have metadata stored for them). Whether the table was detached permanently or not, in both cases you can reattach it using the [ATTACH](../../sql-reference/statements/attach.md). System log tables can be also attached back (e.g. `query_log`, `text_log`, etc). Other system tables can't be reattached. On the next server launch the server will recall those tables again.
`ATTACH MATERIALIZED VIEW` doesn't work with short syntax (without `SELECT`), but you can attach it using the `ATTACH TABLE` query.
Note that you can not detach permanently the table which is already detached (temporary). But you can attach it back and then detach permanently again.
Also you can not [DROP](../../sql-reference/statements/drop.md#drop-table) the detached table, or [CREATE TABLE](../../sql-reference/statements/create/table.md) with the same name as detached permanently, or replace it with the other table with [RENAME TABLE](../../sql-reference/statements/rename.md) query.
**Example**
Creating a table:
Query:
``` sql
CREATE TABLE test ENGINE = Log AS SELECT * FROM numbers(10);
SELECT * FROM test;
```
Result:
``` text
┌─number─┐
│ 0 │
│ 1 │
│ 2 │
│ 3 │
│ 4 │
│ 5 │
│ 6 │
│ 7 │
│ 8 │
│ 9 │
└────────┘
```
Detaching the table:
Query:
``` sql
DETACH TABLE test;
SELECT * FROM test;
```
Result:
``` text
Received exception from server (version 21.4.1):
Code: 60. DB::Exception: Received from localhost:9000. DB::Exception: Table default.test doesn't exist.
```
[Original article](https://clickhouse.tech/docs/en/sql-reference/statements/detach/) <!--hide-->

View File

@ -6,7 +6,7 @@ toc_title: OPTIMIZE
# OPTIMIZE Statement {#misc_operations-optimize} # OPTIMIZE Statement {#misc_operations-optimize}
``` sql ``` sql
OPTIMIZE TABLE [db.]name [ON CLUSTER cluster] [PARTITION partition | PARTITION ID 'partition_id'] [FINAL] [DEDUPLICATE] OPTIMIZE TABLE [db.]name [ON CLUSTER cluster] [PARTITION partition | PARTITION ID 'partition_id'] [FINAL] [DEDUPLICATE [BY expression]]
``` ```
This query tries to initialize an unscheduled merge of data parts for tables with a table engine from the [MergeTree](../../engines/table-engines/mergetree-family/mergetree.md) family. This query tries to initialize an unscheduled merge of data parts for tables with a table engine from the [MergeTree](../../engines/table-engines/mergetree-family/mergetree.md) family.
@ -18,7 +18,69 @@ When `OPTIMIZE` is used with the [ReplicatedMergeTree](../../engines/table-engin
- If `OPTIMIZE` doesnt perform a merge for any reason, it doesnt notify the client. To enable notifications, use the [optimize_throw_if_noop](../../operations/settings/settings.md#setting-optimize_throw_if_noop) setting. - If `OPTIMIZE` doesnt perform a merge for any reason, it doesnt notify the client. To enable notifications, use the [optimize_throw_if_noop](../../operations/settings/settings.md#setting-optimize_throw_if_noop) setting.
- If you specify a `PARTITION`, only the specified partition is optimized. [How to set partition expression](../../sql-reference/statements/alter/index.md#alter-how-to-specify-part-expr). - If you specify a `PARTITION`, only the specified partition is optimized. [How to set partition expression](../../sql-reference/statements/alter/index.md#alter-how-to-specify-part-expr).
- If you specify `FINAL`, optimization is performed even when all the data is already in one part. Also merge is forced even if concurrent merges are performed. - If you specify `FINAL`, optimization is performed even when all the data is already in one part. Also merge is forced even if concurrent merges are performed.
- If you specify `DEDUPLICATE`, then completely identical rows will be deduplicated (all columns are compared), it makes sense only for the MergeTree engine. - If you specify `DEDUPLICATE`, then completely identical rows (unless by-clause is specified) will be deduplicated (all columns are compared), it makes sense only for the MergeTree engine.
### BY expression {#by-expression}
If you want to perform deduplication on custom set of columns rather than on all, you can specify list of columns explicitly or use any combination of [`*`](../../sql-reference/statements/select/index.md#asterisk), [`COLUMNS`](../../sql-reference/statements/select/index.md#columns-expression) or [`EXCEPT`](../../sql-reference/statements/select/index.md#except-modifier) expressions. The explictly written or implicitly expanded list of columns must include all columns specified in row ordering expression (both primary and sorting keys) and partitioning expression (partitioning key).
Note that `*` behaves just like in `SELECT`: `MATERIALIZED`, and `ALIAS` columns are not used for expansion.
Also, it is an error to specify empty list of columns, or write an expression that results in an empty list of columns, or deduplicate by an ALIAS column.
``` sql
OPTIMIZE TABLE table DEDUPLICATE; -- the old one
OPTIMIZE TABLE table DEDUPLICATE BY *; -- not the same as the old one, excludes MATERIALIZED columns (see the note above)
OPTIMIZE TABLE table DEDUPLICATE BY * EXCEPT colX;
OPTIMIZE TABLE table DEDUPLICATE BY * EXCEPT (colX, colY);
OPTIMIZE TABLE table DEDUPLICATE BY col1,col2,col3;
OPTIMIZE TABLE table DEDUPLICATE BY COLUMNS('column-matched-by-regex');
OPTIMIZE TABLE table DEDUPLICATE BY COLUMNS('column-matched-by-regex') EXCEPT colX;
OPTIMIZE TABLE table DEDUPLICATE BY COLUMNS('column-matched-by-regex') EXCEPT (colX, colY);
```
**Example:**
A silly synthetic table.
``` sql
CREATE TABLE example (
primary_key Int32,
secondary_key Int32,
value UInt32,
partition_key UInt32,
materialized_value UInt32 MATERIALIZED 12345,
aliased_value UInt32 ALIAS 2,
PRIMARY KEY primary_key
) ENGINE=MergeTree
PARTITION BY partition_key
ORDER BY (primary_key, secondary_key);
```
``` sql
-- The 'old' deduplicate, all columns are taken into account, i.e. row is removed only if all values in all columns are equal to corresponding values in previous row.
OPTIMIZE TABLE example FINAL DEDUPLICATE;
```
``` sql
-- Deduplicate by all columns that are not `ALIAS` or `MATERIALIZED`: `primary_key`, `secondary_key`, `value`, `partition_key`, and `materialized_value` columns.
OPTIMIZE TABLE example FINAL DEDUPLICATE BY *;
```
``` sql
-- Deduplicate by all columns that are not `ALIAS` or `MATERIALIZED` and explicitly not `materialized_value`: `primary_key`, `secondary_key`, `value`, and `partition_key` columns.
OPTIMIZE TABLE example FINAL DEDUPLICATE BY * EXCEPT materialized_value;
```
``` sql
-- Deduplicate explicitly by `primary_key`, `secondary_key`, and `partition_key` columns.
OPTIMIZE TABLE example FINAL DEDUPLICATE BY primary_key, secondary_key, partition_key;
```
``` sql
-- Deduplicate by any column matching a regex: `primary_key`, `secondary_key`, and `partition_key` columns.
OPTIMIZE TABLE example FINAL DEDUPLICATE BY COLUMNS('.*_key');
```
!!! warning "Warning" !!! warning "Warning"
`OPTIMIZE` cant fix the “Too many parts” error. `OPTIMIZE` cant fix the “Too many parts” error.

View File

@ -57,6 +57,9 @@ Specifics of each optional clause are covered in separate sections, which are li
If you want to include all columns in the result, use the asterisk (`*`) symbol. For example, `SELECT * FROM ...`. If you want to include all columns in the result, use the asterisk (`*`) symbol. For example, `SELECT * FROM ...`.
### COLUMNS expression {#columns-expression}
To match some columns in the result with a [re2](https://en.wikipedia.org/wiki/RE2_(software)) regular expression, you can use the `COLUMNS` expression. To match some columns in the result with a [re2](https://en.wikipedia.org/wiki/RE2_(software)) regular expression, you can use the `COLUMNS` expression.
``` sql ``` sql

View File

@ -517,7 +517,7 @@ CREATE TABLE table_for_aggregation
y Int y Int
) )
ENGINE = MergeTree ENGINE = MergeTree
ORDER BY k1, k2 ORDER BY (k1, k2)
TTL d + INTERVAL 1 MONTH GROUP BY k1, k2 SET x = max(x), y = min(y); TTL d + INTERVAL 1 MONTH GROUP BY k1, k2 SET x = max(x), y = min(y);
``` ```
@ -733,7 +733,6 @@ SETTINGS storage_policy = 'moving_from_ssd_to_hdd'
</proxy> </proxy>
<connect_timeout_ms>10000</connect_timeout_ms> <connect_timeout_ms>10000</connect_timeout_ms>
<request_timeout_ms>5000</request_timeout_ms> <request_timeout_ms>5000</request_timeout_ms>
<max_connections>100</max_connections>
<retry_attempts>10</retry_attempts> <retry_attempts>10</retry_attempts>
<min_bytes_for_seek>1000</min_bytes_for_seek> <min_bytes_for_seek>1000</min_bytes_for_seek>
<metadata_path>/var/lib/clickhouse/disks/s3/</metadata_path> <metadata_path>/var/lib/clickhouse/disks/s3/</metadata_path>
@ -758,7 +757,6 @@ SETTINGS storage_policy = 'moving_from_ssd_to_hdd'
- `proxy` — конфигурация прокси-сервера для конечной точки S3. Каждый элемент `uri` внутри блока `proxy` должен содержать URL прокси-сервера. - `proxy` — конфигурация прокси-сервера для конечной точки S3. Каждый элемент `uri` внутри блока `proxy` должен содержать URL прокси-сервера.
- `connect_timeout_ms` — таймаут подключения к сокету в миллисекундах. Значение по умолчанию: 10 секунд. - `connect_timeout_ms` — таймаут подключения к сокету в миллисекундах. Значение по умолчанию: 10 секунд.
- `request_timeout_ms` — таймаут выполнения запроса в миллисекундах. Значение по умолчанию: 5 секунд. - `request_timeout_ms` — таймаут выполнения запроса в миллисекундах. Значение по умолчанию: 5 секунд.
- `max_connections` — размер пула соединений S3. Значение по умолчанию: `100`.
- `retry_attempts` — число попыток выполнения запроса в случае возникновения ошибки. Значение по умолчанию: `10`. - `retry_attempts` — число попыток выполнения запроса в случае возникновения ошибки. Значение по умолчанию: `10`.
- `min_bytes_for_seek` — минимальное количество байтов, которые используются для операций поиска вместо последовательного чтения. Значение по умолчанию: 1 МБайт. - `min_bytes_for_seek` — минимальное количество байтов, которые используются для операций поиска вместо последовательного чтения. Значение по умолчанию: 1 МБайт.
- `metadata_path` — путь к локальному файловому хранилищу для хранения файлов с метаданными для S3. Значение по умолчанию: `/var/lib/clickhouse/disks/<disk_name>/`. - `metadata_path` — путь к локальному файловому хранилищу для хранения файлов с метаданными для S3. Значение по умолчанию: `/var/lib/clickhouse/disks/<disk_name>/`.

View File

@ -0,0 +1,15 @@
---
toc_folder_title: "\u0412\u043d\u0435\u0448\u043d\u0438\u0435\u0020\u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u044b\u0020\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439\u0020\u0438\u0020\u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0438"
toc_priority: 48
toc_title: "\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435"
---
# Внешние аутентификаторы пользователей и каталоги {#external-authenticators}
ClickHouse поддерживает аутентификацию и управление пользователями при помощи внешних сервисов.
Поддерживаются следующие внешние аутентификаторы и каталоги:
- [LDAP](./ldap.md#external-authenticators-ldap) [аутентификатор](./ldap.md#ldap-external-authenticator) и [каталог](./ldap.md#ldap-external-user-directory)
[Оригинальная статья](https://clickhouse.tech/docs/ru/operations/external-authenticators/index/) <!--hide-->

View File

@ -0,0 +1,148 @@
# LDAP {#external-authenticators-ldap}
Для аутентификации пользователей ClickHouse можно использовать сервер LDAP. Существуют два подхода:
- Использовать LDAP как внешний аутентификатор для существующих пользователей, которые определены в `users.xml`, или в локальных параметрах управления доступом.
- Использовать LDAP как внешний пользовательский каталог и разрешить аутентификацию локально неопределенных пользователей, если они есть на LDAP сервере.
Для обоих подходов необходимо определить внутреннее имя LDAP сервера в конфигурации ClickHouse, чтобы другие параметры конфигурации могли ссылаться на это имя.
## Определение LDAP сервера {#ldap-server-definition}
Чтобы определить LDAP сервер, необходимо добавить секцию `ldap_servers` в `config.xml`.
**Пример**
```xml
<yandex>
<!- ... -->
<ldap_servers>
<my_ldap_server>
<host>localhost</host>
<port>636</port>
<bind_dn>uid={user_name},ou=users,dc=example,dc=com</bind_dn>
<verification_cooldown>300</verification_cooldown>
<enable_tls>yes</enable_tls>
<tls_minimum_protocol_version>tls1.2</tls_minimum_protocol_version>
<tls_require_cert>demand</tls_require_cert>
<tls_cert_file>/path/to/tls_cert_file</tls_cert_file>
<tls_key_file>/path/to/tls_key_file</tls_key_file>
<tls_ca_cert_file>/path/to/tls_ca_cert_file</tls_ca_cert_file>
<tls_ca_cert_dir>/path/to/tls_ca_cert_dir</tls_ca_cert_dir>
<tls_cipher_suite>ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384</tls_cipher_suite>
</my_ldap_server>
</ldap_servers>
</yandex>
```
Обратите внимание, что можно определить несколько LDAP серверов внутри секции `ldap_servers`, используя различные имена.
**Параметры**
- `host` — имя хоста сервера LDAP или его IP. Этот параметр обязательный и не может быть пустым.
- `port` — порт сервера LDAP. Если настройка `enable_tls` равна `true`, то по умолчанию используется порт `636`, иначе — порт `389`.
- `bind_dn` — шаблон для создания DN для привязки.
- При формировании DN все подстроки `{user_name}` в шаблоне будут заменяться на фактическое имя пользователя при каждой попытке аутентификации.
- `verification_cooldown` — промежуток времени (в секундах) после успешной попытки привязки, в течение которого пользователь будет считаться аутентифицированным и сможет выполнять запросы без повторного обращения к серверам LDAP.
- Чтобы отключить кеширование и заставить обращаться к серверу LDAP для каждого запроса аутентификации, укажите `0` (значение по умолчанию).
- `enable_tls` — флаг, включающий использование защищенного соединения с сервером LDAP.
- Укажите `no` для использования текстового протокола `ldap://` (не рекомендовано).
- Укажите `yes` для обращения к LDAP по протоколу SSL/TLS `ldaps://` (рекомендовано, используется по умолчанию).
- Укажите `starttls` для использования устаревшего протокола StartTLS (текстовый `ldap://` протокол, модернизированный до TLS).
- `tls_minimum_protocol_version` — минимальная версия протокола SSL/TLS.
- Возможные значения: `ssl2`, `ssl3`, `tls1.0`, `tls1.1`, `tls1.2` (по-умолчанию).
- `tls_require_cert` — поведение при проверке сертификата SSL/TLS.
- Возможные значения: `never`, `allow`, `try`, `demand` (по-умолчанию).
- `tls_cert_file` — путь к файлу сертификата.
- `tls_key_file` — путь к файлу ключа сертификата.
- `tls_ca_cert_file` — путь к файлу ЦС (certification authority) сертификата.
- `tls_ca_cert_dir` — путь к каталогу, содержащему сертификаты ЦС.
- `tls_cipher_suite` — разрешенный набор шифров (в нотации OpenSSL).
## Внешний аутентификатор LDAP {#ldap-external-authenticator}
Удаленный сервер LDAP можно использовать для верификации паролей локально определенных пользователей (пользователей, которые определены в `users.xml` или в локальных параметрах управления доступом). Для этого укажите имя определенного ранее сервера LDAP вместо `password` или другой аналогичной секции в настройках пользователя.
При каждой попытке авторизации ClickHouse пытается "привязаться" к DN, указанному в [определении LDAP сервера](#ldap-server-definition), используя параметр `bind_dn` и предоставленные реквизиты для входа. Если попытка оказалась успешной, пользователь считается аутентифицированным. Обычно это называют методом "простой привязки".
**Пример**
```xml
<yandex>
<!- ... -->
<users>
<!- ... -->
<my_user>
<!- ... -->
<ldap>
<server>my_ldap_server</server>
</ldap>
</my_user>
</users>
</yandex>
```
Обратите внимание, что пользователь `my_user` ссылается на `my_ldap_server`. Этот LDAP сервер должен быть настроен в основном файле `config.xml`, как это было описано ранее.
При включенном SQL-ориентированном [управлении доступом](../access-rights.md#access-control) пользователи, аутентифицированные LDAP серверами, могут также быть созданы запросом [CREATE USER](../../sql-reference/statements/create/user.md#create-user-statement).
Запрос:
```sql
CREATE USER my_user IDENTIFIED WITH ldap SERVER 'my_ldap_server';
```
## Внешний пользовательский каталог LDAP {#ldap-external-user-directory}
В дополнение к локально определенным пользователям, удаленный LDAP сервер может служить источником определения пользователей. Для этого укажите имя определенного ранее сервера LDAP (см. [Определение LDAP сервера](#ldap-server-definition)) в секции `ldap` внутри секции `users_directories` файла `config.xml`.
При каждой попытке аутентификации ClickHouse пытается локально найти определение пользователя и аутентифицировать его как обычно. Если пользователь не находится локально, ClickHouse предполагает, что он определяется во внешнем LDAP каталоге и пытается "привязаться" к DN, указанному на LDAP сервере, используя предоставленные реквизиты для входа. Если попытка оказалась успешной, пользователь считается существующим и аутентифицированным. Пользователю присваиваются роли из списка, указанного в секции `roles`. Кроме того, если настроена секция `role_mapping`, то выполняется LDAP поиск, а его результаты преобразуются в имена ролей и присваиваются пользователям. Все это работает при условии, что SQL-ориентированное [управлением доступом](../access-rights.md#access-control) включено, а роли созданы запросом [CREATE ROLE](../../sql-reference/statements/create/role.md#create-role-statement).
**Пример**
В `config.xml`.
```xml
<yandex>
<!- ... -->
<user_directories>
<!- ... -->
<ldap>
<server>my_ldap_server</server>
<roles>
<my_local_role1 />
<my_local_role2 />
</roles>
<role_mapping>
<base_dn>ou=groups,dc=example,dc=com</base_dn>
<scope>subtree</scope>
<search_filter>(&amp;(objectClass=groupOfNames)(member={bind_dn}))</search_filter>
<attribute>cn</attribute>
<prefix>clickhouse_</prefix>
</role_mapping>
</ldap>
</user_directories>
</yandex>
```
Обратите внимание, что `my_ldap_server`, указанный в секции `ldap` внутри секции `user_directories`, должен быть настроен в файле `config.xml`, как это было описано ранее. (см. [Определение LDAP сервера](#ldap-server-definition)).
**Параметры**
- `server` — имя одного из серверов LDAP, определенных в секции `ldap_servers` в файле конфигурации (см.выше). Этот параметр обязательный и не может быть пустым.
- `roles` — секция со списком локально определенных ролей, которые будут присвоены каждому пользователю, полученному от сервера LDAP.
- Если роли не указаны ни здесь, ни в секции `role_mapping` (см. ниже), пользователь после аутентификации не сможет выполнять никаких действий.
- `role_mapping` — секция c параметрами LDAP поиска и правилами отображения.
- При аутентификации пользователя, пока еще связанного с LDAP, производится LDAP поиск с помощью `search_filter` и имени этого пользователя. Для каждой записи, найденной в ходе поиска, выделяется значение указанного атрибута. У каждого атрибута, имеющего указанный префикс, этот префикс удаляется, а остальная часть значения становится именем локальной роли, определенной в ClickHouse, причем предполагается, что эта роль была ранее создана запросом [CREATE ROLE](../../sql-reference/statements/create/role.md#create-role-statement) до этого.
- Внутри одной секции `ldap` может быть несколько секций `role_mapping`. Все они будут применены.
- `base_dn` — шаблон, который используется для создания базового DN для LDAP поиска.
- При формировании DN все подстроки `{user_name}` и `{bind_dn}` в шаблоне будут заменяться на фактическое имя пользователя и DN привязки соответственно при каждом LDAP поиске.
- `scope` — Область LDAP поиска.
- Возможные значения: `base`, `one_level`, `children`, `subtree` (по умолчанию).
- `search_filter` — шаблон, который используется для создания фильтра для каждого LDAP поиска.
- при формировании фильтра все подстроки `{user_name}`, `{bind_dn}` и `{base_dn}` в шаблоне будут заменяться на фактическое имя пользователя, DN привязки и базовый DN соответственно при каждом LDAP поиске.
- Обратите внимание, что специальные символы должны быть правильно экранированы в XML.
- `attribute` — имя атрибута, значение которого будет возвращаться LDAP поиском.
- `prefix` — префикс, который, как предполагается, будет находиться перед началом каждой строки в исходном списке строк, возвращаемых LDAP поиском. Префикс будет удален из исходных строк, а сами они будут рассматриваться как имена локальных ролей. По умолчанию: пустая строка.
[Оригинальная статья](https://clickhouse.tech/docs/en/operations/external-authenticators/ldap) <!--hide-->

View File

@ -13,6 +13,7 @@ toc_title: "Манипуляции со столбцами"
- [COMMENT COLUMN](#alter_comment-column) — добавляет комментарий к столбцу; - [COMMENT COLUMN](#alter_comment-column) — добавляет комментарий к столбцу;
- [MODIFY COLUMN](#alter_modify-column) — изменяет тип столбца, выражение для значения по умолчанию и TTL. - [MODIFY COLUMN](#alter_modify-column) — изменяет тип столбца, выражение для значения по умолчанию и TTL.
- [MODIFY COLUMN REMOVE](#modify-remove) — удаляет какое-либо из свойств столбца. - [MODIFY COLUMN REMOVE](#modify-remove) — удаляет какое-либо из свойств столбца.
- [RENAME COLUMN](#alter_rename-column) — переименовывает существующий столбец.
Подробное описание для каждого действия приведено ниже. Подробное описание для каждого действия приведено ниже.
@ -158,6 +159,22 @@ ALTER TABLE table_with_ttl MODIFY COLUMN column_ttl REMOVE TTL;
- [REMOVE TTL](ttl.md). - [REMOVE TTL](ttl.md).
## RENAME COLUMN {#alter_rename-column}
Переименовывает существующий столбец.
Синтаксис:
```sql
ALTER TABLE table_name RENAME COLUMN column_name TO new_column_name
```
**Пример**
```sql
ALTER TABLE table_with_ttl RENAME COLUMN column_ttl TO column_ttl_new;
```
## Ограничения запроса ALTER {#ogranicheniia-zaprosa-alter} ## Ограничения запроса ALTER {#ogranicheniia-zaprosa-alter}
Запрос `ALTER` позволяет создавать и удалять отдельные элементы (столбцы) вложенных структур данных, но не вложенные структуры данных целиком. Для добавления вложенной структуры данных, вы можете добавить столбцы с именем вида `name.nested_name` и типом `Array(T)` - вложенная структура данных полностью эквивалентна нескольким столбцам-массивам с именем, имеющим одинаковый префикс до точки. Запрос `ALTER` позволяет создавать и удалять отдельные элементы (столбцы) вложенных структур данных, но не вложенные структуры данных целиком. Для добавления вложенной структуры данных, вы можете добавить столбцы с именем вида `name.nested_name` и типом `Array(T)` - вложенная структура данных полностью эквивалентна нескольким столбцам-массивам с именем, имеющим одинаковый префикс до точки.

View File

@ -10,13 +10,14 @@ toc_title: ATTACH
- вместо слова `CREATE` используется слово `ATTACH`; - вместо слова `CREATE` используется слово `ATTACH`;
- запрос не создаёт данные на диске, а предполагает, что данные уже лежат в соответствующих местах, и всего лишь добавляет информацию о таблице на сервер. После выполнения запроса `ATTACH` сервер будет знать о существовании таблицы. - запрос не создаёт данные на диске, а предполагает, что данные уже лежат в соответствующих местах, и всего лишь добавляет информацию о таблице на сервер. После выполнения запроса `ATTACH` сервер будет знать о существовании таблицы.
Если таблица перед этим была отсоединена (`DETACH`), т.е. её структура известна, можно использовать сокращенную форму записи без определения структуры. Если таблица перед этим была отключена ([DETACH](../../sql-reference/statements/detach.md)), т.е. её структура известна, можно использовать сокращенную форму записи без определения структуры.
``` sql ``` sql
ATTACH TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] ATTACH TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster]
``` ```
Этот запрос используется при старте сервера. Сервер хранит метаданные таблиц в виде файлов с запросами `ATTACH`, которые он просто исполняет при запуске (за исключением системных таблиц, которые явно создаются на сервере). Этот запрос используется при старте сервера. Сервер хранит метаданные таблиц в виде файлов с запросами `ATTACH`, которые он просто исполняет при запуске (за исключением некоторых системных таблиц, которые явно создаются на сервере).
Если таблица была отключена перманентно, она не будет подключена обратно во время старта сервера, так что нужно явно использовать запрос `ATTACH`, чтобы подключить ее.

View File

@ -5,14 +5,65 @@ toc_title: DETACH
# DETACH {#detach-statement} # DETACH {#detach-statement}
Удаляет из сервера информацию о таблице name. Сервер перестаёт знать о существовании таблицы. Заставляет сервер "забыть" о существовании таблицы или материализованного представления.
Синтаксис:
``` sql ``` sql
DETACH TABLE [IF EXISTS] [db.]name [ON CLUSTER cluster] DETACH TABLE|VIEW [IF EXISTS] [db.]name [PERMANENTLY] [ON CLUSTER cluster]
``` ```
Но ни данные, ни метаданные таблицы не удаляются. При следующем запуске сервера, сервер прочитает метаданные и снова узнает о таблице. Но ни данные, ни метаданные таблицы или материализованного представления не удаляются. При следующем запуске сервера, если не было использовано `PERMANENTLY`, сервер прочитает метаданные и снова узнает о таблице/представлении. Если таблица или представление были отключены перманентно, сервер не подключит их обратно автоматически.
Также, «отцепленную» таблицу можно прицепить заново запросом `ATTACH` (за исключением системных таблиц, для которых метаданные не хранятся).
Запроса `DETACH DATABASE` нет. Независимо от того, каким способом таблица была отключена, ее можно подключить обратно с помощью запроса [ATTACH](../../sql-reference/statements/attach.md). Системные log таблицы также могут быть подключены обратно (к примеру `query_log`, `text_log` и др.) Другие системные таблицы не могут быть подключены обратно, но на следующем запуске сервер снова "вспомнит" об этих таблицах.
`ATTACH MATERIALIZED VIEW` не может быть использован с кратким синтаксисом (без `SELECT`), но можно подключить представление с помощью запроса `ATTACH TABLE`.
Обратите внимание, что нельзя перманентно отключить таблицу, которая уже временно отключена. Для этого ее сначала надо подключить обратно, а затем снова отключить перманентно.
Также нельзя использовать [DROP](../../sql-reference/statements/drop.md#drop-table) с отключенной таблицей или создавать таблицу с помощью [CREATE TABLE](../../sql-reference/statements/create/table.md) с таким же именем, как у отключенной таблицы. Еще нельзя заменить отключенную таблицу другой с помощью запроса [RENAME TABLE](../../sql-reference/statements/rename.md).
**Пример**
Создание таблицы:
Запрос:
``` sql
CREATE TABLE test ENGINE = Log AS SELECT * FROM numbers(10);
SELECT * FROM test;
```
Результат:
``` text
┌─number─┐
│ 0 │
│ 1 │
│ 2 │
│ 3 │
│ 4 │
│ 5 │
│ 6 │
│ 7 │
│ 8 │
│ 9 │
└────────┘
```
Отключение таблицы:
Запрос:
``` sql
DETACH TABLE test;
SELECT * FROM test;
```
Результат:
``` text
Received exception from server (version 21.4.1):
Code: 60. DB::Exception: Received from localhost:9000. DB::Exception: Table default.test doesn't exist.
```

View File

@ -325,7 +325,7 @@ else ()
endif () endif ()
if (ENABLE_TESTS AND USE_GTEST) if (ENABLE_TESTS AND USE_GTEST)
set (CLICKHOUSE_UNIT_TESTS_TARGETS unit_tests_libcommon unit_tests_dbms) set (CLICKHOUSE_UNIT_TESTS_TARGETS unit_tests_dbms)
add_custom_target (clickhouse-tests ALL DEPENDS ${CLICKHOUSE_UNIT_TESTS_TARGETS}) add_custom_target (clickhouse-tests ALL DEPENDS ${CLICKHOUSE_UNIT_TESTS_TARGETS})
add_dependencies(clickhouse-bundle clickhouse-tests) add_dependencies(clickhouse-bundle clickhouse-tests)
endif() endif()

View File

@ -65,6 +65,7 @@
#include <Parsers/ASTSetQuery.h> #include <Parsers/ASTSetQuery.h>
#include <Parsers/ASTUseQuery.h> #include <Parsers/ASTUseQuery.h>
#include <Parsers/ASTInsertQuery.h> #include <Parsers/ASTInsertQuery.h>
#include <Parsers/ASTSelectQuery.h>
#include <Parsers/ASTSelectWithUnionQuery.h> #include <Parsers/ASTSelectWithUnionQuery.h>
#include <Parsers/ASTQueryWithOutput.h> #include <Parsers/ASTQueryWithOutput.h>
#include <Parsers/ASTLiteral.h> #include <Parsers/ASTLiteral.h>
@ -116,6 +117,34 @@ namespace ErrorCodes
} }
static bool queryHasWithClause(const IAST * ast)
{
if (const auto * select = dynamic_cast<const ASTSelectQuery *>(ast);
select && select->with())
{
return true;
}
// This full recursive walk is somewhat excessive, because most of the
// children are not queries, but on the other hand it will let us to avoid
// breakage when the AST structure changes and some new variant of query
// nesting is added. This function is used in fuzzer, so it's better to be
// defensive and avoid weird unexpected errors.
// clang-tidy is confused by this function: it thinks that if `select` is
// nullptr, `ast` is also nullptr, and complains about nullptr dereference.
// NOLINTNEXTLINE
for (const auto & child : ast->children)
{
if (queryHasWithClause(child.get()))
{
return true;
}
}
return false;
}
class Client : public Poco::Util::Application class Client : public Poco::Util::Application
{ {
public: public:
@ -390,7 +419,7 @@ private:
for (auto d : chineseNewYearIndicators) for (auto d : chineseNewYearIndicators)
{ {
/// Let's celebrate until Lantern Festival /// Let's celebrate until Lantern Festival
if (d <= days && d + 25u >= days) if (d <= days && d + 25 >= days)
return true; return true;
else if (d > days) else if (d > days)
return false; return false;
@ -1255,6 +1284,29 @@ private:
return true; return true;
} }
// Prints changed settings to stderr. Useful for debugging fuzzing failures.
void printChangedSettings() const
{
const auto & changes = context.getSettingsRef().changes();
if (!changes.empty())
{
fmt::print(stderr, "Changed settings: ");
for (size_t i = 0; i < changes.size(); ++i)
{
if (i)
{
fmt::print(stderr, ", ");
}
fmt::print(stderr, "{} = '{}'", changes[i].name,
toString(changes[i].value));
}
fmt::print(stderr, "\n");
}
else
{
fmt::print(stderr, "No changed settings.\n");
}
}
/// Returns false when server is not available. /// Returns false when server is not available.
bool processWithFuzzing(const String & text) bool processWithFuzzing(const String & text)
@ -1317,9 +1369,14 @@ private:
auto base_after_fuzz = fuzz_base->formatForErrorMessage(); auto base_after_fuzz = fuzz_base->formatForErrorMessage();
// Debug AST cloning errors. // Check that the source AST didn't change after fuzzing. This
// helps debug AST cloning errors, where the cloned AST doesn't
// clone all its children, and erroneously points to some source
// child elements.
if (base_before_fuzz != base_after_fuzz) if (base_before_fuzz != base_after_fuzz)
{ {
printChangedSettings();
fmt::print(stderr, fmt::print(stderr,
"Base before fuzz: {}\n" "Base before fuzz: {}\n"
"Base after fuzz: {}\n", "Base after fuzz: {}\n",
@ -1334,7 +1391,7 @@ private:
fmt::print(stderr, "IAST::clone() is broken for some AST node. This is a bug. The original AST ('dump before fuzz') and its cloned copy ('dump of cloned AST') refer to the same nodes, which must never happen. This means that their parent node doesn't implement clone() correctly."); fmt::print(stderr, "IAST::clone() is broken for some AST node. This is a bug. The original AST ('dump before fuzz') and its cloned copy ('dump of cloned AST') refer to the same nodes, which must never happen. This means that their parent node doesn't implement clone() correctly.");
assert(false); exit(1);
} }
auto fuzzed_text = ast_to_process->formatForErrorMessage(); auto fuzzed_text = ast_to_process->formatForErrorMessage();
@ -1378,29 +1435,76 @@ private:
// Print the changed settings because they might be needed to // Print the changed settings because they might be needed to
// reproduce the error. // reproduce the error.
const auto & changes = context.getSettingsRef().changes(); printChangedSettings();
if (!changes.empty())
{
fmt::print(stderr, "Changed settings: ");
for (size_t i = 0; i < changes.size(); ++i)
{
if (i)
{
fmt::print(stderr, ", ");
}
fmt::print(stderr, "{} = '{}'", changes[i].name,
toString(changes[i].value));
}
fmt::print(stderr, "\n");
}
else
{
fmt::print(stderr, "No changed settings.\n");
}
return false; return false;
} }
// Check that after the query is formatted, we can parse it back,
// format again and get the same result. Unfortunately, we can't
// compare the ASTs, which would be more sensitive to errors. This
// double formatting check doesn't catch all errors, e.g. we can
// format query incorrectly, but to a valid SQL that we can then
// parse and format into the same SQL.
// There are some complicated cases where we can generate the SQL
// which we can't parse:
// * first argument of lambda() replaced by fuzzer with
// something else, leading to constructs such as
// arrayMap((min(x) + 3) -> x + 1, ....)
// * internals of Enum replaced, leading to:
// Enum(equals(someFunction(y), 3)).
// And there are even the cases when we can parse the query, but
// it's logically incorrect and its formatting is a mess, such as
// when `lambda()` function gets substituted into a wrong place.
// To avoid dealing with these cases, run the check only for the
// queries we were able to successfully execute.
// The final caveat is that sometimes WITH queries are not executed,
// if they are not referenced by the main SELECT, so they can still
// have the aforementioned problems. Disable this check for such
// queries, for lack of a better solution.
if (!have_error && queryHasWithClause(parsed_query.get()))
{
ASTPtr parsed_formatted_query;
try
{
const auto * tmp_pos = query_to_send.c_str();
parsed_formatted_query = parseQuery(tmp_pos,
tmp_pos + query_to_send.size(),
false /* allow_multi_statements */);
}
catch (Exception & e)
{
if (e.code() != ErrorCodes::SYNTAX_ERROR)
{
throw;
}
}
if (parsed_formatted_query)
{
const auto formatted_twice
= parsed_formatted_query->formatForErrorMessage();
if (formatted_twice != query_to_send)
{
fmt::print(stderr, "The query formatting is broken.\n");
printChangedSettings();
fmt::print(stderr, "Got the following (different) text after formatting the fuzzed query and parsing it back:\n'{}'\n, expected:\n'{}'\n",
formatted_twice, query_to_send);
fmt::print(stderr, "In more detail:\n");
fmt::print(stderr, "AST-1:\n'{}'\n", parsed_query->dumpTree());
fmt::print(stderr, "Text-1 (AST-1 formatted):\n'{}'\n", query_to_send);
fmt::print(stderr, "AST-2 (Text-1 parsed):\n'{}'\n", parsed_formatted_query->dumpTree());
fmt::print(stderr, "Text-2 (AST-2 formatted):\n'{}'\n", formatted_twice);
fmt::print(stderr, "Text-1 must be equal to Text-2, but it is not.\n");
exit(1);
}
}
}
// The server is still alive so we're going to continue fuzzing. // The server is still alive so we're going to continue fuzzing.
// Determine what we're going to use as the starting AST. // Determine what we're going to use as the starting AST.
if (have_error) if (have_error)

View File

@ -570,6 +570,15 @@ void QueryFuzzer::addColumnLike(const ASTPtr ast)
} }
const auto name = ast->formatForErrorMessage(); const auto name = ast->formatForErrorMessage();
if (name == "Null")
{
// The `Null` identifier from FORMAT Null clause. We don't quote it
// properly when formatting the AST, and while the resulting query
// technically works, it has non-standard case for Null (the standard
// is NULL), so it breaks the query formatting idempotence check.
// Just plug this particular case for now.
return;
}
if (name.size() < 200) if (name.size() < 200)
{ {
column_like_map.insert({name, ast}); column_like_map.insert({name, ast});

View File

@ -1064,7 +1064,7 @@ void processCommit(
time_t commit_time; time_t commit_time;
readText(commit_time, in); readText(commit_time, in);
commit.time = commit_time; commit.time = LocalDateTime(commit_time);
assertChar('\0', in); assertChar('\0', in);
readNullTerminated(commit.author, in); readNullTerminated(commit.author, in);
std::string parent_hash; std::string parent_hash;

View File

@ -1180,7 +1180,7 @@ try
file_in.seek(0, SEEK_SET); file_in.seek(0, SEEK_SET);
BlockInputStreamPtr input = context.getInputFormat(input_format, file_in, header, max_block_size); BlockInputStreamPtr input = context.getInputFormat(input_format, file_in, header, max_block_size);
BlockOutputStreamPtr output = context.getOutputStream(output_format, file_out, header); BlockOutputStreamPtr output = context.getOutputStreamParallelIfPossible(output_format, file_out, header);
if (processed_rows + source_rows > limit) if (processed_rows + source_rows > limit)
input = std::make_shared<LimitBlockInputStream>(input, limit - processed_rows, 0); input = std::make_shared<LimitBlockInputStream>(input, limit - processed_rows, 0);

View File

@ -176,7 +176,7 @@ void ODBCHandler::handleRequest(HTTPServerRequest & request, HTTPServerResponse
std::string query = params.get("query"); std::string query = params.get("query");
LOG_TRACE(log, "Query: {}", query); LOG_TRACE(log, "Query: {}", query);
BlockOutputStreamPtr writer = FormatFactory::instance().getOutputStream(format, out, *sample_block, context); BlockOutputStreamPtr writer = FormatFactory::instance().getOutputStreamParallelIfPossible(format, out, *sample_block, context);
auto pool = getPool(connection_string); auto pool = getPool(connection_string);
ODBCBlockInputStream inp(pool->get(), query, *sample_block, max_block_size); ODBCBlockInputStream inp(pool->get(), query, *sample_block, max_block_size);
copyData(inp, *writer); copyData(inp, *writer);

View File

@ -87,8 +87,8 @@ namespace
case ValueType::vtDateTime: case ValueType::vtDateTime:
{ {
Poco::DateTime datetime = value.convert<Poco::DateTime>(); Poco::DateTime datetime = value.convert<Poco::DateTime>();
assert_cast<ColumnUInt32 &>(column).insertValue(time_t{LocalDateTime( assert_cast<ColumnUInt32 &>(column).insertValue(DateLUT::instance().makeDateTime(
datetime.year(), datetime.month(), datetime.day(), datetime.hour(), datetime.minute(), datetime.second())}); datetime.year(), datetime.month(), datetime.day(), datetime.hour(), datetime.minute(), datetime.second()));
break; break;
} }
case ValueType::vtUUID: case ValueType::vtUUID:

View File

@ -81,7 +81,7 @@ namespace
case ValueType::vtDate: case ValueType::vtDate:
return Poco::Dynamic::Var(LocalDate(DayNum(field.get<UInt64>())).toString()).convert<String>(); return Poco::Dynamic::Var(LocalDate(DayNum(field.get<UInt64>())).toString()).convert<String>();
case ValueType::vtDateTime: case ValueType::vtDateTime:
return Poco::Dynamic::Var(std::to_string(LocalDateTime(time_t(field.get<UInt64>())))).convert<String>(); return Poco::Dynamic::Var(DateLUT::instance().timeToString(time_t(field.get<UInt64>()))).convert<String>();
case ValueType::vtUUID: case ValueType::vtUUID:
return Poco::Dynamic::Var(UUID(field.get<UInt128>()).toUnderType().toHexString()).convert<std::string>(); return Poco::Dynamic::Var(UUID(field.get<UInt128>()).toUnderType().toHexString()).convert<std::string>();
default: default:

View File

@ -306,10 +306,10 @@
/// Incremental request number. When response is received, /// Incremental request number. When response is received,
/// if it's request number does not equal to the current request number, response will be ignored. /// if it's request number does not equal to the current request number, response will be ignored.
/// This is to avoid race conditions. /// This is to avoid race conditions.
var request_num = 0; let request_num = 0;
/// Save query in history only if it is different. /// Save query in history only if it is different.
var previous_query = ''; let previous_query = '';
/// Substitute the address of the server where the page is served. /// Substitute the address of the server where the page is served.
if (location.protocol != 'file:') { if (location.protocol != 'file:') {
@ -317,7 +317,7 @@
} }
/// Substitute user name if it's specified in the query string /// Substitute user name if it's specified in the query string
var user_from_url = (new URL(window.location)).searchParams.get('user'); let user_from_url = (new URL(window.location)).searchParams.get('user');
if (user_from_url) { if (user_from_url) {
document.getElementById('user').value = user_from_url; document.getElementById('user').value = user_from_url;
} }
@ -326,10 +326,10 @@
{ {
/// TODO: Check if URL already contains query string (append parameters). /// TODO: Check if URL already contains query string (append parameters).
var user = document.getElementById('user').value; let user = document.getElementById('user').value;
var password = document.getElementById('password').value; let password = document.getElementById('password').value;
var url = document.getElementById('url').value + let url = document.getElementById('url').value +
/// Ask server to allow cross-domain requests. /// Ask server to allow cross-domain requests.
'?add_http_cors_header=1' + '?add_http_cors_header=1' +
'&user=' + encodeURIComponent(user) + '&user=' + encodeURIComponent(user) +
@ -338,7 +338,7 @@
/// Safety settings to prevent results that browser cannot display. /// Safety settings to prevent results that browser cannot display.
'&max_result_rows=1000&max_result_bytes=10000000&result_overflow_mode=break'; '&max_result_rows=1000&max_result_bytes=10000000&result_overflow_mode=break';
var xhr = new XMLHttpRequest; let xhr = new XMLHttpRequest;
xhr.open('POST', url, true); xhr.open('POST', url, true);
@ -352,13 +352,13 @@
/// The query is saved in browser history (in state JSON object) /// The query is saved in browser history (in state JSON object)
/// as well as in URL fragment identifier. /// as well as in URL fragment identifier.
if (query != previous_query) { if (query != previous_query) {
var state = { let state = {
query: query, query: query,
status: this.status, status: this.status,
response: this.response.length > 100000 ? null : this.response /// Lower than the browser's limit. response: this.response.length > 100000 ? null : this.response /// Lower than the browser's limit.
}; };
var title = "ClickHouse Query: " + query; let title = "ClickHouse Query: " + query;
var url = window.location.pathname + '?user=' + encodeURIComponent(user) + '#' + window.btoa(query); let url = window.location.pathname + '?user=' + encodeURIComponent(user) + '#' + window.btoa(query);
if (previous_query == '') { if (previous_query == '') {
history.replaceState(state, title, url); history.replaceState(state, title, url);
} else { } else {
@ -382,7 +382,7 @@
document.getElementById('hourglass').style.display = 'none'; document.getElementById('hourglass').style.display = 'none';
if (status === 200) { if (status === 200) {
var json; let json;
try { json = JSON.parse(response); } catch (e) {} try { json = JSON.parse(response); } catch (e) {}
if (json !== undefined && json.statistics !== undefined) { if (json !== undefined && json.statistics !== undefined) {
renderResult(json); renderResult(json);
@ -415,7 +415,7 @@
function post() function post()
{ {
++request_num; ++request_num;
var query = document.getElementById('query').value; let query = document.getElementById('query').value;
postImpl(request_num, query); postImpl(request_num, query);
} }
@ -434,7 +434,7 @@
function clear() function clear()
{ {
var table = document.getElementById('data-table'); let table = document.getElementById('data-table');
while (table.firstChild) { while (table.firstChild) {
table.removeChild(table.lastChild); table.removeChild(table.lastChild);
} }
@ -456,33 +456,45 @@
//console.log(response); //console.log(response);
clear(); clear();
var stats = document.getElementById('stats'); let stats = document.getElementById('stats');
stats.innerText = 'Elapsed: ' + response.statistics.elapsed.toFixed(3) + " sec, read " + response.statistics.rows_read + " rows."; stats.innerText = 'Elapsed: ' + response.statistics.elapsed.toFixed(3) + " sec, read " + response.statistics.rows_read + " rows.";
var thead = document.createElement('thead'); let thead = document.createElement('thead');
for (var idx in response.meta) { for (let idx in response.meta) {
var th = document.createElement('th'); let th = document.createElement('th');
var name = document.createTextNode(response.meta[idx].name); let name = document.createTextNode(response.meta[idx].name);
th.appendChild(name); th.appendChild(name);
thead.appendChild(th); thead.appendChild(th);
} }
/// To prevent hanging the browser, limit the number of cells in a table. /// To prevent hanging the browser, limit the number of cells in a table.
/// It's important to have the limit on number of cells, not just rows, because tables may be wide or narrow. /// It's important to have the limit on number of cells, not just rows, because tables may be wide or narrow.
var max_rows = 10000 / response.meta.length; let max_rows = 10000 / response.meta.length;
var row_num = 0; let row_num = 0;
var tbody = document.createElement('tbody'); let column_classes = response.meta.map(elem => elem.type.match(/^(U?Int|Decimal|Float)/) ? 'right' : 'left');
for (var row_idx in response.data) {
var tr = document.createElement('tr'); let tbody = document.createElement('tbody');
for (var col_idx in response.data[row_idx]) { for (let row_idx in response.data) {
var td = document.createElement('td'); let tr = document.createElement('tr');
var cell = response.data[row_idx][col_idx]; for (let col_idx in response.data[row_idx]) {
var is_null = (cell === null); let td = document.createElement('td');
var content = document.createTextNode(is_null ? 'ᴺᵁᴸᴸ' : cell); let cell = response.data[row_idx][col_idx];
td.appendChild(content);
/// TODO: Execute regexp only once for each column. let is_null = (cell === null);
td.className = response.meta[col_idx].type.match(/^(U?Int|Decimal|Float)/) ? 'right' : 'left';
/// Test: SELECT number, toString(number) AS str, number % 2 ? number : NULL AS nullable, range(number) AS arr, CAST((['hello', 'world'], [number, number % 2]) AS Map(String, UInt64)) AS map FROM numbers(10)
let text;
if (is_null) {
text = 'ᴺᵁᴸᴸ';
} else if (typeof(cell) === 'object') {
text = JSON.stringify(cell);
} else {
text = cell;
}
td.appendChild(document.createTextNode(text));
td.className = column_classes[col_idx];
if (is_null) { if (is_null) {
td.className += ' null'; td.className += ' null';
} }
@ -496,7 +508,7 @@
} }
} }
var table = document.getElementById('data-table'); let table = document.getElementById('data-table');
table.appendChild(thead); table.appendChild(thead);
table.appendChild(tbody); table.appendChild(tbody);
} }
@ -505,7 +517,7 @@
function renderUnparsedResult(response) function renderUnparsedResult(response)
{ {
clear(); clear();
var data = document.getElementById('data-unparsed') let data = document.getElementById('data-unparsed')
if (response === '') { if (response === '') {
/// TODO: Fade or remove previous result when new request will be performed. /// TODO: Fade or remove previous result when new request will be performed.
@ -531,12 +543,12 @@
} }
/// The choice of color theme is saved in browser. /// The choice of color theme is saved in browser.
var theme = window.localStorage.getItem('theme'); let theme = window.localStorage.getItem('theme');
if (theme) { if (theme) {
setColorTheme(theme); setColorTheme(theme);
} else { } else {
/// Obtain system-level user preference /// Obtain system-level user preference
var media_query_list = window.matchMedia('prefers-color-scheme: dark') let media_query_list = window.matchMedia('prefers-color-scheme: dark')
if (media_query_list.matches) { if (media_query_list.matches) {
/// Set without saving to localstorage /// Set without saving to localstorage

View File

@ -459,6 +459,8 @@ endif()
dbms_target_link_libraries(PRIVATE _boost_context) dbms_target_link_libraries(PRIVATE _boost_context)
include (${ClickHouse_SOURCE_DIR}/cmake/add_check.cmake)
if (ENABLE_TESTS AND USE_GTEST) if (ENABLE_TESTS AND USE_GTEST)
macro (grep_gtest_sources BASE_DIR DST_VAR) macro (grep_gtest_sources BASE_DIR DST_VAR)
# Cold match files that are not in tests/ directories # Cold match files that are not in tests/ directories
@ -479,6 +481,15 @@ if (ENABLE_TESTS AND USE_GTEST)
-Wno-gnu-zero-variadic-macro-arguments -Wno-gnu-zero-variadic-macro-arguments
) )
target_link_libraries(unit_tests_dbms PRIVATE ${GTEST_BOTH_LIBRARIES} clickhouse_functions clickhouse_aggregate_functions clickhouse_parsers dbms clickhouse_common_zookeeper string_utils) target_link_libraries(unit_tests_dbms PRIVATE
${GTEST_BOTH_LIBRARIES}
clickhouse_functions
clickhouse_aggregate_functions
clickhouse_parsers
clickhouse_storages_system
dbms
clickhouse_common_zookeeper
string_utils)
add_check(unit_tests_dbms) add_check(unit_tests_dbms)
endif () endif ()

View File

@ -1,4 +1,5 @@
#include <Common/ErrorCodes.h> #include <Common/ErrorCodes.h>
#include <chrono>
/** Previously, these constants were located in one enum. /** Previously, these constants were located in one enum.
* But in this case there is a problem: when you add a new constant, you need to recompile * But in this case there is a problem: when you add a new constant, you need to recompile
@ -547,6 +548,7 @@
M(578, INVALID_FORMAT_INSERT_QUERY_WITH_DATA) \ M(578, INVALID_FORMAT_INSERT_QUERY_WITH_DATA) \
M(579, INCORRECT_PART_TYPE) \ M(579, INCORRECT_PART_TYPE) \
\ \
M(998, POSTGRESQL_CONNECTION_FAILURE) \
M(999, KEEPER_EXCEPTION) \ M(999, KEEPER_EXCEPTION) \
M(1000, POCO_EXCEPTION) \ M(1000, POCO_EXCEPTION) \
M(1001, STD_EXCEPTION) \ M(1001, STD_EXCEPTION) \
@ -562,8 +564,8 @@ namespace ErrorCodes
APPLY_FOR_ERROR_CODES(M) APPLY_FOR_ERROR_CODES(M)
#undef M #undef M
constexpr Value END = 3000; constexpr ErrorCode END = 3000;
std::atomic<Value> values[END + 1]{}; ErrorPairHolder values[END + 1]{};
struct ErrorCodesNames struct ErrorCodesNames
{ {
@ -578,12 +580,43 @@ namespace ErrorCodes
std::string_view getName(ErrorCode error_code) std::string_view getName(ErrorCode error_code)
{ {
if (error_code >= END) if (error_code < 0 || error_code >= END)
return std::string_view(); return std::string_view();
return error_codes_names.names[error_code]; return error_codes_names.names[error_code];
} }
ErrorCode end() { return END + 1; } ErrorCode end() { return END + 1; }
void increment(ErrorCode error_code, bool remote, const std::string & message, const std::string & stacktrace)
{
if (error_code >= end())
{
/// For everything outside the range, use END.
/// (end() is the pointer pass the end, while END is the last value that has an element in values array).
error_code = end() - 1;
}
values[error_code].increment(remote, message, stacktrace);
}
void ErrorPairHolder::increment(bool remote, const std::string & message, const std::string & stacktrace)
{
const auto now = std::chrono::system_clock::now();
std::lock_guard lock(mutex);
auto & error = remote ? value.remote : value.local;
++error.count;
error.message = message;
error.stacktrace = stacktrace;
error.error_time_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
}
ErrorPair ErrorPairHolder::get()
{
std::lock_guard lock(mutex);
return value;
}
} }
} }

View File

@ -3,7 +3,7 @@
#include <stddef.h> #include <stddef.h>
#include <cstdint> #include <cstdint>
#include <utility> #include <utility>
#include <atomic> #include <mutex>
#include <common/types.h> #include <common/types.h>
#include <string_view> #include <string_view>
@ -17,30 +17,50 @@ namespace DB
namespace ErrorCodes namespace ErrorCodes
{ {
/// ErrorCode identifier (index in array). /// ErrorCode identifier (index in array).
using ErrorCode = size_t; using ErrorCode = int;
using Value = int; using Value = size_t;
/// Get name of error_code by identifier. /// Get name of error_code by identifier.
/// Returns statically allocated string. /// Returns statically allocated string.
std::string_view getName(ErrorCode error_code); std::string_view getName(ErrorCode error_code);
struct Error
{
/// Number of times Exception with this ErrorCode had been throw.
Value count;
/// Time of the last error.
UInt64 error_time_ms = 0;
/// Message for the last error.
std::string message;
/// Stacktrace for the last error.
std::string stacktrace;
};
struct ErrorPair
{
Error local;
Error remote;
};
/// Thread-safe
struct ErrorPairHolder
{
public:
ErrorPair get();
void increment(bool remote, const std::string & message, const std::string & stacktrace);
private:
ErrorPair value;
std::mutex mutex;
};
/// ErrorCode identifier -> current value of error_code. /// ErrorCode identifier -> current value of error_code.
extern std::atomic<Value> values[]; extern ErrorPairHolder values[];
/// Get index just after last error_code identifier. /// Get index just after last error_code identifier.
ErrorCode end(); ErrorCode end();
/// Add value for specified error_code. /// Add value for specified error_code.
inline void increment(ErrorCode error_code) void increment(ErrorCode error_code, bool remote, const std::string & message, const std::string & stacktrace);
{
if (error_code >= end())
{
/// For everything outside the range, use END.
/// (end() is the pointer pass the end, while END is the last value that has an element in values array).
error_code = end() - 1;
}
values[error_code].fetch_add(1, std::memory_order_relaxed);
}
} }
} }

View File

@ -34,9 +34,9 @@ namespace ErrorCodes
extern const int CANNOT_MREMAP; extern const int CANNOT_MREMAP;
} }
/// Aborts the process if error code is LOGICAL_ERROR. /// - Aborts the process if error code is LOGICAL_ERROR.
/// Increments error codes statistics. /// - Increments error codes statistics.
void handle_error_code([[maybe_unused]] const std::string & msg, int code) void handle_error_code([[maybe_unused]] const std::string & msg, const std::string & stacktrace, int code, bool remote)
{ {
// In debug builds and builds with sanitizers, treat LOGICAL_ERROR as an assertion failure. // In debug builds and builds with sanitizers, treat LOGICAL_ERROR as an assertion failure.
// Log the message before we fail. // Log the message before we fail.
@ -47,20 +47,20 @@ void handle_error_code([[maybe_unused]] const std::string & msg, int code)
abort(); abort();
} }
#endif #endif
ErrorCodes::increment(code); ErrorCodes::increment(code, remote, msg, stacktrace);
} }
Exception::Exception(const std::string & msg, int code, bool remote_) Exception::Exception(const std::string & msg, int code, bool remote_)
: Poco::Exception(msg, code) : Poco::Exception(msg, code)
, remote(remote_) , remote(remote_)
{ {
handle_error_code(msg, code); handle_error_code(msg, getStackTraceString(), code, remote);
} }
Exception::Exception(const std::string & msg, const Exception & nested, int code) Exception::Exception(const std::string & msg, const Exception & nested, int code)
: Poco::Exception(msg, nested, code) : Poco::Exception(msg, nested, code)
{ {
handle_error_code(msg, code); handle_error_code(msg, getStackTraceString(), code, remote);
} }
Exception::Exception(CreateFromPocoTag, const Poco::Exception & exc) Exception::Exception(CreateFromPocoTag, const Poco::Exception & exc)

View File

@ -180,7 +180,17 @@ String FieldVisitorToString::operator() (const Tuple & x) const
{ {
WriteBufferFromOwnString wb; WriteBufferFromOwnString wb;
wb << '('; // For single-element tuples we must use the explicit tuple() function,
// or they will be parsed back as plain literals.
if (x.size() > 1)
{
wb << '(';
}
else
{
wb << "tuple(";
}
for (auto it = x.begin(); it != x.end(); ++it) for (auto it = x.begin(); it != x.end(); ++it)
{ {
if (it != x.begin()) if (it != x.begin())

View File

@ -0,0 +1,534 @@
#include <common/DateLUT.h>
#include <common/DateLUTImpl.h>
#include <gtest/gtest.h>
#include <string>
#include <string_view>
#include <cctz/time_zone.h>
/// For the expansion of gtest macros.
#if defined(__clang__)
#pragma clang diagnostic ignored "-Wused-but-marked-unused"
#endif
// All timezones present at build time and embedded into ClickHouse binary.
extern const char * auto_time_zones[];
namespace
{
cctz::civil_day YYYYMMDDToDay(unsigned value)
{
return cctz::civil_day(
value / 10000, // year
(value % 10000) / 100, // month
value % 100); // day
}
std::vector<const char*> allTimezones(bool with_weird_offsets = true)
{
std::vector<const char*> result;
const auto * timezone_name = auto_time_zones;
while (*timezone_name)
{
bool weird_offsets = (std::string_view(*timezone_name) == "Africa/Monrovia");
if (!weird_offsets || with_weird_offsets)
result.push_back(*timezone_name);
++timezone_name;
}
return result;
}
struct FailuresCount
{
size_t non_fatal = 0;
size_t fatal = 0;
size_t total = 0;
};
FailuresCount countFailures(const ::testing::TestResult & test_result)
{
FailuresCount failures{0, 0, 0};
const size_t count = test_result.total_part_count();
for (size_t i = 0; i < count; ++i)
{
const auto & part = test_result.GetTestPartResult(i);
if (part.nonfatally_failed())
{
++failures.non_fatal;
++failures.total;
}
if (part.fatally_failed())
{
++failures.fatal;
++failures.total;
}
}
return failures;
}
}
TEST(DateLUTTest, makeDayNumTest)
{
const DateLUTImpl & lut = DateLUT::instance("UTC");
EXPECT_EQ(0, lut.makeDayNum(2500, 12, 25));
EXPECT_EQ(0, lut.makeDayNum(1924, 12, 31));
}
TEST(DateLUTTest, TimeValuesInMiddleOfRange)
{
const DateLUTImpl & lut = DateLUT::instance("Europe/Minsk");
const time_t time = 1568650811; // 2019-09-16 19:20:11 (Monday)
EXPECT_EQ(lut.getTimeZone(), "Europe/Minsk");
EXPECT_EQ(lut.getOffsetAtStartOfEpoch(), 3600*3); // UTC-3
EXPECT_EQ(lut.toDate(time), 1568581200);
EXPECT_EQ(lut.toMonth(time), 9);
EXPECT_EQ(lut.toQuarter(time), 3);
EXPECT_EQ(lut.toYear(time), 2019);
EXPECT_EQ(lut.toDayOfMonth(time), 16);
EXPECT_EQ(lut.toFirstDayOfWeek(time), 1568581200 /*time_t*/);
EXPECT_EQ(lut.toFirstDayNumOfWeek(time), DayNum(18155) /*DayNum*/);
EXPECT_EQ(lut.toFirstDayOfMonth(time), 1567285200 /*time_t*/);
EXPECT_EQ(lut.toFirstDayNumOfMonth(time), DayNum(18140) /*DayNum*/);
EXPECT_EQ(lut.toFirstDayNumOfQuarter(time), DayNum(18078) /*DayNum*/);
EXPECT_EQ(lut.toFirstDayOfQuarter(time), 1561928400 /*time_t*/);
EXPECT_EQ(lut.toFirstDayOfYear(time), 1546290000 /*time_t*/);
EXPECT_EQ(lut.toFirstDayNumOfYear(time), DayNum(17897) /*DayNum*/);
EXPECT_EQ(lut.toFirstDayOfNextMonth(time), 1569877200 /*time_t*/);
EXPECT_EQ(lut.toFirstDayOfPrevMonth(time), 1564606800 /*time_t*/);
EXPECT_EQ(lut.daysInMonth(time), 30 /*UInt8*/);
EXPECT_EQ(lut.toDateAndShift(time, 10), 1569445200 /*time_t*/);
EXPECT_EQ(lut.toTime(time), 58811 /*time_t*/);
EXPECT_EQ(lut.toHour(time), 19 /*unsigned*/);
EXPECT_EQ(lut.toSecond(time), 11 /*unsigned*/);
EXPECT_EQ(lut.toMinute(time), 20 /*unsigned*/);
EXPECT_EQ(lut.toStartOfMinute(time), 1568650800 /*time_t*/);
EXPECT_EQ(lut.toStartOfFiveMinute(time), 1568650800 /*time_t*/);
EXPECT_EQ(lut.toStartOfFifteenMinutes(time), 1568650500 /*time_t*/);
EXPECT_EQ(lut.toStartOfTenMinutes(time), 1568650800 /*time_t*/);
EXPECT_EQ(lut.toStartOfHour(time), 1568649600 /*time_t*/);
EXPECT_EQ(lut.toDayNum(time), DayNum(18155) /*DayNum*/);
EXPECT_EQ(lut.toDayOfYear(time), 259 /*unsigned*/);
EXPECT_EQ(lut.toRelativeWeekNum(time), 2594 /*unsigned*/);
EXPECT_EQ(lut.toISOYear(time), 2019 /*unsigned*/);
EXPECT_EQ(lut.toFirstDayNumOfISOYear(time), DayNum(17896) /*DayNum*/);
EXPECT_EQ(lut.toFirstDayOfISOYear(time), 1546203600 /*time_t*/);
EXPECT_EQ(lut.toISOWeek(time), 38 /*unsigned*/);
EXPECT_EQ(lut.toRelativeMonthNum(time), 24237 /*unsigned*/);
EXPECT_EQ(lut.toRelativeQuarterNum(time), 8078 /*unsigned*/);
EXPECT_EQ(lut.toRelativeHourNum(time), 435736 /*time_t*/);
EXPECT_EQ(lut.toRelativeMinuteNum(time), 26144180 /*time_t*/);
EXPECT_EQ(lut.toStartOfHourInterval(time, 5), 1568646000 /*time_t*/);
EXPECT_EQ(lut.toStartOfMinuteInterval(time, 6), 1568650680 /*time_t*/);
EXPECT_EQ(lut.toStartOfSecondInterval(time, 7), 1568650811 /*time_t*/);
EXPECT_EQ(lut.toNumYYYYMM(time), 201909 /*UInt32*/);
EXPECT_EQ(lut.toNumYYYYMMDD(time), 20190916 /*UInt32*/);
EXPECT_EQ(lut.toNumYYYYMMDDhhmmss(time), 20190916192011 /*UInt64*/);
EXPECT_EQ(lut.addDays(time, 100), 1577290811 /*time_t*/);
EXPECT_EQ(lut.addWeeks(time, 100), 1629130811 /*time_t*/);
EXPECT_EQ(lut.addMonths(time, 100), 1831652411 /*time_t*/);
EXPECT_EQ(lut.addQuarters(time, 100), 2357655611 /*time_t*/);
EXPECT_EQ(lut.addYears(time, 10), 1884270011 /*time_t*/);
EXPECT_EQ(lut.timeToString(time), "2019-09-16 19:20:11" /*std::string*/);
EXPECT_EQ(lut.dateToString(time), "2019-09-16" /*std::string*/);
}
TEST(DateLUTTest, TimeValuesAtLeftBoderOfRange)
{
const DateLUTImpl & lut = DateLUT::instance("UTC");
const time_t time = 0; // 1970-01-01 00:00:00 (Thursday)
EXPECT_EQ(lut.getTimeZone(), "UTC");
EXPECT_EQ(lut.toDate(time), 0);
EXPECT_EQ(lut.toMonth(time), 1);
EXPECT_EQ(lut.toQuarter(time), 1);
EXPECT_EQ(lut.toYear(time), 1970);
EXPECT_EQ(lut.toDayOfMonth(time), 1);
EXPECT_EQ(lut.toFirstDayOfWeek(time), -259200 /*time_t*/); // 1969-12-29 00:00:00
EXPECT_EQ(lut.toFirstDayNumOfWeek(time), ExtendedDayNum(-3) /*DayNum*/);
EXPECT_EQ(lut.toFirstDayOfMonth(time), 0 /*time_t*/);
EXPECT_EQ(lut.toFirstDayNumOfMonth(time), DayNum(0) /*DayNum*/);
EXPECT_EQ(lut.toFirstDayNumOfQuarter(time), DayNum(0) /*DayNum*/);
EXPECT_EQ(lut.toFirstDayOfQuarter(time), 0 /*time_t*/);
EXPECT_EQ(lut.toFirstDayOfYear(time), 0 /*time_t*/);
EXPECT_EQ(lut.toFirstDayNumOfYear(time), DayNum(0) /*DayNum*/);
EXPECT_EQ(lut.toFirstDayOfNextMonth(time), 2678400 /*time_t*/);
EXPECT_EQ(lut.toFirstDayOfPrevMonth(time), -2678400 /*time_t*/); // 1969-12-01 00:00:00
EXPECT_EQ(lut.daysInMonth(time), 31 /*UInt8*/);
EXPECT_EQ(lut.toDateAndShift(time, 10), 864000 /*time_t*/);
EXPECT_EQ(lut.toTime(time), 0 /*time_t*/);
EXPECT_EQ(lut.toHour(time), 0 /*unsigned*/);
EXPECT_EQ(lut.toSecond(time), 0 /*unsigned*/);
EXPECT_EQ(lut.toMinute(time), 0 /*unsigned*/);
EXPECT_EQ(lut.toStartOfMinute(time), 0 /*time_t*/);
EXPECT_EQ(lut.toStartOfFiveMinute(time), 0 /*time_t*/);
EXPECT_EQ(lut.toStartOfFifteenMinutes(time), 0 /*time_t*/);
EXPECT_EQ(lut.toStartOfTenMinutes(time), 0 /*time_t*/);
EXPECT_EQ(lut.toStartOfHour(time), 0 /*time_t*/);
EXPECT_EQ(lut.toDayNum(time), DayNum(0) /*DayNum*/);
EXPECT_EQ(lut.toDayOfYear(time), 1 /*unsigned*/);
EXPECT_EQ(lut.toRelativeWeekNum(time), 0 /*unsigned*/);
EXPECT_EQ(lut.toISOYear(time), 1970 /*unsigned*/);
EXPECT_EQ(lut.toFirstDayNumOfISOYear(time), ExtendedDayNum(-3) /*DayNum*/);
EXPECT_EQ(lut.toFirstDayOfISOYear(time), -259200 /*time_t*/); // 1969-12-29 00:00:00
EXPECT_EQ(lut.toISOWeek(time), 1 /*unsigned*/);
EXPECT_EQ(lut.toRelativeMonthNum(time), 23641 /*unsigned*/); // ?
EXPECT_EQ(lut.toRelativeQuarterNum(time), 7880 /*unsigned*/); // ?
EXPECT_EQ(lut.toRelativeHourNum(time), 0 /*time_t*/);
EXPECT_EQ(lut.toRelativeMinuteNum(time), 0 /*time_t*/);
EXPECT_EQ(lut.toStartOfHourInterval(time, 5), 0 /*time_t*/);
EXPECT_EQ(lut.toStartOfMinuteInterval(time, 6), 0 /*time_t*/);
EXPECT_EQ(lut.toStartOfSecondInterval(time, 7), 0 /*time_t*/);
EXPECT_EQ(lut.toNumYYYYMM(time), 197001 /*UInt32*/);
EXPECT_EQ(lut.toNumYYYYMMDD(time), 19700101 /*UInt32*/);
EXPECT_EQ(lut.toNumYYYYMMDDhhmmss(time), 19700101000000 /*UInt64*/);
EXPECT_EQ(lut.addDays(time, 100), 8640000 /*time_t*/);
EXPECT_EQ(lut.addWeeks(time, 100), 60480000 /*time_t*/);
EXPECT_EQ(lut.addMonths(time, 100), 262828800 /*time_t*/);
EXPECT_EQ(lut.addQuarters(time, 100), 788918400 /*time_t*/);
EXPECT_EQ(lut.addYears(time, 10), 315532800 /*time_t*/);
EXPECT_EQ(lut.timeToString(time), "1970-01-01 00:00:00" /*std::string*/);
EXPECT_EQ(lut.dateToString(time), "1970-01-01" /*std::string*/);
}
TEST(DateLUTTest, TimeValuesAtRightBoderOfRangeOfOldLUT)
{
// Value is at the right border of the old (small) LUT, and provides meaningful values where old LUT would provide garbage.
const DateLUTImpl & lut = DateLUT::instance("UTC");
const time_t time = 4294343873; // 2106-01-31T01:17:53 (Sunday)
EXPECT_EQ(lut.getTimeZone(), "UTC");
EXPECT_EQ(lut.toDate(time), 4294339200);
EXPECT_EQ(lut.toMonth(time), 1);
EXPECT_EQ(lut.toQuarter(time), 1);
EXPECT_EQ(lut.toYear(time), 2106);
EXPECT_EQ(lut.toDayOfMonth(time), 31);
EXPECT_EQ(lut.toFirstDayOfWeek(time), 4293820800 /*time_t*/);
EXPECT_EQ(lut.toFirstDayNumOfWeek(time), DayNum(49697));
EXPECT_EQ(lut.toFirstDayOfMonth(time), 4291747200 /*time_t*/); // 2016-01-01
EXPECT_EQ(lut.toFirstDayNumOfMonth(time), DayNum(49673));
EXPECT_EQ(lut.toFirstDayNumOfQuarter(time), DayNum(49673) /*DayNum*/);
EXPECT_EQ(lut.toFirstDayOfQuarter(time), 4291747200 /*time_t*/);
EXPECT_EQ(lut.toFirstDayOfYear(time), 4291747200 /*time_t*/);
EXPECT_EQ(lut.toFirstDayNumOfYear(time), DayNum(49673) /*DayNum*/);
EXPECT_EQ(lut.toFirstDayOfNextMonth(time), 4294425600 /*time_t*/); // 2106-02-01
EXPECT_EQ(lut.toFirstDayOfPrevMonth(time), 4289068800 /*time_t*/); // 2105-12-01
EXPECT_EQ(lut.daysInMonth(time), 31 /*UInt8*/);
EXPECT_EQ(lut.toDateAndShift(time, 10), 4295203200 /*time_t*/); // 2106-02-10
EXPECT_EQ(lut.toTime(time), 4673 /*time_t*/);
EXPECT_EQ(lut.toHour(time), 1 /*unsigned*/);
EXPECT_EQ(lut.toMinute(time), 17 /*unsigned*/);
EXPECT_EQ(lut.toSecond(time), 53 /*unsigned*/);
EXPECT_EQ(lut.toStartOfMinute(time), 4294343820 /*time_t*/);
EXPECT_EQ(lut.toStartOfFiveMinute(time), 4294343700 /*time_t*/);
EXPECT_EQ(lut.toStartOfFifteenMinutes(time), 4294343700 /*time_t*/);
EXPECT_EQ(lut.toStartOfTenMinutes(time), 4294343400 /*time_t*/);
EXPECT_EQ(lut.toStartOfHour(time), 4294342800 /*time_t*/);
EXPECT_EQ(lut.toDayNum(time), DayNum(49703) /*DayNum*/);
EXPECT_EQ(lut.toDayOfYear(time), 31 /*unsigned*/);
EXPECT_EQ(lut.toRelativeWeekNum(time), 7100 /*unsigned*/);
EXPECT_EQ(lut.toISOYear(time), 2106 /*unsigned*/);
EXPECT_EQ(lut.toFirstDayNumOfISOYear(time), DayNum(49676) /*DayNum*/); // 2106-01-04
EXPECT_EQ(lut.toFirstDayOfISOYear(time), 4292006400 /*time_t*/);
EXPECT_EQ(lut.toISOWeek(time), 4 /*unsigned*/);
EXPECT_EQ(lut.toRelativeMonthNum(time), 25273 /*unsigned*/);
EXPECT_EQ(lut.toRelativeQuarterNum(time), 8424 /*unsigned*/);
EXPECT_EQ(lut.toRelativeHourNum(time), 1192873 /*time_t*/);
EXPECT_EQ(lut.toRelativeMinuteNum(time), 71572397 /*time_t*/);
EXPECT_EQ(lut.toStartOfHourInterval(time, 5), 4294332000 /*time_t*/);
EXPECT_EQ(lut.toStartOfMinuteInterval(time, 6), 4294343520 /*time_t*/);
EXPECT_EQ(lut.toStartOfSecondInterval(time, 7), 4294343872 /*time_t*/);
EXPECT_EQ(lut.toNumYYYYMM(time), 210601 /*UInt32*/);
EXPECT_EQ(lut.toNumYYYYMMDD(time), 21060131 /*UInt32*/);
EXPECT_EQ(lut.toNumYYYYMMDDhhmmss(time), 21060131011753 /*UInt64*/);
EXPECT_EQ(lut.addDays(time, 100), 4302983873 /*time_t*/);
EXPECT_EQ(lut.addWeeks(time, 10), 4300391873 /*time_t*/);
EXPECT_EQ(lut.addMonths(time, 10), 4320523073 /*time_t*/); // 2106-11-30 01:17:53
EXPECT_EQ(lut.addQuarters(time, 10), 4373140673 /*time_t*/); // 2108-07-31 01:17:53
EXPECT_EQ(lut.addYears(time, 10), 4609876673 /*time_t*/); // 2116-01-31 01:17:53
EXPECT_EQ(lut.timeToString(time), "2106-01-31 01:17:53" /*std::string*/);
EXPECT_EQ(lut.dateToString(time), "2106-01-31" /*std::string*/);
}
class DateLUTWithTimeZone : public ::testing::TestWithParam<const char * /* timezone name */>
{};
TEST_P(DateLUTWithTimeZone, LoadLUT)
{
// There are some assumptions and assertions about TZ data made in DateLUTImpl which are verified upon loading,
// to make sure that those assertions are true for all timezones we are going to load all of them one by one.
DateLUT::instance(GetParam());
}
// Another long running test, shouldn't be run to often
TEST_P(DateLUTWithTimeZone, VaidateTimeComponentsAroundEpoch)
{
// Converting time around 1970-01-01 to hour-minute-seconds time components
// could be problematic.
const size_t max_failures_per_tz = 3;
const auto * timezone_name = GetParam();
const auto * test_info = ::testing::UnitTest::GetInstance()->current_test_info();
const DateLUTImpl & lut = DateLUT::instance(timezone_name);
for (time_t i = -856147870; i < 86400 * 10000; i += 11 * 13 * 17 * 19)
{
SCOPED_TRACE(::testing::Message()
<< "\n\tTimezone: " << timezone_name
<< "\n\ttimestamp: " << i
<< "\n\t offset at start of epoch : " << lut.getOffsetAtStartOfEpoch()
<< "\n\t offset_at_start_of_lut : " << lut.getTimeOffsetAtStartOfLUT());
EXPECT_GE(24, lut.toHour(i));
EXPECT_GT(60, lut.toMinute(i));
EXPECT_GT(60, lut.toSecond(i));
const auto current_failures = countFailures(*test_info->result());
if (current_failures.total > 0)
{
if (i < 0)
i = -1;
}
if (current_failures.total >= max_failures_per_tz)
break;
}
}
TEST_P(DateLUTWithTimeZone, getTimeZone)
{
const auto & lut = DateLUT::instance(GetParam());
EXPECT_EQ(GetParam(), lut.getTimeZone());
}
// Group of tests for timezones that have or had some time ago an offset which is not multiple of 15 minutes.
INSTANTIATE_TEST_SUITE_P(ExoticTimezones,
DateLUTWithTimeZone,
::testing::ValuesIn(std::initializer_list<const char*>{
"Africa/El_Aaiun",
"Pacific/Apia",
"Pacific/Enderbury",
"Pacific/Fakaofo",
"Pacific/Kiritimati",
})
);
INSTANTIATE_TEST_SUITE_P(AllTimeZones,
DateLUTWithTimeZone,
::testing::ValuesIn(allTimezones())
);
std::ostream & operator<<(std::ostream & ostr, const DateLUTImpl::Values & v)
{
return ostr << "DateLUTImpl::Values{"
<< "\n\t date : " << v.date
<< "\n\t year : " << static_cast<unsigned int>(v.year)
<< "\n\t month : " << static_cast<unsigned int>(v.month)
<< "\n\t day : " << static_cast<unsigned int>(v.day_of_month)
<< "\n\t weekday : " << static_cast<unsigned int>(v.day_of_week)
<< "\n\t days in month : " << static_cast<unsigned int>(v.days_in_month)
<< "\n\t offset change : " << v.amount_of_offset_change()
<< "\n\t offset change at : " << v.time_at_offset_change()
<< "\n}";
}
struct TimeRangeParam
{
const cctz::civil_second begin;
const cctz::civil_second end;
const int step_in_seconds;
};
std::ostream & operator<<(std::ostream & ostr, const TimeRangeParam & param)
{
return ostr << param.begin << " : " << param.end << " step: " << param.step_in_seconds << "s";
}
class DateLUTWithTimeZoneAndTimeRange : public ::testing::TestWithParam<std::tuple<const char* /*timezone_name*/, TimeRangeParam>>
{};
// refactored test from tests/date_lut3.cpp
TEST_P(DateLUTWithTimeZoneAndTimeRange, InRange)
{
// for a time_t values in range [begin, end) to match with reference obtained from cctz:
// compare date and time components: year, month, day, hours, minutes, seconds, formatted time string.
const auto & [timezone_name, range_data] = GetParam();
const auto & [begin, end, step] = range_data;
const auto * test_info = ::testing::UnitTest::GetInstance()->current_test_info();
static const size_t max_failures_per_case = 3;
cctz::time_zone tz;
ASSERT_TRUE(cctz::load_time_zone(timezone_name, &tz));
const auto & lut = DateLUT::instance(timezone_name);
const auto start = cctz::convert(begin, tz).time_since_epoch().count();
const auto stop = cctz::convert(end, tz).time_since_epoch().count();
for (time_t expected_time_t = start; expected_time_t < stop; expected_time_t += step)
{
SCOPED_TRACE(expected_time_t);
const cctz::civil_second tz_time = cctz::convert(std::chrono::system_clock::from_time_t(expected_time_t), tz);
/// Weird offset, not supported.
/// Example: Africa/Monrovia has offset UTC-0:44:30 in year 1970.
auto timestamp_current_day_pre = std::chrono::system_clock::to_time_t(tz.lookup(cctz::civil_day(tz_time)).pre);
auto timestamp_current_day_post = std::chrono::system_clock::to_time_t(tz.lookup(cctz::civil_day(tz_time) + 1).post);
if (timestamp_current_day_pre % 900 || timestamp_current_day_post % 900)
continue;
/// Unsupported timezone transitions - not in 15-minute time point or to different day.
/// Example: America/Goose_Bay decided to go back one hour at 00:01:
/// $ seq 1289097900 30 1289103600 | TZ=America/Goose_Bay LC_ALL=C xargs -I{} date -d @{}
/// Sat Nov 6 23:59:00 ADT 2010
/// Sat Nov 6 23:59:30 ADT 2010
/// Sun Nov 7 00:00:00 ADT 2010
/// Sun Nov 7 00:00:30 ADT 2010
/// Sat Nov 6 23:01:00 AST 2010
/// Sat Nov 6 23:01:30 AST 2010
bool has_transition = false;
cctz::time_zone::civil_transition transition{};
if (tz.next_transition(std::chrono::system_clock::from_time_t(expected_time_t - 1), &transition)
&& (transition.from.day() == tz_time.day() || transition.to.day() == tz_time.day()))
{
has_transition = true;
}
if (has_transition && (transition.from.second() != 0 || transition.from.minute() % 15 != 0))
{
/*std::cerr << "Skipping " << timezone_name << " " << tz_time
<< " because of unsupported timezone transition from " << transition.from << " to " << transition.to
<< " (not divisible by 15 minutes)\n";*/
continue;
}
/// Transition to previous day, but not from midnight.
if (has_transition && cctz::civil_day(transition.from) == cctz::civil_day(transition.to) + 1
&& transition.from != cctz::civil_day(transition.from))
{
/*std::cerr << "Skipping " << timezone_name << " " << tz_time
<< " because of unsupported timezone transition from " << transition.from << " to " << transition.to
<< " (to previous day but not at midnight)\n";*/
continue;
}
/// To large transition.
if (has_transition
&& std::abs(transition.from - transition.to) > 3600 * 3)
{
/*std::cerr << "Skipping " << timezone_name << " " << tz_time
<< " because of unsupported timezone transition from " << transition.from << " to " << transition.to
<< " (it is too large)\n";*/
continue;
}
EXPECT_EQ(tz_time.year(), lut.toYear(expected_time_t));
EXPECT_EQ(tz_time.month(), lut.toMonth(expected_time_t));
EXPECT_EQ(tz_time.day(), lut.toDayOfMonth(expected_time_t));
/// tm.tm_wday Sunday is 0, while for DateLUTImpl it is 7
EXPECT_EQ(static_cast<int>(cctz::get_weekday(tz_time)) + 1, lut.toDayOfWeek(expected_time_t));
EXPECT_EQ(cctz::get_yearday(tz_time), lut.toDayOfYear(expected_time_t));
EXPECT_EQ(tz_time.hour(), lut.toHour(expected_time_t));
EXPECT_EQ(tz_time.minute(), lut.toMinute(expected_time_t));
EXPECT_EQ(tz_time.second(), lut.toSecond(expected_time_t));
const auto time_string = cctz::format("%E4Y-%m-%d %H:%M:%S", std::chrono::system_clock::from_time_t(expected_time_t), tz);
EXPECT_EQ(time_string, lut.timeToString(expected_time_t));
/// It makes sense to let test execute all checks above to simplify debugging,
/// but once we've found a bad apple, no need to dig deeper.
if (countFailures(*test_info->result()).total >= max_failures_per_case)
break;
}
}
INSTANTIATE_TEST_SUITE_P(AllTimezones_Year2010,
DateLUTWithTimeZoneAndTimeRange,
::testing::Combine(
::testing::ValuesIn(allTimezones()),
::testing::ValuesIn(std::initializer_list<TimeRangeParam>{
// Values from tests/date_lut3.cpp
{YYYYMMDDToDay(20101031), YYYYMMDDToDay(20101101), 15 * 60},
{YYYYMMDDToDay(20100328), YYYYMMDDToDay(20100330), 15 * 60}
}))
);
INSTANTIATE_TEST_SUITE_P(AllTimezones_Year1970_WHOLE,
DateLUTWithTimeZoneAndTimeRange,
::testing::Combine(
::testing::ValuesIn(allTimezones(false)),
::testing::ValuesIn(std::initializer_list<TimeRangeParam>{
// Values from tests/date_lut3.cpp
{YYYYMMDDToDay(19700101), YYYYMMDDToDay(19701231), 3191 /*53m 11s*/},
}))
);
INSTANTIATE_TEST_SUITE_P(AllTimezones_Year2010_WHOLE,
DateLUTWithTimeZoneAndTimeRange,
::testing::Combine(
::testing::ValuesIn(allTimezones(false)),
::testing::ValuesIn(std::initializer_list<TimeRangeParam>{
// Values from tests/date_lut3.cpp
{YYYYMMDDToDay(20100101), YYYYMMDDToDay(20101231), 3191 /*53m 11s*/},
}))
);
INSTANTIATE_TEST_SUITE_P(AllTimezones_Year2020_WHOLE,
DateLUTWithTimeZoneAndTimeRange,
::testing::Combine(
::testing::ValuesIn(allTimezones()),
::testing::ValuesIn(std::initializer_list<TimeRangeParam>{
// Values from tests/date_lut3.cpp
{YYYYMMDDToDay(20200101), YYYYMMDDToDay(20201231), 3191 /*53m 11s*/},
}))
);
INSTANTIATE_TEST_SUITE_P(AllTimezones_PreEpoch,
DateLUTWithTimeZoneAndTimeRange,
::testing::Combine(
::testing::ValuesIn(allTimezones(false)),
::testing::ValuesIn(std::initializer_list<TimeRangeParam>{
{YYYYMMDDToDay(19500101), YYYYMMDDToDay(19600101), 15 * 60},
{YYYYMMDDToDay(19300101), YYYYMMDDToDay(19350101), 11 * 15 * 60}
}))
);
INSTANTIATE_TEST_SUITE_P(AllTimezones_Year1970,
DateLUTWithTimeZoneAndTimeRange,
::testing::Combine(
::testing::ValuesIn(allTimezones(false)),
::testing::ValuesIn(std::initializer_list<TimeRangeParam>{
{YYYYMMDDToDay(19700101), YYYYMMDDToDay(19700201), 15 * 60},
{YYYYMMDDToDay(19700101), YYYYMMDDToDay(19701231), 11 * 13 * 17}
// // 11 was chosen as a number which can't divide product of 2-combinarions of (7, 24, 60),
// // to reduce likelehood of hitting same hour/minute/second values for different days.
// // + 12 is just to make sure that last day is covered fully.
// {0, 0 + 11 * 3600 * 24 + 12, 11},
}))
);

View File

@ -0,0 +1,656 @@
#include <vector>
#include <string>
#include <exception>
#include <string_view>
#include <common/JSON.h>
#include <boost/range/irange.hpp>
#include <gtest/gtest.h>
enum class ResultType
{
Return,
Throw
};
struct GetStringTestRecord
{
std::string_view input;
ResultType result_type;
std::string_view result;
};
TEST(JSONSuite, SimpleTest)
{
using namespace std::literals;
std::vector<GetStringTestRecord> test_data =
{
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("Вафельница Vitek WX-1102 FL")"sv, ResultType::Return, "Вафельница Vitek WX-1102 FL"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("184509")"sv, ResultType::Return, "184509"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("Все для детей/Детская техника/Vitek")"sv, ResultType::Return, "Все для детей/Детская техника/Vitek"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("В наличии")"sv, ResultType::Return, "В наличии"sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("2390.00")"sv, ResultType::Return, "2390.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("Карточка")"sv, ResultType::Return, "Карточка"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("detail")"sv, ResultType::Return, "detail"sv },
{ R"("actionField")"sv, ResultType::Return, "actionField"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("http://www.techport.ru/q/?t=вафельница&sort=price&sdim=asc")"sv, ResultType::Return, "http://www.techport.ru/q/?t=вафельница&sort=price&sdim=asc"sv },
{ R"("action")"sv, ResultType::Return, "action"sv },
{ R"("detail")"sv, ResultType::Return, "detail"sv },
{ R"("products")"sv, ResultType::Return, "products"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("Вафельница Vitek WX-1102 FL")"sv, ResultType::Return, "Вафельница Vitek WX-1102 FL"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("184509")"sv, ResultType::Return, "184509"sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("2390.00")"sv, ResultType::Return, "2390.00"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("Vitek")"sv, ResultType::Return, "Vitek"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("Все для детей/Детская техника/Vitek")"sv, ResultType::Return, "Все для детей/Детская техника/Vitek"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("В наличии")"sv, ResultType::Return, "В наличии"sv },
{ R"("ru")"sv, ResultType::Return, "ru"sv },
{ R"("experiments")"sv, ResultType::Return, "experiments"sv },
{ R"("lang")"sv, ResultType::Return, "lang"sv },
{ R"("ru")"sv, ResultType::Return, "ru"sv },
{ R"("los_portal")"sv, ResultType::Return, "los_portal"sv },
{ R"("los_level")"sv, ResultType::Return, "los_level"sv },
{ R"("none")"sv, ResultType::Return, "none"sv },
{ R"("isAuthorized")"sv, ResultType::Return, "isAuthorized"sv },
{ R"("isSubscriber")"sv, ResultType::Return, "isSubscriber"sv },
{ R"("postType")"sv, ResultType::Return, "postType"sv },
{ R"("Новости")"sv, ResultType::Return, "Новости"sv },
{ R"("experiments")"sv, ResultType::Return, "experiments"sv },
{ R"("lang")"sv, ResultType::Return, "lang"sv },
{ R"("ru")"sv, ResultType::Return, "ru"sv },
{ R"("los_portal")"sv, ResultType::Return, "los_portal"sv },
{ R"("los_level")"sv, ResultType::Return, "los_level"sv },
{ R"("none")"sv, ResultType::Return, "none"sv },
{ R"("lang")"sv, ResultType::Return, "lang"sv },
{ R"("ru")"sv, ResultType::Return, "ru"sv },
{ R"("Электроплита GEFEST Брест ЭПНД 5140-01 0001")"sv, ResultType::Return, "Электроплита GEFEST Брест ЭПНД 5140-01 0001"sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("currencyCode")"sv, ResultType::Return, "currencyCode"sv },
{ R"("RUB")"sv, ResultType::Return, "RUB"sv },
{ R"("lang")"sv, ResultType::Return, "lang"sv },
{ R"("ru")"sv, ResultType::Return, "ru"sv },
{ R"("experiments")"sv, ResultType::Return, "experiments"sv },
{ R"("lang")"sv, ResultType::Return, "lang"sv },
{ R"("ru")"sv, ResultType::Return, "ru"sv },
{ R"("los_portal")"sv, ResultType::Return, "los_portal"sv },
{ R"("los_level")"sv, ResultType::Return, "los_level"sv },
{ R"("none")"sv, ResultType::Return, "none"sv },
{ R"("trash_login")"sv, ResultType::Return, "trash_login"sv },
{ R"("novikoff")"sv, ResultType::Return, "novikoff"sv },
{ R"("trash_cat_link")"sv, ResultType::Return, "trash_cat_link"sv },
{ R"("progs")"sv, ResultType::Return, "progs"sv },
{ R"("trash_parent_link")"sv, ResultType::Return, "trash_parent_link"sv },
{ R"("content")"sv, ResultType::Return, "content"sv },
{ R"("trash_posted_parent")"sv, ResultType::Return, "trash_posted_parent"sv },
{ R"("content.01.2016")"sv, ResultType::Return, "content.01.2016"sv },
{ R"("trash_posted_cat")"sv, ResultType::Return, "trash_posted_cat"sv },
{ R"("progs.01.2016")"sv, ResultType::Return, "progs.01.2016"sv },
{ R"("trash_virus_count")"sv, ResultType::Return, "trash_virus_count"sv },
{ R"("trash_is_android")"sv, ResultType::Return, "trash_is_android"sv },
{ R"("trash_is_wp8")"sv, ResultType::Return, "trash_is_wp8"sv },
{ R"("trash_is_ios")"sv, ResultType::Return, "trash_is_ios"sv },
{ R"("trash_posted")"sv, ResultType::Return, "trash_posted"sv },
{ R"("01.2016")"sv, ResultType::Return, "01.2016"sv },
{ R"("experiments")"sv, ResultType::Return, "experiments"sv },
{ R"("lang")"sv, ResultType::Return, "lang"sv },
{ R"("ru")"sv, ResultType::Return, "ru"sv },
{ R"("los_portal")"sv, ResultType::Return, "los_portal"sv },
{ R"("los_level")"sv, ResultType::Return, "los_level"sv },
{ R"("none")"sv, ResultType::Return, "none"sv },
{ R"("merchantId")"sv, ResultType::Return, "merchantId"sv },
{ R"("13694_49246")"sv, ResultType::Return, "13694_49246"sv },
{ R"("cps-source")"sv, ResultType::Return, "cps-source"sv },
{ R"("wargaming")"sv, ResultType::Return, "wargaming"sv },
{ R"("cps_provider")"sv, ResultType::Return, "cps_provider"sv },
{ R"("default")"sv, ResultType::Return, "default"sv },
{ R"("errorReason")"sv, ResultType::Return, "errorReason"sv },
{ R"("no errors")"sv, ResultType::Return, "no errors"sv },
{ R"("scid")"sv, ResultType::Return, "scid"sv },
{ R"("isAuthPayment")"sv, ResultType::Return, "isAuthPayment"sv },
{ R"("lang")"sv, ResultType::Return, "lang"sv },
{ R"("ru")"sv, ResultType::Return, "ru"sv },
{ R"("rubric")"sv, ResultType::Return, "rubric"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("rubric")"sv, ResultType::Return, "rubric"sv },
{ R"("Мир")"sv, ResultType::Return, "Мир"sv },
{ R"("lang")"sv, ResultType::Return, "lang"sv },
{ R"("ru")"sv, ResultType::Return, "ru"sv },
{ R"("experiments")"sv, ResultType::Return, "experiments"sv },
{ R"("lang")"sv, ResultType::Return, "lang"sv },
{ R"("ru")"sv, ResultType::Return, "ru"sv },
{ R"("los_portal")"sv, ResultType::Return, "los_portal"sv },
{ R"("los_level")"sv, ResultType::Return, "los_level"sv },
{ R"("none")"sv, ResultType::Return, "none"sv },
{ R"("lang")"sv, ResultType::Return, "lang"sv },
{ R"("ru")"sv, ResultType::Return, "ru"sv },
{ R"("__ym")"sv, ResultType::Return, "__ym"sv },
{ R"("ecommerce")"sv, ResultType::Return, "ecommerce"sv },
{ R"("impressions")"sv, ResultType::Return, "impressions"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("863813")"sv, ResultType::Return, "863813"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("Футболка детская 3D Happy, возраст 1-2 года, трикотаж")"sv, ResultType::Return, "Футболка детская 3D Happy, возраст 1-2 года, трикотаж"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("/Летние товары/Летний текстиль/")"sv, ResultType::Return, "/Летние товары/Летний текстиль/"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("390.00")"sv, ResultType::Return, "390.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("/retailrocket/")"sv, ResultType::Return, "/retailrocket/"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")"sv, ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("863839")"sv, ResultType::Return, "863839"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("Футболка детская 3D Pretty kitten, возраст 1-2 года, трикотаж")"sv, ResultType::Return, "Футболка детская 3D Pretty kitten, возраст 1-2 года, трикотаж"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("/Летние товары/Летний текстиль/")"sv, ResultType::Return, "/Летние товары/Летний текстиль/"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("390.00")"sv, ResultType::Return, "390.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("/retailrocket/")"sv, ResultType::Return, "/retailrocket/"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")"sv, ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("863847")"sv, ResultType::Return, "863847"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("Футболка детская 3D Little tiger, возраст 1-2 года, трикотаж")"sv, ResultType::Return, "Футболка детская 3D Little tiger, возраст 1-2 года, трикотаж"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("/Летние товары/Летний текстиль/")"sv, ResultType::Return, "/Летние товары/Летний текстиль/"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("390.00")"sv, ResultType::Return, "390.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("/retailrocket/")"sv, ResultType::Return, "/retailrocket/"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")"sv, ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("911480")"sv, ResultType::Return, "911480"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("Футболка детская 3D Puppy, возраст 1-2 года, трикотаж")"sv, ResultType::Return, "Футболка детская 3D Puppy, возраст 1-2 года, трикотаж"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("/Летние товары/Летний текстиль/")"sv, ResultType::Return, "/Летние товары/Летний текстиль/"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("390.00")"sv, ResultType::Return, "390.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("/retailrocket/")"sv, ResultType::Return, "/retailrocket/"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")"sv, ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("911484")"sv, ResultType::Return, "911484"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("Футболка детская 3D Little bears, возраст 1-2 года, трикотаж")"sv, ResultType::Return, "Футболка детская 3D Little bears, возраст 1-2 года, трикотаж"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("/Летние товары/Летний текстиль/")"sv, ResultType::Return, "/Летние товары/Летний текстиль/"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("390.00")"sv, ResultType::Return, "390.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("/retailrocket/")"sv, ResultType::Return, "/retailrocket/"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")"sv, ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("911489")"sv, ResultType::Return, "911489"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("Футболка детская 3D Dolphin, возраст 2-4 года, трикотаж")"sv, ResultType::Return, "Футболка детская 3D Dolphin, возраст 2-4 года, трикотаж"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("/Летние товары/Летний текстиль/")"sv, ResultType::Return, "/Летние товары/Летний текстиль/"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("390.00")"sv, ResultType::Return, "390.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("/retailrocket/")"sv, ResultType::Return, "/retailrocket/"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")"sv, ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("911496")"sv, ResultType::Return, "911496"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("Футболка детская 3D Pretty, возраст 1-2 года, трикотаж")"sv, ResultType::Return, "Футболка детская 3D Pretty, возраст 1-2 года, трикотаж"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("/Летние товары/Летний текстиль/")"sv, ResultType::Return, "/Летние товары/Летний текстиль/"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("390.00")"sv, ResultType::Return, "390.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("/retailrocket/")"sv, ResultType::Return, "/retailrocket/"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")"sv, ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("911504")"sv, ResultType::Return, "911504"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("Футболка детская 3D Fairytale, возраст 1-2 года, трикотаж")"sv, ResultType::Return, "Футболка детская 3D Fairytale, возраст 1-2 года, трикотаж"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("/Летние товары/Летний текстиль/")"sv, ResultType::Return, "/Летние товары/Летний текстиль/"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("390.00")"sv, ResultType::Return, "390.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("/retailrocket/")"sv, ResultType::Return, "/retailrocket/"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")"sv, ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("911508")"sv, ResultType::Return, "911508"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("Футболка детская 3D Kittens, возраст 1-2 года, трикотаж")"sv, ResultType::Return, "Футболка детская 3D Kittens, возраст 1-2 года, трикотаж"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("/Летние товары/Летний текстиль/")"sv, ResultType::Return, "/Летние товары/Летний текстиль/"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("390.00")"sv, ResultType::Return, "390.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("/retailrocket/")"sv, ResultType::Return, "/retailrocket/"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")"sv, ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("911512")"sv, ResultType::Return, "911512"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("Футболка детская 3D Sunshine, возраст 1-2 года, трикотаж")"sv, ResultType::Return, "Футболка детская 3D Sunshine, возраст 1-2 года, трикотаж"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("/Летние товары/Летний текстиль/")"sv, ResultType::Return, "/Летние товары/Летний текстиль/"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("390.00")"sv, ResultType::Return, "390.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("/retailrocket/")"sv, ResultType::Return, "/retailrocket/"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")"sv, ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("911516")"sv, ResultType::Return, "911516"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("Футболка детская 3D Dog in bag, возраст 1-2 года, трикотаж")"sv, ResultType::Return, "Футболка детская 3D Dog in bag, возраст 1-2 года, трикотаж"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("/Летние товары/Летний текстиль/")"sv, ResultType::Return, "/Летние товары/Летний текстиль/"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("390.00")"sv, ResultType::Return, "390.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("/retailrocket/")"sv, ResultType::Return, "/retailrocket/"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")"sv, ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("911520")"sv, ResultType::Return, "911520"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("Футболка детская 3D Cute puppy, возраст 1-2 года, трикотаж")"sv, ResultType::Return, "Футболка детская 3D Cute puppy, возраст 1-2 года, трикотаж"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("/Летние товары/Летний текстиль/")"sv, ResultType::Return, "/Летние товары/Летний текстиль/"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("390.00")"sv, ResultType::Return, "390.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("/retailrocket/")"sv, ResultType::Return, "/retailrocket/"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")"sv, ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("911524")"sv, ResultType::Return, "911524"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("Футболка детская 3D Rabbit, возраст 1-2 года, трикотаж")"sv, ResultType::Return, "Футболка детская 3D Rabbit, возраст 1-2 года, трикотаж"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("/Летние товары/Летний текстиль/")"sv, ResultType::Return, "/Летние товары/Летний текстиль/"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("390.00")"sv, ResultType::Return, "390.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("/retailrocket/")"sv, ResultType::Return, "/retailrocket/"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")"sv, ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("911528")"sv, ResultType::Return, "911528"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("Футболка детская 3D Turtle, возраст 1-2 года, трикотаж")"sv, ResultType::Return, "Футболка детская 3D Turtle, возраст 1-2 года, трикотаж"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("/Летние товары/Летний текстиль/")"sv, ResultType::Return, "/Летние товары/Летний текстиль/"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("390.00")"sv, ResultType::Return, "390.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("/retailrocket/")"sv, ResultType::Return, "/retailrocket/"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")"sv, ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("888616")"sv, ResultType::Return, "888616"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ "\"3Д Футболка мужская \\\"Collorista\\\" Светлое завтра р-р XL(52-54), 100% хлопок, трикотаж\""sv, ResultType::Return, "3Д Футболка мужская \"Collorista\" Светлое завтра р-р XL(52-54), 100% хлопок, трикотаж"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("/Одежда и обувь/Мужская одежда/Футболки/")"sv, ResultType::Return, "/Одежда и обувь/Мужская одежда/Футболки/"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("406.60")"sv, ResultType::Return, "406.60"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("/retailrocket/")"sv, ResultType::Return, "/retailrocket/"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")"sv, ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("913361")"sv, ResultType::Return, "913361"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("3Д Футболка детская World р-р 8-10, 100% хлопок, трикотаж")"sv, ResultType::Return, "3Д Футболка детская World р-р 8-10, 100% хлопок, трикотаж"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("/Летние товары/Летний текстиль/")"sv, ResultType::Return, "/Летние товары/Летний текстиль/"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("470.00")"sv, ResultType::Return, "470.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("/retailrocket/")"sv, ResultType::Return, "/retailrocket/"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")"sv, ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("913364")"sv, ResultType::Return, "913364"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("3Д Футболка детская Force р-р 8-10, 100% хлопок, трикотаж")"sv, ResultType::Return, "3Д Футболка детская Force р-р 8-10, 100% хлопок, трикотаж"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("/Летние товары/Летний текстиль/")"sv, ResultType::Return, "/Летние товары/Летний текстиль/"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("470.00")"sv, ResultType::Return, "470.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("/retailrocket/")"sv, ResultType::Return, "/retailrocket/"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")"sv, ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("913367")"sv, ResultType::Return, "913367"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("3Д Футболка детская Winter tale р-р 8-10, 100% хлопок, трикотаж")"sv, ResultType::Return, "3Д Футболка детская Winter tale р-р 8-10, 100% хлопок, трикотаж"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("/Летние товары/Летний текстиль/")"sv, ResultType::Return, "/Летние товары/Летний текстиль/"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("470.00")"sv, ResultType::Return, "470.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("/retailrocket/")"sv, ResultType::Return, "/retailrocket/"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")"sv, ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("913385")"sv, ResultType::Return, "913385"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("3Д Футболка детская Moonshine р-р 8-10, 100% хлопок, трикотаж")"sv, ResultType::Return, "3Д Футболка детская Moonshine р-р 8-10, 100% хлопок, трикотаж"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("/Летние товары/Летний текстиль/")"sv, ResultType::Return, "/Летние товары/Летний текстиль/"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("470.00")"sv, ResultType::Return, "470.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("/retailrocket/")"sv, ResultType::Return, "/retailrocket/"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")"sv, ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("913391")"sv, ResultType::Return, "913391"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("3Д Футболка детская Shaman р-р 8-10, 100% хлопок, трикотаж")"sv, ResultType::Return, "3Д Футболка детская Shaman р-р 8-10, 100% хлопок, трикотаж"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("/Летние товары/Летний текстиль/")"sv, ResultType::Return, "/Летние товары/Летний текстиль/"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("")"sv, ResultType::Return, ""sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("470.00")"sv, ResultType::Return, "470.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("/retailrocket/")"sv, ResultType::Return, "/retailrocket/"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")"sv, ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/"sv },
{ R"("usertype")"sv, ResultType::Return, "usertype"sv },
{ R"("visitor")"sv, ResultType::Return, "visitor"sv },
{ R"("lang")"sv, ResultType::Return, "lang"sv },
{ R"("ru")"sv, ResultType::Return, "ru"sv },
{ R"("__ym")"sv, ResultType::Return, "__ym"sv },
{ R"("ecommerce")"sv, ResultType::Return, "ecommerce"sv },
{ R"("impressions")"sv, ResultType::Return, "impressions"sv },
{ R"("experiments")"sv, ResultType::Return, "experiments"sv },
{ R"("lang")"sv, ResultType::Return, "lang"sv },
{ R"("ru")"sv, ResultType::Return, "ru"sv },
{ R"("los_portal")"sv, ResultType::Return, "los_portal"sv },
{ R"("los_level")"sv, ResultType::Return, "los_level"sv },
{ R"("none")"sv, ResultType::Return, "none"sv },
{ R"("experiments")"sv, ResultType::Return, "experiments"sv },
{ R"("lang")"sv, ResultType::Return, "lang"sv },
{ R"("ru")"sv, ResultType::Return, "ru"sv },
{ R"("los_portal")"sv, ResultType::Return, "los_portal"sv },
{ R"("los_level")"sv, ResultType::Return, "los_level"sv },
{ R"("none")"sv, ResultType::Return, "none"sv },
{ R"("experiments")"sv, ResultType::Return, "experiments"sv },
{ R"("lang")"sv, ResultType::Return, "lang"sv },
{ R"("ru")"sv, ResultType::Return, "ru"sv },
{ R"("los_portal")"sv, ResultType::Return, "los_portal"sv },
{ R"("los_level")"sv, ResultType::Return, "los_level"sv },
{ R"("none")"sv, ResultType::Return, "none"sv },
{ R"("experiments")"sv, ResultType::Return, "experiments"sv },
{ R"("lang")"sv, ResultType::Return, "lang"sv },
{ R"("ru")"sv, ResultType::Return, "ru"sv },
{ R"("los_portal")"sv, ResultType::Return, "los_portal"sv },
{ R"("los_level")"sv, ResultType::Return, "los_level"sv },
{ R"("none")"sv, ResultType::Return, "none"sv },
{ R"("experiments")"sv, ResultType::Return, "experiments"sv },
{ R"("lang")"sv, ResultType::Return, "lang"sv },
{ R"("ru")"sv, ResultType::Return, "ru"sv },
{ R"("los_portal")"sv, ResultType::Return, "los_portal"sv },
{ R"("los_level")"sv, ResultType::Return, "los_level"sv },
{ R"("none")"sv, ResultType::Return, "none"sv },
{ R"("__ym")"sv, ResultType::Return, "__ym"sv },
{ R"("ecommerce")"sv, ResultType::Return, "ecommerce"sv },
{ R"("currencyCode")"sv, ResultType::Return, "currencyCode"sv },
{ R"("RUR")"sv, ResultType::Return, "RUR"sv },
{ R"("impressions")"sv, ResultType::Return, "impressions"sv },
{ R"("name")"sv, ResultType::Return, "name"sv },
{ R"("Чайник электрический Mystery MEK-1627, белый")"sv, ResultType::Return, "Чайник электрический Mystery MEK-1627, белый"sv },
{ R"("brand")"sv, ResultType::Return, "brand"sv },
{ R"("Mystery")"sv, ResultType::Return, "Mystery"sv },
{ R"("id")"sv, ResultType::Return, "id"sv },
{ R"("187180")"sv, ResultType::Return, "187180"sv },
{ R"("category")"sv, ResultType::Return, "category"sv },
{ R"("Мелкая бытовая техника/Мелкие кухонные приборы/Чайники электрические/Mystery")"sv, ResultType::Return, "Мелкая бытовая техника/Мелкие кухонные приборы/Чайники электрические/Mystery"sv },
{ R"("variant")"sv, ResultType::Return, "variant"sv },
{ R"("В наличии")"sv, ResultType::Return, "В наличии"sv },
{ R"("price")"sv, ResultType::Return, "price"sv },
{ R"("1630.00")"sv, ResultType::Return, "1630.00"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ R"("Карточка")"sv, ResultType::Return, "Карточка"sv },
{ R"("position")"sv, ResultType::Return, "position"sv },
{ R"("detail")"sv, ResultType::Return, "detail"sv },
{ R"("actionField")"sv, ResultType::Return, "actionField"sv },
{ R"("list")"sv, ResultType::Return, "list"sv },
{ "\0\""sv, ResultType::Throw, "JSON: expected \", got \0"sv },
{ "\"/igrushki/konstruktory\0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/1290414/komplekt-zhenskiy-dzhemper-plusbryuki-m-254-09-malina-plustemno-siniy-\0a"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/Творчество/Рисование/Инструменты и кра\0a"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Строительство и ремонт/Силовая техника/Зарядные устройства для автомобильных аккумуляторов/Пуско-зарядные устр\xD0\0a"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Строительство и ремонт/Силовая техника/Зарядные устройств\xD0\0t"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Строительство и ремонт/Силовая техника/Зарядные устройства для автомобиль\0k"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\0t"sv, ResultType::Throw, "JSON: expected \", got \0"sv },
{ "\"/Хозтовары/Хранение вещей и организа\xD1\0t"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/Хозтовары/Товары для стир\0a"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"li\0a"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/734859/samolet-radioupravlyaemyy-istrebitel-rabotaet-o\0k"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/kosmetika-i-parfyum/parfyumeriya/mu\0t"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/ko\0\x04"sv, ResultType::Throw, "JSON: begin >= end."sv },
{ ""sv, ResultType::Throw, "JSON: expected \", got \0"sv },
{ "\"/stroitelstvo-i-remont/stroit\0t"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/stroitelstvo-i-remont/stroitelnyy-instrument/av\0k"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/s\0a"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/Строительство и ремонт/Строительный инструмент/Изм\0e"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/avto/soputstvuy\0l"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/str\0\xD0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Отвертка 2 в 1 \\\"TUNDRA basic\\\" 5х75 мм (+,-) \0\xFF"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/stroitelstvo-i-remont/stroitelnyy-instrument/avtoinstrumen\0\0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Мелкая бытовая техника/Мелки\xD0\0\0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Пряжа \\\"Бамбук стрейч\\0\0\0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Карандаш чёрнографитны\xD0\0\xD0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/Творчество/Рукоделие, аппликации/Пряжа и шерсть для \xD0\0l"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/1071547/karandash-chernografitnyy-volshebstvo-nv-kruglyy-d-7-2mm-dl-176mm-plast-tuba/\0e"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"ca\0e"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"ca\0e"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/1165424/chipbord-vyrubnoy-dlya-skrapbukinga-malyshi-mikki-maus-disney-bebi\0t"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/posuda/kuhonnye-prinadlezhnosti-i-i\0d"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/Канцтовары/Ежедневники и блокн\xD0\0\0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/kanctovary/ezhednevniki-i-blok\0a"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Стакан \xD0\0a"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Набор бумаги для скрапбукинга \\\"Мои первый годик\\\": Микки Маус, Дисней бэби, 12 листов 29.5 х 29.5 см, 160\0\x80"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"c\0\0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Органайзер для хранения аксессуаров, \0\0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"quantity\00"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Сменный блок для тетрадей на кольцах А5, 160 листов клетка, офсет \xE2\x84\0="sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/Сувениры/Ф\xD0\0 "sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"\0\""sv, ResultType::Return, "\0"sv },
{ "\"\0\x04"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"va\0\0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"ca\0\0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"В \0\x04"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/letnie-tovary/z\0\x04"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Посудомоечная машина Ha\0="sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Крупная бытов\0\0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Полочная акустическая система Magnat Needl\0\0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"brand\00"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"\0d"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"pos\0 "sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"c\0o"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"var\0\0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Телевизоры и видеотехника/Всё для домашних кинотеатр\0="sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Флеш-диск Transcend JetFlash 620 8GB (TS8GJF62\0\0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Табурет Мег\0\xD0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"variant\0\x04"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Катал\xD0\0\""sv, ResultType::Return, "Катал\xD0\0"sv },
{ "\"К\0\0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Полочная акустическая система Magnat Needl\0\0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"brand\00"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"\0d"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"pos\0 "sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"c\0o"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"17\0o"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/igrushki/razvivayusc\0 "sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Ключница \\\"\0 "sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/Игр\xD1\0 "sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/Игрушки/Игрушки для девочек/Игровые модули дл\xD1\0o"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Крупная бытовая техника/Стиральные машины/С фронт\xD0\0o"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\0 "sv, ResultType::Throw, "JSON: expected \", got \0"sv },
{ "\"Светодиодная лента SMD3528, 5 м. IP33, 60LED, зеленый, 4,8W/мет\xD1\0 "sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Сантехника/Мебель для ванных комнат/Стол\0o"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\0o"sv, ResultType::Throw, "JSON: expected \", got \0"sv },
{ "\"/igrushki/konstruktory\0 "sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/posuda/kuhonnye-prinadlezhnosti-i-instrumenty/kuhonnye-pr\0 "sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/1290414/komplekt-zhenskiy-dzhemper-plusbryuki-m-254-09-malina-plustemno-siniy-\0o"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/Творчество/Рисование/Инструменты и кра\0o"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Строительство и ремонт/Силовая техника/Зарядные устройства для автомобильных аккумуляторов/Пуско-зарядные устр\xD0\0o"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Строительство и ремонт/Силовая техника/Зарядные устройств\xD0\0 "sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Строительство и ремонт/Силовая техника/Зарядные устройства для автомобиль\0d"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\0 "sv, ResultType::Throw, "JSON: expected \", got \0"sv },
{ "\"/Хозтовары/Хранение вещей и организа\xD1\0 "sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/Хозтовары/Товары для стир\0o"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"li\0o"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/igrushki/igrus\0d"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/734859/samolet-radioupravlyaemyy-istrebitel-rabotaet-o\0 "sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/kosmetika-i-parfyum/parfyumeriya/mu\00"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/ko\0\0"sv, ResultType::Throw, "JSON: begin >= end."sv },
{ "\"/avto/avtomobilnyy\0\0"sv, ResultType::Throw, "JSON: begin >= end."sv },
{ "\"/stroitelstvo-i-remont/stroit\00"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/stroitelstvo-i-remont/stroitelnyy-instrument/av\0 "sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/s\0d"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/Строительство и ремонт/Строительный инструмент/Изм\0o"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/avto/soputstvuy\0\""sv, ResultType::Return, "/avto/soputstvuy\0"sv },
{ "\"/str\0k"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Отвертка 2 в 1 \\\"TUNDRA basic\\\" 5х75 мм (+,-) \0\xD0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/stroitelstvo-i-remont/stroitelnyy-instrument/avtoinstrumen\0="sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Чайник электрический Vitesse\0="sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Мелкая бытовая техника/Мелки\xD0\0\xD0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Пряжа \\\"Бамбук стрейч\\0о"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Карандаш чёрнографитны\xD0\0k"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/Творчество/Рукоделие, аппликации/Пряжа и шерсть для \xD0\0\""sv, ResultType::Return, "/Творчество/Рукоделие, аппликации/Пряжа и шерсть для \xD0\0"sv },
{ "\"/1071547/karandash-chernografitnyy-volshebstvo-nv-kruglyy-d-7-2mm-dl-176mm-plast-tuba/\0o"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"ca\0o"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/Подаро\0o"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Средство для прочис\xD1\0o"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"i\0o"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/p\0\""sv, ResultType::Return, "/p\0"sv },
{ "\"/Сувениры/Магниты, н\xD0\0k"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Дерев\xD0\0="sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/prazdniki/svadba/svadebnaya-c\0\xD0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/Канцт\0d"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/Праздники/То\xD0\0 "sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"v\0 "sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/Косметика \xD0\0d"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/Спорт и отдых/Настольные игры/Покер, руле\xD1\0\xD0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"categ\0="sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/retailr\0k"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/retailrocket\0k"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Ежедневник недат А5 140л кл,ляссе,обл пв\0="sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/432809/ezhednevnik-organayzer-sredniy-s-remeshkom-na-knopke-v-oblozhke-kalkulyator-kalendar-do-\0\xD0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/1165424/chipbord-vyrubnoy-dlya-skrapbukinga-malyshi-mikki-maus-disney-bebi\0d"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/posuda/kuhonnye-prinadlezhnosti-i-i\0 "sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/Канцтовары/Ежедневники и блокн\xD0\0o"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"/kanctovary/ezhednevniki-i-blok\00"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Стакан \xD0\0\0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"Набор бумаги для скрапбукинга \\\"Мои первый годик\\\": Микки Маус, Дисней бэби, 12 листов 29.5 х 29.5 см, 160\0\0"sv, ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)."sv },
{ "\"c\0\""sv, ResultType::Return, "c\0"sv },
};
for (auto i : boost::irange(0, 1/*00000*/))
{
static_cast<void>(i);
for (auto & r : test_data)
{
try
{
JSON j(r.input.data(), r.input.data() + r.input.size());
ASSERT_EQ(j.getString(), r.result);
ASSERT_EQ(r.result_type, ResultType::Return);
}
catch (const JSONException &)
{
ASSERT_EQ(r.result_type, ResultType::Throw);
}
}
}
}

View File

@ -1,5 +1,6 @@
#include <iostream> #include <iostream>
#include <stdexcept> #include <stdexcept>
#include <gtest/gtest.h>
#include <common/LocalDateTime.h> #include <common/LocalDateTime.h>
@ -16,14 +17,13 @@ void checkComparison()
LocalDateTime a("2018-07-18 01:02:03"); LocalDateTime a("2018-07-18 01:02:03");
LocalDateTime b("2018-07-18 01:02:03"); LocalDateTime b("2018-07-18 01:02:03");
if (a != b) EXPECT_EQ(a, b);
throw std::runtime_error("Test failed"); EXPECT_FALSE(a != b);
} }
int main(int, char **) TEST(LocalDateTime, Comparison)
{ {
fillStackWithGarbage(); fillStackWithGarbage();
checkComparison(); checkComparison();
return 0;
} }

View File

@ -95,7 +95,7 @@ struct PerformanceStatistics
/// How to select method to run. /// How to select method to run.
/// -1 - automatically, based on statistics (default); /// -1 - automatically, based on statistics (default);
/// 0..3 - always choose specified method (for performance testing); /// >= 0 - always choose specified method (for performance testing);
/// -2 - choose methods in round robin fashion (for performance testing). /// -2 - choose methods in round robin fashion (for performance testing).
ssize_t choose_method = -1; ssize_t choose_method = -1;

View File

@ -50,9 +50,10 @@ inline auto scaleMultiplier(UInt32 scale)
* whole - represents whole part of decimal, can be negative or positive. * whole - represents whole part of decimal, can be negative or positive.
* fractional - for fractional part of decimal, always positive. * fractional - for fractional part of decimal, always positive.
*/ */
template <typename T> template <typename DecimalType>
struct DecimalComponents struct DecimalComponents
{ {
using T = typename DecimalType::NativeType;
T whole; T whole;
T fractional; T fractional;
}; };
@ -106,6 +107,15 @@ inline DecimalType decimalFromComponentsWithMultiplier(
return DecimalType(value); return DecimalType(value);
} }
template <typename DecimalType>
inline DecimalType decimalFromComponentsWithMultiplier(
const DecimalComponents<DecimalType> & components,
typename DecimalType::NativeType scale_multiplier)
{
return decimalFromComponentsWithMultiplier<DecimalType>(components.whole, components.fractional, scale_multiplier);
}
/** Make a decimal value from whole and fractional components with given scale. /** Make a decimal value from whole and fractional components with given scale.
* *
* @see `decimalFromComponentsWithMultiplier` for details. * @see `decimalFromComponentsWithMultiplier` for details.
@ -126,7 +136,7 @@ inline DecimalType decimalFromComponents(
*/ */
template <typename DecimalType> template <typename DecimalType>
inline DecimalType decimalFromComponents( inline DecimalType decimalFromComponents(
const DecimalComponents<typename DecimalType::NativeType> & components, const DecimalComponents<DecimalType> & components,
UInt32 scale) UInt32 scale)
{ {
return decimalFromComponents<DecimalType>(components.whole, components.fractional, scale); return decimalFromComponents<DecimalType>(components.whole, components.fractional, scale);
@ -136,7 +146,7 @@ inline DecimalType decimalFromComponents(
* This is an optimization to reduce number of calls to scaleMultiplier on known scale. * This is an optimization to reduce number of calls to scaleMultiplier on known scale.
*/ */
template <typename DecimalType> template <typename DecimalType>
inline DecimalComponents<typename DecimalType::NativeType> splitWithScaleMultiplier( inline DecimalComponents<DecimalType> splitWithScaleMultiplier(
const DecimalType & decimal, const DecimalType & decimal,
typename DecimalType::NativeType scale_multiplier) typename DecimalType::NativeType scale_multiplier)
{ {
@ -151,7 +161,7 @@ inline DecimalComponents<typename DecimalType::NativeType> splitWithScaleMultipl
/// Split decimal into components: whole and fractional part, @see `DecimalComponents` for details. /// Split decimal into components: whole and fractional part, @see `DecimalComponents` for details.
template <typename DecimalType> template <typename DecimalType>
inline DecimalComponents<typename DecimalType::NativeType> split(const DecimalType & decimal, UInt32 scale) inline DecimalComponents<DecimalType> split(const DecimalType & decimal, UInt32 scale)
{ {
if (scale == 0) if (scale == 0)
{ {

View File

@ -452,7 +452,7 @@ template <> bool decimalLessOrEqual(DateTime64 x, DateTime64 y, UInt32 x_scale,
inline void writeText(const Null &, WriteBuffer & buf) inline void writeText(const Null &, WriteBuffer & buf)
{ {
writeText(std::string("Null"), buf); writeText(std::string("NULL"), buf);
} }
String toString(const Field & x) String toString(const Field & x)

View File

@ -421,8 +421,8 @@ namespace MySQLReplication
UInt32 i24 = 0; UInt32 i24 = 0;
payload.readStrict(reinterpret_cast<char *>(&i24), 3); payload.readStrict(reinterpret_cast<char *>(&i24), 3);
DayNum date_day_number = DateLUT::instance().makeDayNum( const DayNum date_day_number(DateLUT::instance().makeDayNum(
static_cast<int>((i24 >> 9) & 0x7fff), static_cast<int>((i24 >> 5) & 0xf), static_cast<int>(i24 & 0x1f)); static_cast<int>((i24 >> 9) & 0x7fff), static_cast<int>((i24 >> 5) & 0xf), static_cast<int>(i24 & 0x1f)).toUnderType());
row.push_back(Field(date_day_number.toUnderType())); row.push_back(Field(date_day_number.toUnderType()));
break; break;
@ -444,7 +444,7 @@ namespace MySQLReplication
row.push_back(Field{UInt32(date_time)}); row.push_back(Field{UInt32(date_time)});
else else
{ {
DB::DecimalUtils::DecimalComponents<DateTime64::NativeType> components{ DB::DecimalUtils::DecimalComponents<DateTime64> components{
static_cast<DateTime64::NativeType>(date_time), 0}; static_cast<DateTime64::NativeType>(date_time), 0};
components.fractional = fsp; components.fractional = fsp;
@ -463,7 +463,7 @@ namespace MySQLReplication
row.push_back(Field{sec}); row.push_back(Field{sec});
else else
{ {
DB::DecimalUtils::DecimalComponents<DateTime64::NativeType> components{ DB::DecimalUtils::DecimalComponents<DateTime64> components{
static_cast<DateTime64::NativeType>(sec), 0}; static_cast<DateTime64::NativeType>(sec), 0};
components.fractional = fsp; components.fractional = fsp;

View File

@ -14,7 +14,7 @@ struct DecimalUtilsSplitAndCombineTestParam
Decimal64 decimal_value; Decimal64 decimal_value;
uint8_t scale; uint8_t scale;
DecimalUtils::DecimalComponents<typename Decimal64::NativeType> components; DecimalUtils::DecimalComponents<Decimal64> components;
}; };
std::ostream & operator << (std::ostream & ostr, const DecimalUtilsSplitAndCombineTestParam & param) std::ostream & operator << (std::ostream & ostr, const DecimalUtilsSplitAndCombineTestParam & param)

View File

@ -270,8 +270,8 @@ namespace
throw Exception{"Type mismatch, expected Timestamp, got type id = " + toString(value.type()) + " for column " + name, throw Exception{"Type mismatch, expected Timestamp, got type id = " + toString(value.type()) + " for column " + name,
ErrorCodes::TYPE_MISMATCH}; ErrorCodes::TYPE_MISMATCH};
assert_cast<ColumnUInt16 &>(column).getData().push_back(UInt16{DateLUT::instance().toDayNum( assert_cast<ColumnUInt16 &>(column).getData().push_back(static_cast<UInt16>(DateLUT::instance().toDayNum(
static_cast<const Poco::MongoDB::ConcreteElement<Poco::Timestamp> &>(value).value().epochTime())}); static_cast<const Poco::MongoDB::ConcreteElement<Poco::Timestamp> &>(value).value().epochTime())));
break; break;
} }

View File

@ -160,8 +160,15 @@ void PostgreSQLBlockInputStream::insertValue(IColumn & column, std::string_view
assert_cast<ColumnUInt16 &>(column).insertValue(UInt16{LocalDate{std::string(value)}.getDayNum()}); assert_cast<ColumnUInt16 &>(column).insertValue(UInt16{LocalDate{std::string(value)}.getDayNum()});
break; break;
case ValueType::vtDateTime: case ValueType::vtDateTime:
assert_cast<ColumnUInt32 &>(column).insertValue(time_t{LocalDateTime{std::string(value)}}); {
ReadBufferFromString in(value);
time_t time = 0;
readDateTimeText(time, in);
if (time < 0)
time = 0;
assert_cast<ColumnUInt32 &>(column).insertValue(time);
break; break;
}
case ValueType::vtDateTime64:[[fallthrough]]; case ValueType::vtDateTime64:[[fallthrough]];
case ValueType::vtDecimal32: [[fallthrough]]; case ValueType::vtDecimal32: [[fallthrough]];
case ValueType::vtDecimal64: [[fallthrough]]; case ValueType::vtDecimal64: [[fallthrough]];
@ -257,7 +264,13 @@ void PostgreSQLBlockInputStream::prepareArrayInfo(size_t column_idx, const DataT
else if (which.isDate()) else if (which.isDate())
parser = [](std::string & field) -> Field { return UInt16{LocalDate{field}.getDayNum()}; }; parser = [](std::string & field) -> Field { return UInt16{LocalDate{field}.getDayNum()}; };
else if (which.isDateTime()) else if (which.isDateTime())
parser = [](std::string & field) -> Field { return time_t{LocalDateTime{field}}; }; parser = [](std::string & field) -> Field
{
ReadBufferFromString in(field);
time_t time = 0;
readDateTimeText(time, in);
return time;
};
else if (which.isDecimal32()) else if (which.isDecimal32())
parser = [nested](std::string & field) -> Field parser = [nested](std::string & field) -> Field
{ {

View File

@ -104,11 +104,11 @@ void RemoteQueryExecutorReadContext::setConnectionFD(int fd, const Poco::Timespa
connection_fd_description = fd_description; connection_fd_description = fd_description;
} }
bool RemoteQueryExecutorReadContext::checkTimeout() const bool RemoteQueryExecutorReadContext::checkTimeout(bool blocking) const
{ {
try try
{ {
return checkTimeoutImpl(); return checkTimeoutImpl(blocking);
} }
catch (DB::Exception & e) catch (DB::Exception & e)
{ {
@ -118,13 +118,13 @@ bool RemoteQueryExecutorReadContext::checkTimeout() const
} }
} }
bool RemoteQueryExecutorReadContext::checkTimeoutImpl() const bool RemoteQueryExecutorReadContext::checkTimeoutImpl(bool blocking) const
{ {
/// Wait for epoll will not block if it was polled externally. /// Wait for epoll will not block if it was polled externally.
epoll_event events[3]; epoll_event events[3];
events[0].data.fd = events[1].data.fd = events[2].data.fd = -1; events[0].data.fd = events[1].data.fd = events[2].data.fd = -1;
int num_events = epoll.getManyReady(3, events,/* blocking = */ false); int num_events = epoll.getManyReady(3, events, blocking);
bool is_socket_ready = false; bool is_socket_ready = false;
bool is_pipe_alarmed = false; bool is_pipe_alarmed = false;
@ -184,9 +184,16 @@ bool RemoteQueryExecutorReadContext::resumeRoutine()
void RemoteQueryExecutorReadContext::cancel() void RemoteQueryExecutorReadContext::cancel()
{ {
std::lock_guard guard(fiber_lock); std::lock_guard guard(fiber_lock);
/// It is safe to just destroy fiber - we are not in the process of reading from socket. /// It is safe to just destroy fiber - we are not in the process of reading from socket.
boost::context::fiber to_destroy = std::move(fiber); boost::context::fiber to_destroy = std::move(fiber);
while (is_read_in_progress.load(std::memory_order_relaxed))
{
checkTimeout(/* blocking= */ true);
to_destroy = std::move(to_destroy).resume();
}
/// Send something to pipe to cancel executor waiting. /// Send something to pipe to cancel executor waiting.
uint64_t buf = 0; uint64_t buf = 0;
while (-1 == write(pipe_fd[1], &buf, sizeof(buf))) while (-1 == write(pipe_fd[1], &buf, sizeof(buf)))

View File

@ -54,8 +54,8 @@ public:
explicit RemoteQueryExecutorReadContext(IConnections & connections_); explicit RemoteQueryExecutorReadContext(IConnections & connections_);
~RemoteQueryExecutorReadContext(); ~RemoteQueryExecutorReadContext();
bool checkTimeout() const; bool checkTimeout(bool blocking = false) const;
bool checkTimeoutImpl() const; bool checkTimeoutImpl(bool blocking) const;
void setConnectionFD(int fd, const Poco::Timespan & timeout = 0, const std::string & fd_description = ""); void setConnectionFD(int fd, const Poco::Timespan & timeout = 0, const std::string & fd_description = "");
void setTimer() const; void setTimer() const;

View File

@ -12,10 +12,14 @@
#include <IO/parseDateTimeBestEffort.h> #include <IO/parseDateTimeBestEffort.h>
#include <Parsers/ASTLiteral.h> #include <Parsers/ASTLiteral.h>
namespace DB
{
namespace namespace
{ {
using namespace DB;
inline void readText(time_t & x, ReadBuffer & istr, const FormatSettings & settings, const DateLUTImpl & time_zone, const DateLUTImpl & utc_time_zone) inline void readTextHelper(
time_t & x, ReadBuffer & istr, const FormatSettings & settings, const DateLUTImpl & time_zone, const DateLUTImpl & utc_time_zone)
{ {
switch (settings.date_time_input_format) switch (settings.date_time_input_format)
{ {
@ -27,16 +31,16 @@ inline void readText(time_t & x, ReadBuffer & istr, const FormatSettings & setti
return; return;
} }
} }
} }
namespace DB
{
TimezoneMixin::TimezoneMixin(const String & time_zone_name) TimezoneMixin::TimezoneMixin(const String & time_zone_name)
: has_explicit_time_zone(!time_zone_name.empty()), : has_explicit_time_zone(!time_zone_name.empty()),
time_zone(DateLUT::instance(time_zone_name)), time_zone(DateLUT::instance(time_zone_name)),
utc_time_zone(DateLUT::instance("UTC")) utc_time_zone(DateLUT::instance("UTC"))
{} {
}
DataTypeDateTime::DataTypeDateTime(const String & time_zone_name) DataTypeDateTime::DataTypeDateTime(const String & time_zone_name)
: TimezoneMixin(time_zone_name) : TimezoneMixin(time_zone_name)
@ -45,7 +49,8 @@ DataTypeDateTime::DataTypeDateTime(const String & time_zone_name)
DataTypeDateTime::DataTypeDateTime(const TimezoneMixin & time_zone_) DataTypeDateTime::DataTypeDateTime(const TimezoneMixin & time_zone_)
: TimezoneMixin(time_zone_) : TimezoneMixin(time_zone_)
{} {
}
String DataTypeDateTime::doGetName() const String DataTypeDateTime::doGetName() const
{ {
@ -86,8 +91,10 @@ void DataTypeDateTime::deserializeWholeText(IColumn & column, ReadBuffer & istr,
void DataTypeDateTime::deserializeTextEscaped(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const void DataTypeDateTime::deserializeTextEscaped(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const
{ {
time_t x; time_t x = 0;
::readText(x, istr, settings, time_zone, utc_time_zone); readTextHelper(x, istr, settings, time_zone, utc_time_zone);
if (x < 0)
x = 0;
assert_cast<ColumnType &>(column).getData().push_back(x); assert_cast<ColumnType &>(column).getData().push_back(x);
} }
@ -100,16 +107,19 @@ void DataTypeDateTime::serializeTextQuoted(const IColumn & column, size_t row_nu
void DataTypeDateTime::deserializeTextQuoted(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const void DataTypeDateTime::deserializeTextQuoted(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const
{ {
time_t x; time_t x = 0;
if (checkChar('\'', istr)) /// Cases: '2017-08-31 18:36:48' or '1504193808' if (checkChar('\'', istr)) /// Cases: '2017-08-31 18:36:48' or '1504193808'
{ {
::readText(x, istr, settings, time_zone, utc_time_zone); readTextHelper(x, istr, settings, time_zone, utc_time_zone);
assertChar('\'', istr); assertChar('\'', istr);
} }
else /// Just 1504193808 or 01504193808 else /// Just 1504193808 or 01504193808
{ {
readIntText(x, istr); readIntText(x, istr);
} }
if (x < 0)
x = 0;
assert_cast<ColumnType &>(column).getData().push_back(x); /// It's important to do this at the end - for exception safety. assert_cast<ColumnType &>(column).getData().push_back(x); /// It's important to do this at the end - for exception safety.
} }
@ -122,16 +132,21 @@ void DataTypeDateTime::serializeTextJSON(const IColumn & column, size_t row_num,
void DataTypeDateTime::deserializeTextJSON(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const void DataTypeDateTime::deserializeTextJSON(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const
{ {
time_t x; time_t x = 0;
if (checkChar('"', istr)) if (checkChar('"', istr))
{ {
::readText(x, istr, settings, time_zone, utc_time_zone); readTextHelper(x, istr, settings, time_zone, utc_time_zone);
assertChar('"', istr); assertChar('"', istr);
} }
else else
{ {
readIntText(x, istr); readIntText(x, istr);
} }
if (x < 0)
x = 0;
assert_cast<ColumnType &>(column).getData().push_back(x); assert_cast<ColumnType &>(column).getData().push_back(x);
} }
@ -144,7 +159,7 @@ void DataTypeDateTime::serializeTextCSV(const IColumn & column, size_t row_num,
void DataTypeDateTime::deserializeTextCSV(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const void DataTypeDateTime::deserializeTextCSV(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const
{ {
time_t x; time_t x = 0;
if (istr.eof()) if (istr.eof())
throwReadAfterEOF(); throwReadAfterEOF();
@ -154,11 +169,14 @@ void DataTypeDateTime::deserializeTextCSV(IColumn & column, ReadBuffer & istr, c
if (maybe_quote == '\'' || maybe_quote == '\"') if (maybe_quote == '\'' || maybe_quote == '\"')
++istr.position(); ++istr.position();
::readText(x, istr, settings, time_zone, utc_time_zone); readTextHelper(x, istr, settings, time_zone, utc_time_zone);
if (maybe_quote == '\'' || maybe_quote == '\"') if (maybe_quote == '\'' || maybe_quote == '\"')
assertChar(maybe_quote, istr); assertChar(maybe_quote, istr);
if (x < 0)
x = 0;
assert_cast<ColumnType &>(column).getData().push_back(x); assert_cast<ColumnType &>(column).getData().push_back(x);
} }

View File

@ -48,66 +48,5 @@ public:
bool canBePromoted() const override { return false; } bool canBePromoted() const override { return false; }
}; };
/** Tansform-type wrapper for DateTime64, applies given Transform to DateTime64 value or only to a whole part of it.
*
* Depending on what overloads of Transform::execute() are available, when called with DateTime64 value,
* invokes Transform::execute() with:
* * whole part of DateTime64 value, discarding fractional part.
* * DateTime64 value and scale factor.
*
* Suitable Transfotm-types are commonly used in Date/DateTime manipulation functions,
* and should implement static (or const) function with following signatures:
* R execute(UInt32 whole_value, ... , const TimeZoneImpl &)
* OR
* R execute(DateTime64 value, Int64 scale_factor, ... , const TimeZoneImpl &)
*
* Where R and T could be arbitrary types.
*/
template <typename Transform>
class TransformDateTime64 : public Transform
{
private:
// Detect if Transform::execute is const or static method
// with signature defined by template args (ignoring result type).
template<typename = void, typename... Args>
struct TransformHasExecuteOverload : std::false_type {};
template<typename... Args>
struct TransformHasExecuteOverload<std::void_t<decltype(std::declval<Transform>().execute(std::declval<Args>()...))>, Args...>
: std::true_type {};
template<typename... Args>
static constexpr bool TransformHasExecuteOverload_v = TransformHasExecuteOverload<void, Args...>::value;
public:
static constexpr auto name = Transform::name;
using Transform::execute;
// non-explicit constructor to allow creating from scale value (or with no scale at all), indispensable in some contexts.
TransformDateTime64(UInt32 scale_ = 0)
: scale_multiplier(DecimalUtils::scaleMultiplier<DateTime64::NativeType>(scale_))
{}
template <typename ... Args>
inline auto execute(const DateTime64 & t, Args && ... args) const
{
const auto transform = static_cast<const Transform *>(this);
if constexpr (TransformHasExecuteOverload_v<DateTime64, decltype(scale_multiplier), Args...>)
{
return transform->execute(t, scale_multiplier, std::forward<Args>(args)...);
}
else
{
const auto components = DecimalUtils::splitWithScaleMultiplier(t, scale_multiplier);
return transform->execute(static_cast<UInt32>(components.whole), std::forward<Args>(args)...);
}
}
private:
DateTime64::NativeType scale_multiplier = 1;
};
} }

View File

@ -16,6 +16,7 @@
#include <Poco/DirectoryIterator.h> #include <Poco/DirectoryIterator.h>
#include <Poco/File.h> #include <Poco/File.h>
#include <Databases/PostgreSQL/fetchPostgreSQLTableStructure.h> #include <Databases/PostgreSQL/fetchPostgreSQLTableStructure.h>
#include <Common/quoteString.h>
namespace DB namespace DB
@ -162,13 +163,13 @@ StoragePtr DatabasePostgreSQL::fetchTable(const String & table_name, const Conte
return StoragePtr{}; return StoragePtr{};
auto use_nulls = context.getSettingsRef().external_table_functions_use_nulls; auto use_nulls = context.getSettingsRef().external_table_functions_use_nulls;
auto columns = fetchPostgreSQLTableStructure(connection->conn(), table_name, use_nulls); auto columns = fetchPostgreSQLTableStructure(connection->conn(), doubleQuoteString(table_name), use_nulls);
if (!columns) if (!columns)
return StoragePtr{}; return StoragePtr{};
auto storage = StoragePostgreSQL::create( auto storage = StoragePostgreSQL::create(
StorageID(database_name, table_name), table_name, std::make_shared<PostgreSQLConnection>(connection->conn_str()), StorageID(database_name, table_name), table_name, std::make_shared<PostgreSQLConnection>(*connection),
ColumnsDescription{*columns}, ConstraintsDescription{}, context); ColumnsDescription{*columns}, ConstraintsDescription{}, context);
if (cache_tables) if (cache_tables)

View File

@ -118,10 +118,9 @@ std::string ClickHouseDictionarySource::getUpdateFieldAndDate()
{ {
if (update_time != std::chrono::system_clock::from_time_t(0)) if (update_time != std::chrono::system_clock::from_time_t(0))
{ {
auto tmp_time = update_time; time_t hr_time = std::chrono::system_clock::to_time_t(update_time) - 1;
std::string str_time = DateLUT::instance().timeToString(hr_time);
update_time = std::chrono::system_clock::now(); update_time = std::chrono::system_clock::now();
time_t hr_time = std::chrono::system_clock::to_time_t(tmp_time) - 1;
std::string str_time = std::to_string(LocalDateTime(hr_time));
return query_builder.composeUpdateQuery(update_field, str_time); return query_builder.composeUpdateQuery(update_field, str_time);
} }
else else

View File

@ -136,7 +136,7 @@ BlockInputStreamPtr HTTPDictionarySource::loadIds(const std::vector<UInt64> & id
ReadWriteBufferFromHTTP::OutStreamCallback out_stream_callback = [block, this](std::ostream & ostr) ReadWriteBufferFromHTTP::OutStreamCallback out_stream_callback = [block, this](std::ostream & ostr)
{ {
WriteBufferFromOStream out_buffer(ostr); WriteBufferFromOStream out_buffer(ostr);
auto output_stream = context.getOutputStream(format, out_buffer, sample_block); auto output_stream = context.getOutputStreamParallelIfPossible(format, out_buffer, sample_block);
formatBlock(output_stream, block); formatBlock(output_stream, block);
}; };
@ -157,7 +157,7 @@ BlockInputStreamPtr HTTPDictionarySource::loadKeys(const Columns & key_columns,
ReadWriteBufferFromHTTP::OutStreamCallback out_stream_callback = [block, this](std::ostream & ostr) ReadWriteBufferFromHTTP::OutStreamCallback out_stream_callback = [block, this](std::ostream & ostr)
{ {
WriteBufferFromOStream out_buffer(ostr); WriteBufferFromOStream out_buffer(ostr);
auto output_stream = context.getOutputStream(format, out_buffer, sample_block); auto output_stream = context.getOutputStreamParallelIfPossible(format, out_buffer, sample_block);
formatBlock(output_stream, block); formatBlock(output_stream, block);
}; };

View File

@ -107,10 +107,9 @@ std::string MySQLDictionarySource::getUpdateFieldAndDate()
{ {
if (update_time != std::chrono::system_clock::from_time_t(0)) if (update_time != std::chrono::system_clock::from_time_t(0))
{ {
auto tmp_time = update_time; time_t hr_time = std::chrono::system_clock::to_time_t(update_time) - 1;
std::string str_time = DateLUT::instance().timeToString(hr_time);
update_time = std::chrono::system_clock::now(); update_time = std::chrono::system_clock::now();
time_t hr_time = std::chrono::system_clock::to_time_t(tmp_time) - 1;
std::string str_time = std::to_string(LocalDateTime(hr_time));
return query_builder.composeUpdateQuery(update_field, str_time); return query_builder.composeUpdateQuery(update_field, str_time);
} }
else else
@ -262,7 +261,7 @@ LocalDateTime MySQLDictionarySource::getLastModification(mysqlxx::Pool::Entry &
if (!update_time_value.isNull()) if (!update_time_value.isNull())
{ {
modification_time = update_time_value.getDateTime(); modification_time = update_time_value.getDateTime();
LOG_TRACE(log, "Got modification time: {}", modification_time); LOG_TRACE(log, "Got modification time: {}", update_time_value.getString());
} }
/// fetch remaining rows to avoid "commands out of sync" error /// fetch remaining rows to avoid "commands out of sync" error

View File

@ -8,7 +8,6 @@
#include <Columns/ColumnString.h> #include <Columns/ColumnString.h>
#include <DataTypes/DataTypeString.h> #include <DataTypes/DataTypeString.h>
#include <DataStreams/PostgreSQLBlockInputStream.h> #include <DataStreams/PostgreSQLBlockInputStream.h>
#include <Storages/PostgreSQL/PostgreSQLConnection.h>
#include "readInvalidateQuery.h" #include "readInvalidateQuery.h"
#endif #endif
@ -29,11 +28,10 @@ PostgreSQLDictionarySource::PostgreSQLDictionarySource(
const DictionaryStructure & dict_struct_, const DictionaryStructure & dict_struct_,
const Poco::Util::AbstractConfiguration & config_, const Poco::Util::AbstractConfiguration & config_,
const std::string & config_prefix, const std::string & config_prefix,
PostgreSQLConnectionPtr connection_,
const Block & sample_block_) const Block & sample_block_)
: dict_struct{dict_struct_} : dict_struct{dict_struct_}
, sample_block(sample_block_) , sample_block(sample_block_)
, connection(std::move(connection_)) , connection(std::make_shared<PostgreSQLReplicaConnection>(config_, config_prefix))
, log(&Poco::Logger::get("PostgreSQLDictionarySource")) , log(&Poco::Logger::get("PostgreSQLDictionarySource"))
, db(config_.getString(fmt::format("{}.db", config_prefix), "")) , db(config_.getString(fmt::format("{}.db", config_prefix), ""))
, table(config_.getString(fmt::format("{}.table", config_prefix), "")) , table(config_.getString(fmt::format("{}.table", config_prefix), ""))
@ -50,7 +48,7 @@ PostgreSQLDictionarySource::PostgreSQLDictionarySource(
PostgreSQLDictionarySource::PostgreSQLDictionarySource(const PostgreSQLDictionarySource & other) PostgreSQLDictionarySource::PostgreSQLDictionarySource(const PostgreSQLDictionarySource & other)
: dict_struct(other.dict_struct) : dict_struct(other.dict_struct)
, sample_block(other.sample_block) , sample_block(other.sample_block)
, connection(std::make_shared<PostgreSQLConnection>(other.connection->conn_str())) , connection(other.connection)
, log(&Poco::Logger::get("PostgreSQLDictionarySource")) , log(&Poco::Logger::get("PostgreSQLDictionarySource"))
, db(other.db) , db(other.db)
, table(other.table) , table(other.table)
@ -68,8 +66,7 @@ PostgreSQLDictionarySource::PostgreSQLDictionarySource(const PostgreSQLDictionar
BlockInputStreamPtr PostgreSQLDictionarySource::loadAll() BlockInputStreamPtr PostgreSQLDictionarySource::loadAll()
{ {
LOG_TRACE(log, load_all_query); LOG_TRACE(log, load_all_query);
return std::make_shared<PostgreSQLBlockInputStream>( return loadBase(load_all_query);
connection->conn(), load_all_query, sample_block, max_block_size);
} }
@ -77,23 +74,28 @@ BlockInputStreamPtr PostgreSQLDictionarySource::loadUpdatedAll()
{ {
auto load_update_query = getUpdateFieldAndDate(); auto load_update_query = getUpdateFieldAndDate();
LOG_TRACE(log, load_update_query); LOG_TRACE(log, load_update_query);
return std::make_shared<PostgreSQLBlockInputStream>(connection->conn(), load_update_query, sample_block, max_block_size); return loadBase(load_update_query);
} }
BlockInputStreamPtr PostgreSQLDictionarySource::loadIds(const std::vector<UInt64> & ids) BlockInputStreamPtr PostgreSQLDictionarySource::loadIds(const std::vector<UInt64> & ids)
{ {
const auto query = query_builder.composeLoadIdsQuery(ids); const auto query = query_builder.composeLoadIdsQuery(ids);
return std::make_shared<PostgreSQLBlockInputStream>(connection->conn(), query, sample_block, max_block_size); return loadBase(query);
} }
BlockInputStreamPtr PostgreSQLDictionarySource::loadKeys(const Columns & key_columns, const std::vector<size_t> & requested_rows) BlockInputStreamPtr PostgreSQLDictionarySource::loadKeys(const Columns & key_columns, const std::vector<size_t> & requested_rows)
{ {
const auto query = query_builder.composeLoadKeysQuery(key_columns, requested_rows, ExternalQueryBuilder::AND_OR_CHAIN); const auto query = query_builder.composeLoadKeysQuery(key_columns, requested_rows, ExternalQueryBuilder::AND_OR_CHAIN);
return std::make_shared<PostgreSQLBlockInputStream>(connection->conn(), query, sample_block, max_block_size); return loadBase(query);
} }
BlockInputStreamPtr PostgreSQLDictionarySource::loadBase(const String & query)
{
return std::make_shared<PostgreSQLBlockInputStream>(connection->get(), query, sample_block, max_block_size);
}
bool PostgreSQLDictionarySource::isModified() const bool PostgreSQLDictionarySource::isModified() const
{ {
if (!invalidate_query.empty()) if (!invalidate_query.empty())
@ -112,7 +114,7 @@ std::string PostgreSQLDictionarySource::doInvalidateQuery(const std::string & re
Block invalidate_sample_block; Block invalidate_sample_block;
ColumnPtr column(ColumnString::create()); ColumnPtr column(ColumnString::create());
invalidate_sample_block.insert(ColumnWithTypeAndName(column, std::make_shared<DataTypeString>(), "Sample Block")); invalidate_sample_block.insert(ColumnWithTypeAndName(column, std::make_shared<DataTypeString>(), "Sample Block"));
PostgreSQLBlockInputStream block_input_stream(connection->conn(), request, invalidate_sample_block, 1); PostgreSQLBlockInputStream block_input_stream(connection->get(), request, invalidate_sample_block, 1);
return readInvalidateQuery(block_input_stream); return readInvalidateQuery(block_input_stream);
} }
@ -127,10 +129,9 @@ std::string PostgreSQLDictionarySource::getUpdateFieldAndDate()
{ {
if (update_time != std::chrono::system_clock::from_time_t(0)) if (update_time != std::chrono::system_clock::from_time_t(0))
{ {
auto tmp_time = update_time; time_t hr_time = std::chrono::system_clock::to_time_t(update_time) - 1;
std::string str_time = DateLUT::instance().timeToString(hr_time);
update_time = std::chrono::system_clock::now(); update_time = std::chrono::system_clock::now();
time_t hr_time = std::chrono::system_clock::to_time_t(tmp_time) - 1;
std::string str_time = std::to_string(LocalDateTime(hr_time));
return query_builder.composeUpdateQuery(update_field, str_time); return query_builder.composeUpdateQuery(update_field, str_time);
} }
else else
@ -172,15 +173,8 @@ void registerDictionarySourcePostgreSQL(DictionarySourceFactory & factory)
{ {
#if USE_LIBPQXX #if USE_LIBPQXX
const auto config_prefix = root_config_prefix + ".postgresql"; const auto config_prefix = root_config_prefix + ".postgresql";
auto connection = std::make_shared<PostgreSQLConnection>(
config.getString(fmt::format("{}.db", config_prefix), ""),
config.getString(fmt::format("{}.host", config_prefix), ""),
config.getUInt(fmt::format("{}.port", config_prefix), 0),
config.getString(fmt::format("{}.user", config_prefix), ""),
config.getString(fmt::format("{}.password", config_prefix), ""));
return std::make_unique<PostgreSQLDictionarySource>( return std::make_unique<PostgreSQLDictionarySource>(
dict_struct, config, config_prefix, connection, sample_block); dict_struct, config, config_prefix, sample_block);
#else #else
(void)dict_struct; (void)dict_struct;
(void)config; (void)config;

View File

@ -11,7 +11,7 @@
#include <Core/Block.h> #include <Core/Block.h>
#include <common/LocalDateTime.h> #include <common/LocalDateTime.h>
#include <common/logger_useful.h> #include <common/logger_useful.h>
#include <Storages/StoragePostgreSQL.h> #include <Storages/PostgreSQL/PostgreSQLReplicaConnection.h>
#include <pqxx/pqxx> #include <pqxx/pqxx>
@ -26,7 +26,6 @@ public:
const DictionaryStructure & dict_struct_, const DictionaryStructure & dict_struct_,
const Poco::Util::AbstractConfiguration & config_, const Poco::Util::AbstractConfiguration & config_,
const std::string & config_prefix, const std::string & config_prefix,
PostgreSQLConnectionPtr connection_,
const Block & sample_block_); const Block & sample_block_);
/// copy-constructor is provided in order to support cloneability /// copy-constructor is provided in order to support cloneability
@ -48,10 +47,11 @@ public:
private: private:
std::string getUpdateFieldAndDate(); std::string getUpdateFieldAndDate();
std::string doInvalidateQuery(const std::string & request) const; std::string doInvalidateQuery(const std::string & request) const;
BlockInputStreamPtr loadBase(const String & query);
const DictionaryStructure dict_struct; const DictionaryStructure dict_struct;
Block sample_block; Block sample_block;
PostgreSQLConnectionPtr connection; PostgreSQLReplicaConnectionPtr connection;
Poco::Logger * log; Poco::Logger * log;
const std::string db; const std::string db;

View File

@ -556,8 +556,9 @@ void RangeHashedDictionary::getIdsAndDates(
start_dates.push_back(value.range.left); start_dates.push_back(value.range.left);
end_dates.push_back(value.range.right); end_dates.push_back(value.range.right);
if (is_date && static_cast<UInt64>(end_dates.back()) > DATE_LUT_MAX_DAY_NUM) if constexpr (std::numeric_limits<RangeType>::max() > DATE_LUT_MAX_DAY_NUM) /// Avoid warning about tautological comparison in next line.
end_dates.back() = 0; if (is_date && static_cast<UInt64>(end_dates.back()) > DATE_LUT_MAX_DAY_NUM)
end_dates.back() = 0;
} }
} }
} }

View File

@ -99,8 +99,15 @@ namespace DB
assert_cast<ColumnUInt16 &>(column).insertValue(parse<LocalDate>(string_value).getDayNum()); assert_cast<ColumnUInt16 &>(column).insertValue(parse<LocalDate>(string_value).getDayNum());
break; break;
case ValueType::vtDateTime: case ValueType::vtDateTime:
assert_cast<ColumnUInt32 &>(column).insertValue(static_cast<UInt32>(parse<LocalDateTime>(string_value))); {
ReadBufferFromString in(string_value);
time_t time = 0;
readDateTimeText(time, in);
if (time < 0)
time = 0;
assert_cast<ColumnUInt32 &>(column).insertValue(time);
break; break;
}
case ValueType::vtUUID: case ValueType::vtUUID:
assert_cast<ColumnUInt128 &>(column).insertValue(parse<UUID>(string_value)); assert_cast<ColumnUInt128 &>(column).insertValue(parse<UUID>(string_value));
break; break;

View File

@ -156,10 +156,9 @@ std::string XDBCDictionarySource::getUpdateFieldAndDate()
{ {
if (update_time != std::chrono::system_clock::from_time_t(0)) if (update_time != std::chrono::system_clock::from_time_t(0))
{ {
auto tmp_time = update_time; time_t hr_time = std::chrono::system_clock::to_time_t(update_time) - 1;
std::string str_time = DateLUT::instance().timeToString(hr_time);
update_time = std::chrono::system_clock::now(); update_time = std::chrono::system_clock::now();
time_t hr_time = std::chrono::system_clock::to_time_t(tmp_time) - 1;
std::string str_time = std::to_string(LocalDateTime(hr_time));
return query_builder.composeUpdateQuery(update_field, str_time); return query_builder.composeUpdateQuery(update_field, str_time);
} }
else else

View File

@ -11,6 +11,7 @@
# include <Columns/ColumnFixedString.h> # include <Columns/ColumnFixedString.h>
# include <DataTypes/IDataType.h> # include <DataTypes/IDataType.h>
# include <DataTypes/DataTypeNullable.h> # include <DataTypes/DataTypeNullable.h>
# include <IO/ReadBufferFromString.h>
# include <IO/ReadHelpers.h> # include <IO/ReadHelpers.h>
# include <IO/WriteHelpers.h> # include <IO/WriteHelpers.h>
# include <IO/Operators.h> # include <IO/Operators.h>
@ -97,8 +98,15 @@ namespace
assert_cast<ColumnUInt16 &>(column).insertValue(UInt16(value.getDate().getDayNum())); assert_cast<ColumnUInt16 &>(column).insertValue(UInt16(value.getDate().getDayNum()));
break; break;
case ValueType::vtDateTime: case ValueType::vtDateTime:
assert_cast<ColumnUInt32 &>(column).insertValue(UInt32(value.getDateTime())); {
ReadBufferFromString in(value);
time_t time = 0;
readDateTimeText(time, in);
if (time < 0)
time = 0;
assert_cast<ColumnUInt32 &>(column).insertValue(time);
break; break;
}
case ValueType::vtUUID: case ValueType::vtUUID:
assert_cast<ColumnUInt128 &>(column).insert(parse<UUID>(value.data(), value.size())); assert_cast<ColumnUInt128 &>(column).insert(parse<UUID>(value.data(), value.size()));
break; break;

View File

@ -1486,6 +1486,8 @@ namespace
ReadBufferFromString buf{str}; ReadBufferFromString buf{str};
time_t tm = 0; time_t tm = 0;
readDateTimeText(tm, buf); readDateTimeText(tm, buf);
if (tm < 0)
tm = 0;
return tm; return tm;
} }

View File

@ -33,14 +33,21 @@ static inline UInt32 dateIsNotSupported(const char * name)
/// This factor transformation will say that the function is monotone everywhere. /// This factor transformation will say that the function is monotone everywhere.
struct ZeroTransform struct ZeroTransform
{ {
static inline UInt16 execute(UInt32, UInt8, const DateLUTImpl &) { return 0; }
static inline UInt16 execute(UInt16, UInt8, const DateLUTImpl &) { return 0; } static inline UInt16 execute(UInt16, UInt8, const DateLUTImpl &) { return 0; }
static inline UInt16 execute(UInt32, UInt8, const DateLUTImpl &) { return 0; }
static inline UInt16 execute(Int64, UInt8, const DateLUTImpl &) { return 0; }
}; };
struct ToWeekImpl struct ToWeekImpl
{ {
static constexpr auto name = "toWeek"; static constexpr auto name = "toWeek";
static inline UInt8 execute(Int64 t, UInt8 week_mode, const DateLUTImpl & time_zone)
{
// TODO: ditch conversion to DayNum, since it doesn't support extended range.
YearWeek yw = time_zone.toYearWeek(time_zone.toDayNum(t), week_mode);
return yw.second;
}
static inline UInt8 execute(UInt32 t, UInt8 week_mode, const DateLUTImpl & time_zone) static inline UInt8 execute(UInt32 t, UInt8 week_mode, const DateLUTImpl & time_zone)
{ {
YearWeek yw = time_zone.toYearWeek(time_zone.toDayNum(t), week_mode); YearWeek yw = time_zone.toYearWeek(time_zone.toDayNum(t), week_mode);
@ -59,6 +66,13 @@ struct ToYearWeekImpl
{ {
static constexpr auto name = "toYearWeek"; static constexpr auto name = "toYearWeek";
static inline UInt32 execute(Int64 t, UInt8 week_mode, const DateLUTImpl & time_zone)
{
// TODO: ditch toDayNum()
YearWeek yw = time_zone.toYearWeek(time_zone.toDayNum(t), week_mode | static_cast<UInt32>(WeekModeFlag::YEAR));
return yw.first * 100 + yw.second;
}
static inline UInt32 execute(UInt32 t, UInt8 week_mode, const DateLUTImpl & time_zone) static inline UInt32 execute(UInt32 t, UInt8 week_mode, const DateLUTImpl & time_zone)
{ {
YearWeek yw = time_zone.toYearWeek(time_zone.toDayNum(t), week_mode | static_cast<UInt32>(WeekModeFlag::YEAR)); YearWeek yw = time_zone.toYearWeek(time_zone.toDayNum(t), week_mode | static_cast<UInt32>(WeekModeFlag::YEAR));
@ -77,13 +91,19 @@ struct ToStartOfWeekImpl
{ {
static constexpr auto name = "toStartOfWeek"; static constexpr auto name = "toStartOfWeek";
static inline UInt16 execute(Int64 t, UInt8 week_mode, const DateLUTImpl & time_zone)
{
return time_zone.toFirstDayNumOfWeek(time_zone.toDayNum(t), week_mode);
// return time_zone.toFirstDayNumOfWeek(t, week_mode);
}
static inline UInt16 execute(UInt32 t, UInt8 week_mode, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt32 t, UInt8 week_mode, const DateLUTImpl & time_zone)
{ {
return time_zone.toFirstDayNumOfWeek(time_zone.toDayNum(t), week_mode); return time_zone.toFirstDayNumOfWeek(time_zone.toDayNum(t), week_mode);
// return time_zone.toFirstDayNumOfWeek(t, week_mode);
} }
static inline UInt16 execute(UInt16 d, UInt8 week_mode, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt16 d, UInt8 week_mode, const DateLUTImpl & time_zone)
{ {
return time_zone.toFirstDayNumOfWeek(DayNum(d), week_mode); return time_zone.toFirstDayNumOfWeek(ExtendedDayNum(d), week_mode);
} }
using FactorTransform = ZeroTransform; using FactorTransform = ZeroTransform;

View File

@ -3,6 +3,7 @@
#include <Core/DecimalFunctions.h> #include <Core/DecimalFunctions.h>
#include <Common/Exception.h> #include <Common/Exception.h>
#include <common/DateLUTImpl.h> #include <common/DateLUTImpl.h>
//#include <common/TimeZone.h>
#include <Columns/ColumnVector.h> #include <Columns/ColumnVector.h>
#include <Columns/ColumnDecimal.h> #include <Columns/ColumnDecimal.h>
#include <Functions/FunctionHelpers.h> #include <Functions/FunctionHelpers.h>
@ -33,14 +34,15 @@ namespace ErrorCodes
* factor-transformation F is "round to the nearest month" (2015-02-03 -> 2015-02-01). * factor-transformation F is "round to the nearest month" (2015-02-03 -> 2015-02-01).
*/ */
static inline UInt32 dateIsNotSupported(const char * name) static inline UInt32 dateIsNotSupported(const char * name)
{ {
throw Exception("Illegal type Date of argument for function " + std::string(name), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); throw Exception("Illegal type Date of argument for function " + std::string(name), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
} }
/// This factor transformation will say that the function is monotone everywhere. /// This factor transformation will say that the function is monotone everywhere.
struct ZeroTransform struct ZeroTransform
{ {
static inline UInt16 execute(Int64, const DateLUTImpl &) { return 0; }
static inline UInt16 execute(UInt32, const DateLUTImpl &) { return 0; } static inline UInt16 execute(UInt32, const DateLUTImpl &) { return 0; }
static inline UInt16 execute(UInt16, const DateLUTImpl &) { return 0; } static inline UInt16 execute(UInt16, const DateLUTImpl &) { return 0; }
}; };
@ -49,6 +51,10 @@ struct ToDateImpl
{ {
static constexpr auto name = "toDate"; static constexpr auto name = "toDate";
static inline UInt16 execute(Int64 t, const DateLUTImpl & time_zone)
{
return UInt16(time_zone.toDayNum(t));
}
static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return UInt16(time_zone.toDayNum(t)); return UInt16(time_zone.toDayNum(t));
@ -65,13 +71,18 @@ struct ToStartOfDayImpl
{ {
static constexpr auto name = "toStartOfDay"; static constexpr auto name = "toStartOfDay";
//TODO: right now it is hardcoded to produce DateTime only, needs fixing later. See date_and_time_type_details::ResultDataTypeMap for deduction of result type example.
static inline UInt32 execute(const DecimalUtils::DecimalComponents<DateTime64> & t, const DateLUTImpl & time_zone)
{
return time_zone.toDate(static_cast<time_t>(t.whole));
}
static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toDate(t); return time_zone.toDate(t);
} }
static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.toDate(DayNum(d)); return time_zone.toDate(ExtendedDayNum(d));
} }
using FactorTransform = ZeroTransform; using FactorTransform = ZeroTransform;
@ -81,13 +92,19 @@ struct ToMondayImpl
{ {
static constexpr auto name = "toMonday"; static constexpr auto name = "toMonday";
static inline UInt16 execute(Int64 t, const DateLUTImpl & time_zone)
{
//return time_zone.toFirstDayNumOfWeek(time_zone.toDayNum(t));
return time_zone.toFirstDayNumOfWeek(t);
}
static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toFirstDayNumOfWeek(time_zone.toDayNum(t)); //return time_zone.toFirstDayNumOfWeek(time_zone.toDayNum(t));
return time_zone.toFirstDayNumOfWeek(t);
} }
static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.toFirstDayNumOfWeek(DayNum(d)); return time_zone.toFirstDayNumOfWeek(ExtendedDayNum(d));
} }
using FactorTransform = ZeroTransform; using FactorTransform = ZeroTransform;
@ -97,13 +114,17 @@ struct ToStartOfMonthImpl
{ {
static constexpr auto name = "toStartOfMonth"; static constexpr auto name = "toStartOfMonth";
static inline UInt16 execute(Int64 t, const DateLUTImpl & time_zone)
{
return time_zone.toFirstDayNumOfMonth(time_zone.toDayNum(t));
}
static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toFirstDayNumOfMonth(time_zone.toDayNum(t)); return time_zone.toFirstDayNumOfMonth(time_zone.toDayNum(t));
} }
static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.toFirstDayNumOfMonth(DayNum(d)); return time_zone.toFirstDayNumOfMonth(ExtendedDayNum(d));
} }
using FactorTransform = ZeroTransform; using FactorTransform = ZeroTransform;
@ -113,13 +134,17 @@ struct ToStartOfQuarterImpl
{ {
static constexpr auto name = "toStartOfQuarter"; static constexpr auto name = "toStartOfQuarter";
static inline UInt16 execute(Int64 t, const DateLUTImpl & time_zone)
{
return time_zone.toFirstDayNumOfQuarter(time_zone.toDayNum(t));
}
static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toFirstDayNumOfQuarter(time_zone.toDayNum(t)); return time_zone.toFirstDayNumOfQuarter(time_zone.toDayNum(t));
} }
static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.toFirstDayNumOfQuarter(DayNum(d)); return time_zone.toFirstDayNumOfQuarter(ExtendedDayNum(d));
} }
using FactorTransform = ZeroTransform; using FactorTransform = ZeroTransform;
@ -129,13 +154,17 @@ struct ToStartOfYearImpl
{ {
static constexpr auto name = "toStartOfYear"; static constexpr auto name = "toStartOfYear";
static inline UInt16 execute(Int64 t, const DateLUTImpl & time_zone)
{
return time_zone.toFirstDayNumOfYear(time_zone.toDayNum(t));
}
static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toFirstDayNumOfYear(time_zone.toDayNum(t)); return time_zone.toFirstDayNumOfYear(time_zone.toDayNum(t));
} }
static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.toFirstDayNumOfYear(DayNum(d)); return time_zone.toFirstDayNumOfYear(ExtendedDayNum(d));
} }
using FactorTransform = ZeroTransform; using FactorTransform = ZeroTransform;
@ -144,9 +173,13 @@ struct ToStartOfYearImpl
struct ToTimeImpl struct ToTimeImpl
{ {
/// When transforming to time, the date will be equated to 1970-01-01.
static constexpr auto name = "toTime"; static constexpr auto name = "toTime";
/// When transforming to time, the date will be equated to 1970-01-02. static UInt32 execute(const DecimalUtils::DecimalComponents<DateTime64> & t, const DateLUTImpl & time_zone)
{
return time_zone.toTime(t.whole) + 86400;
}
static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toTime(t) + 86400; return time_zone.toTime(t) + 86400;
@ -164,6 +197,10 @@ struct ToStartOfMinuteImpl
{ {
static constexpr auto name = "toStartOfMinute"; static constexpr auto name = "toStartOfMinute";
static inline UInt32 execute(const DecimalUtils::DecimalComponents<DateTime64> & t, const DateLUTImpl & time_zone)
{
return time_zone.toStartOfMinute(t.whole);
}
static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toStartOfMinute(t); return time_zone.toStartOfMinute(t);
@ -215,6 +252,10 @@ struct ToStartOfFiveMinuteImpl
{ {
static constexpr auto name = "toStartOfFiveMinute"; static constexpr auto name = "toStartOfFiveMinute";
static inline UInt32 execute(const DecimalUtils::DecimalComponents<DateTime64> & t, const DateLUTImpl & time_zone)
{
return time_zone.toStartOfFiveMinute(t.whole);
}
static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toStartOfFiveMinute(t); return time_zone.toStartOfFiveMinute(t);
@ -231,6 +272,10 @@ struct ToStartOfTenMinutesImpl
{ {
static constexpr auto name = "toStartOfTenMinutes"; static constexpr auto name = "toStartOfTenMinutes";
static inline UInt32 execute(const DecimalUtils::DecimalComponents<DateTime64> & t, const DateLUTImpl & time_zone)
{
return time_zone.toStartOfTenMinutes(t.whole);
}
static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toStartOfTenMinutes(t); return time_zone.toStartOfTenMinutes(t);
@ -247,6 +292,10 @@ struct ToStartOfFifteenMinutesImpl
{ {
static constexpr auto name = "toStartOfFifteenMinutes"; static constexpr auto name = "toStartOfFifteenMinutes";
static inline UInt32 execute(const DecimalUtils::DecimalComponents<DateTime64> & t, const DateLUTImpl & time_zone)
{
return time_zone.toStartOfFifteenMinutes(t.whole);
}
static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toStartOfFifteenMinutes(t); return time_zone.toStartOfFifteenMinutes(t);
@ -264,6 +313,12 @@ struct TimeSlotImpl
{ {
static constexpr auto name = "timeSlot"; static constexpr auto name = "timeSlot";
//static inline DecimalUtils::DecimalComponents<DateTime64> execute(const DecimalUtils::DecimalComponents<DateTime64> & t, const DateLUTImpl &)
static inline UInt32 execute(const DecimalUtils::DecimalComponents<DateTime64> & t, const DateLUTImpl &)
{
return t.whole / 1800 * 1800;
}
static inline UInt32 execute(UInt32 t, const DateLUTImpl &) static inline UInt32 execute(UInt32 t, const DateLUTImpl &)
{ {
return t / 1800 * 1800; return t / 1800 * 1800;
@ -281,6 +336,11 @@ struct ToStartOfHourImpl
{ {
static constexpr auto name = "toStartOfHour"; static constexpr auto name = "toStartOfHour";
static inline UInt32 execute(const DecimalUtils::DecimalComponents<DateTime64> & t, const DateLUTImpl & time_zone)
{
return time_zone.toStartOfHour(t.whole);
}
static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toStartOfHour(t); return time_zone.toStartOfHour(t);
@ -298,13 +358,17 @@ struct ToYearImpl
{ {
static constexpr auto name = "toYear"; static constexpr auto name = "toYear";
static inline UInt16 execute(Int64 t, const DateLUTImpl & time_zone)
{
return time_zone.toYear(t);
}
static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toYear(t); return time_zone.toYear(t);
} }
static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.toYear(DayNum(d)); return time_zone.toYear(ExtendedDayNum(d));
} }
using FactorTransform = ZeroTransform; using FactorTransform = ZeroTransform;
@ -314,13 +378,17 @@ struct ToQuarterImpl
{ {
static constexpr auto name = "toQuarter"; static constexpr auto name = "toQuarter";
static inline UInt8 execute(Int64 t, const DateLUTImpl & time_zone)
{
return time_zone.toQuarter(t);
}
static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toQuarter(t); return time_zone.toQuarter(t);
} }
static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.toQuarter(DayNum(d)); return time_zone.toQuarter(ExtendedDayNum(d));
} }
using FactorTransform = ToStartOfYearImpl; using FactorTransform = ToStartOfYearImpl;
@ -330,13 +398,17 @@ struct ToMonthImpl
{ {
static constexpr auto name = "toMonth"; static constexpr auto name = "toMonth";
static inline UInt8 execute(Int64 t, const DateLUTImpl & time_zone)
{
return time_zone.toMonth(t);
}
static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toMonth(t); return time_zone.toMonth(t);
} }
static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.toMonth(DayNum(d)); return time_zone.toMonth(ExtendedDayNum(d));
} }
using FactorTransform = ToStartOfYearImpl; using FactorTransform = ToStartOfYearImpl;
@ -346,13 +418,17 @@ struct ToDayOfMonthImpl
{ {
static constexpr auto name = "toDayOfMonth"; static constexpr auto name = "toDayOfMonth";
static inline UInt8 execute(Int64 t, const DateLUTImpl & time_zone)
{
return time_zone.toDayOfMonth(t);
}
static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toDayOfMonth(t); return time_zone.toDayOfMonth(t);
} }
static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.toDayOfMonth(DayNum(d)); return time_zone.toDayOfMonth(ExtendedDayNum(d));
} }
using FactorTransform = ToStartOfMonthImpl; using FactorTransform = ToStartOfMonthImpl;
@ -362,13 +438,17 @@ struct ToDayOfWeekImpl
{ {
static constexpr auto name = "toDayOfWeek"; static constexpr auto name = "toDayOfWeek";
static inline UInt8 execute(Int64 t, const DateLUTImpl & time_zone)
{
return time_zone.toDayOfWeek(t);
}
static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toDayOfWeek(t); return time_zone.toDayOfWeek(t);
} }
static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.toDayOfWeek(DayNum(d)); return time_zone.toDayOfWeek(ExtendedDayNum(d));
} }
using FactorTransform = ToMondayImpl; using FactorTransform = ToMondayImpl;
@ -378,13 +458,17 @@ struct ToDayOfYearImpl
{ {
static constexpr auto name = "toDayOfYear"; static constexpr auto name = "toDayOfYear";
static inline UInt16 execute(Int64 t, const DateLUTImpl & time_zone)
{
return time_zone.toDayOfYear(t);
}
static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toDayOfYear(t); return time_zone.toDayOfYear(t);
} }
static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.toDayOfYear(DayNum(d)); return time_zone.toDayOfYear(ExtendedDayNum(d));
} }
using FactorTransform = ToStartOfYearImpl; using FactorTransform = ToStartOfYearImpl;
@ -394,6 +478,10 @@ struct ToHourImpl
{ {
static constexpr auto name = "toHour"; static constexpr auto name = "toHour";
static inline UInt8 execute(Int64 t, const DateLUTImpl & time_zone)
{
return time_zone.toHour(t);
}
static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toHour(t); return time_zone.toHour(t);
@ -411,6 +499,11 @@ struct TimezoneOffsetImpl
{ {
static constexpr auto name = "timezoneOffset"; static constexpr auto name = "timezoneOffset";
static inline time_t execute(Int64 t, const DateLUTImpl & time_zone)
{
return time_zone.timezoneOffset(t);
}
static inline time_t execute(UInt32 t, const DateLUTImpl & time_zone) static inline time_t execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.timezoneOffset(t); return time_zone.timezoneOffset(t);
@ -428,6 +521,10 @@ struct ToMinuteImpl
{ {
static constexpr auto name = "toMinute"; static constexpr auto name = "toMinute";
static inline UInt8 execute(Int64 t, const DateLUTImpl & time_zone)
{
return time_zone.toMinute(t);
}
static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toMinute(t); return time_zone.toMinute(t);
@ -444,6 +541,10 @@ struct ToSecondImpl
{ {
static constexpr auto name = "toSecond"; static constexpr auto name = "toSecond";
static inline UInt8 execute(Int64 t, const DateLUTImpl & time_zone)
{
return time_zone.toSecond(t);
}
static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toSecond(t); return time_zone.toSecond(t);
@ -460,13 +561,17 @@ struct ToISOYearImpl
{ {
static constexpr auto name = "toISOYear"; static constexpr auto name = "toISOYear";
static inline UInt16 execute(Int64 t, const DateLUTImpl & time_zone)
{
return time_zone.toISOYear(time_zone.toDayNum(t));
}
static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toISOYear(time_zone.toDayNum(t)); return time_zone.toISOYear(time_zone.toDayNum(t));
} }
static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.toISOYear(DayNum(d)); return time_zone.toISOYear(ExtendedDayNum(d));
} }
using FactorTransform = ZeroTransform; using FactorTransform = ZeroTransform;
@ -476,13 +581,17 @@ struct ToStartOfISOYearImpl
{ {
static constexpr auto name = "toStartOfISOYear"; static constexpr auto name = "toStartOfISOYear";
static inline UInt16 execute(Int64 t, const DateLUTImpl & time_zone)
{
return time_zone.toFirstDayNumOfISOYear(time_zone.toDayNum(t));
}
static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toFirstDayNumOfISOYear(time_zone.toDayNum(t)); return time_zone.toFirstDayNumOfISOYear(time_zone.toDayNum(t));
} }
static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.toFirstDayNumOfISOYear(DayNum(d)); return time_zone.toFirstDayNumOfISOYear(ExtendedDayNum(d));
} }
using FactorTransform = ZeroTransform; using FactorTransform = ZeroTransform;
@ -492,13 +601,17 @@ struct ToISOWeekImpl
{ {
static constexpr auto name = "toISOWeek"; static constexpr auto name = "toISOWeek";
static inline UInt8 execute(Int64 t, const DateLUTImpl & time_zone)
{
return time_zone.toISOWeek(time_zone.toDayNum(t));
}
static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toISOWeek(time_zone.toDayNum(t)); return time_zone.toISOWeek(time_zone.toDayNum(t));
} }
static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.toISOWeek(DayNum(d)); return time_zone.toISOWeek(ExtendedDayNum(d));
} }
using FactorTransform = ToISOYearImpl; using FactorTransform = ToISOYearImpl;
@ -508,13 +621,17 @@ struct ToRelativeYearNumImpl
{ {
static constexpr auto name = "toRelativeYearNum"; static constexpr auto name = "toRelativeYearNum";
static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt16 execute(Int64 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toYear(t); return time_zone.toYear(t);
} }
static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
{
return time_zone.toYear(static_cast<time_t>(t));
}
static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.toYear(DayNum(d)); return time_zone.toYear(ExtendedDayNum(d));
} }
using FactorTransform = ZeroTransform; using FactorTransform = ZeroTransform;
@ -524,13 +641,17 @@ struct ToRelativeQuarterNumImpl
{ {
static constexpr auto name = "toRelativeQuarterNum"; static constexpr auto name = "toRelativeQuarterNum";
static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt16 execute(Int64 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toRelativeQuarterNum(t); return time_zone.toRelativeQuarterNum(t);
} }
static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
{
return time_zone.toRelativeQuarterNum(static_cast<time_t>(t));
}
static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.toRelativeQuarterNum(DayNum(d)); return time_zone.toRelativeQuarterNum(ExtendedDayNum(d));
} }
using FactorTransform = ZeroTransform; using FactorTransform = ZeroTransform;
@ -540,13 +661,17 @@ struct ToRelativeMonthNumImpl
{ {
static constexpr auto name = "toRelativeMonthNum"; static constexpr auto name = "toRelativeMonthNum";
static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt16 execute(Int64 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toRelativeMonthNum(t); return time_zone.toRelativeMonthNum(t);
} }
static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
{
return time_zone.toRelativeMonthNum(static_cast<time_t>(t));
}
static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.toRelativeMonthNum(DayNum(d)); return time_zone.toRelativeMonthNum(ExtendedDayNum(d));
} }
using FactorTransform = ZeroTransform; using FactorTransform = ZeroTransform;
@ -556,13 +681,17 @@ struct ToRelativeWeekNumImpl
{ {
static constexpr auto name = "toRelativeWeekNum"; static constexpr auto name = "toRelativeWeekNum";
static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt16 execute(Int64 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toRelativeWeekNum(t); return time_zone.toRelativeWeekNum(t);
} }
static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
{
return time_zone.toRelativeWeekNum(static_cast<time_t>(t));
}
static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.toRelativeWeekNum(DayNum(d)); return time_zone.toRelativeWeekNum(ExtendedDayNum(d));
} }
using FactorTransform = ZeroTransform; using FactorTransform = ZeroTransform;
@ -572,10 +701,14 @@ struct ToRelativeDayNumImpl
{ {
static constexpr auto name = "toRelativeDayNum"; static constexpr auto name = "toRelativeDayNum";
static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt16 execute(Int64 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toDayNum(t); return time_zone.toDayNum(t);
} }
static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone)
{
return time_zone.toDayNum(static_cast<time_t>(t));
}
static inline UInt16 execute(UInt16 d, const DateLUTImpl &) static inline UInt16 execute(UInt16 d, const DateLUTImpl &)
{ {
return static_cast<DayNum>(d); return static_cast<DayNum>(d);
@ -589,13 +722,17 @@ struct ToRelativeHourNumImpl
{ {
static constexpr auto name = "toRelativeHourNum"; static constexpr auto name = "toRelativeHourNum";
static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt32 execute(Int64 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toRelativeHourNum(t); return time_zone.toRelativeHourNum(t);
} }
static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
{
return time_zone.toRelativeHourNum(static_cast<time_t>(t));
}
static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.toRelativeHourNum(DayNum(d)); return time_zone.toRelativeHourNum(ExtendedDayNum(d));
} }
using FactorTransform = ZeroTransform; using FactorTransform = ZeroTransform;
@ -605,13 +742,17 @@ struct ToRelativeMinuteNumImpl
{ {
static constexpr auto name = "toRelativeMinuteNum"; static constexpr auto name = "toRelativeMinuteNum";
static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt32 execute(Int64 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toRelativeMinuteNum(t); return time_zone.toRelativeMinuteNum(t);
} }
static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
{
return time_zone.toRelativeMinuteNum(static_cast<time_t>(t));
}
static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.toRelativeMinuteNum(DayNum(d)); return time_zone.toRelativeMinuteNum(ExtendedDayNum(d));
} }
using FactorTransform = ZeroTransform; using FactorTransform = ZeroTransform;
@ -621,13 +762,17 @@ struct ToRelativeSecondNumImpl
{ {
static constexpr auto name = "toRelativeSecondNum"; static constexpr auto name = "toRelativeSecondNum";
static inline Int64 execute(Int64 t, const DateLUTImpl &)
{
return t;
}
static inline UInt32 execute(UInt32 t, const DateLUTImpl &) static inline UInt32 execute(UInt32 t, const DateLUTImpl &)
{ {
return t; return t;
} }
static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone) static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone)
{ {
return time_zone.fromDayNum(DayNum(d)); return time_zone.fromDayNum(ExtendedDayNum(d));
} }
using FactorTransform = ZeroTransform; using FactorTransform = ZeroTransform;
@ -637,6 +782,10 @@ struct ToYYYYMMImpl
{ {
static constexpr auto name = "toYYYYMM"; static constexpr auto name = "toYYYYMM";
static inline UInt32 execute(Int64 t, const DateLUTImpl & time_zone)
{
return time_zone.toNumYYYYMM(t);
}
static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toNumYYYYMM(t); return time_zone.toNumYYYYMM(t);
@ -653,6 +802,10 @@ struct ToYYYYMMDDImpl
{ {
static constexpr auto name = "toYYYYMMDD"; static constexpr auto name = "toYYYYMMDD";
static inline UInt32 execute(Int64 t, const DateLUTImpl & time_zone)
{
return time_zone.toNumYYYYMMDD(t);
}
static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toNumYYYYMMDD(t); return time_zone.toNumYYYYMMDD(t);
@ -669,6 +822,10 @@ struct ToYYYYMMDDhhmmssImpl
{ {
static constexpr auto name = "toYYYYMMDDhhmmss"; static constexpr auto name = "toYYYYMMDDhhmmss";
static inline UInt64 execute(Int64 t, const DateLUTImpl & time_zone)
{
return time_zone.toNumYYYYMMDDhhmmss(t);
}
static inline UInt64 execute(UInt32 t, const DateLUTImpl & time_zone) static inline UInt64 execute(UInt32 t, const DateLUTImpl & time_zone)
{ {
return time_zone.toNumYYYYMMDDhhmmss(t); return time_zone.toNumYYYYMMDDhhmmss(t);

View File

@ -4,6 +4,7 @@
#include <DataTypes/DataTypeDateTime64.h> #include <DataTypes/DataTypeDateTime64.h>
#include <Functions/CustomWeekTransforms.h> #include <Functions/CustomWeekTransforms.h>
#include <Functions/IFunctionImpl.h> #include <Functions/IFunctionImpl.h>
#include <Functions/TransformDateTime64.h>
#include <Functions/extractTimeZoneFromFunctionArguments.h> #include <Functions/extractTimeZoneFromFunctionArguments.h>
#include <IO/WriteHelpers.h> #include <IO/WriteHelpers.h>

View File

@ -11,6 +11,7 @@
#include <Functions/FunctionHelpers.h> #include <Functions/FunctionHelpers.h>
#include <Functions/castTypeToEither.h> #include <Functions/castTypeToEither.h>
#include <Functions/extractTimeZoneFromFunctionArguments.h> #include <Functions/extractTimeZoneFromFunctionArguments.h>
#include <Functions/TransformDateTime64.h>
#include <IO/WriteHelpers.h> #include <IO/WriteHelpers.h>
@ -25,31 +26,6 @@ namespace ErrorCodes
extern const int ILLEGAL_COLUMN; extern const int ILLEGAL_COLUMN;
} }
/// AddOnDateTime64DefaultImpl provides default implementation of add-X functionality for DateTime64.
///
/// Default implementation is not to change fractional part, but only modify whole part as if it was DateTime.
/// That means large whole values (for scale less than 9) might not fit into UInt32-range,
/// and hence default implementation will produce incorrect results.
template <typename T>
struct AddOnDateTime64DefaultImpl
{
AddOnDateTime64DefaultImpl(UInt32 scale_ = 0)
: scale_multiplier(DecimalUtils::scaleMultiplier<DateTime64::NativeType>(scale_))
{}
// Default implementation for add/sub on DateTime64: do math on whole part (the same way as for DateTime), leave fractional as it is.
inline DateTime64 execute(const DateTime64 & t, Int64 delta, const DateLUTImpl & time_zone) const
{
const auto components = DecimalUtils::splitWithScaleMultiplier(t, scale_multiplier);
const auto whole = static_cast<const T *>(this)->execute(static_cast<UInt32>(components.whole), delta, time_zone);
return DecimalUtils::decimalFromComponentsWithMultiplier<DateTime64>(static_cast<DateTime64::NativeType>(whole), components.fractional, scale_multiplier);
}
UInt32 scale_multiplier = 1;
};
/// Type of first argument of 'execute' function overload defines what INPUT DataType it is used for. /// Type of first argument of 'execute' function overload defines what INPUT DataType it is used for.
/// Return type defines what is the OUTPUT (return) type of the CH function. /// Return type defines what is the OUTPUT (return) type of the CH function.
/// Corresponding types: /// Corresponding types:
@ -60,14 +36,15 @@ struct AddOnDateTime64DefaultImpl
/// - 'AddSecondsImpl::execute(UInt32, ...) -> UInt32' is available to the ClickHouse users as 'addSeconds(DateTime, ...) -> DateTime' /// - 'AddSecondsImpl::execute(UInt32, ...) -> UInt32' is available to the ClickHouse users as 'addSeconds(DateTime, ...) -> DateTime'
/// - 'AddSecondsImpl::execute(UInt16, ...) -> UInt32' is available to the ClickHouse users as 'addSeconds(Date, ...) -> DateTime' /// - 'AddSecondsImpl::execute(UInt16, ...) -> UInt32' is available to the ClickHouse users as 'addSeconds(Date, ...) -> DateTime'
struct AddSecondsImpl : public AddOnDateTime64DefaultImpl<AddSecondsImpl> struct AddSecondsImpl
{ {
using Base = AddOnDateTime64DefaultImpl<AddSecondsImpl>;
using Base::Base;
using Base::execute;
static constexpr auto name = "addSeconds"; static constexpr auto name = "addSeconds";
static inline NO_SANITIZE_UNDEFINED DecimalUtils::DecimalComponents<DateTime64> execute(DecimalUtils::DecimalComponents<DateTime64> t, Int64 delta, const DateLUTImpl &)
{
return {t.whole + delta, t.fractional};
}
static inline NO_SANITIZE_UNDEFINED UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl &) static inline NO_SANITIZE_UNDEFINED UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl &)
{ {
return t + delta; return t + delta;
@ -75,18 +52,19 @@ struct AddSecondsImpl : public AddOnDateTime64DefaultImpl<AddSecondsImpl>
static inline NO_SANITIZE_UNDEFINED UInt32 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) static inline NO_SANITIZE_UNDEFINED UInt32 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone)
{ {
return time_zone.fromDayNum(DayNum(d)) + delta; return time_zone.fromDayNum(ExtendedDayNum(d)) + delta;
} }
}; };
struct AddMinutesImpl : public AddOnDateTime64DefaultImpl<AddMinutesImpl> struct AddMinutesImpl
{ {
using Base = AddOnDateTime64DefaultImpl<AddMinutesImpl>;
using Base::Base;
using Base::execute;
static constexpr auto name = "addMinutes"; static constexpr auto name = "addMinutes";
static inline NO_SANITIZE_UNDEFINED DecimalUtils::DecimalComponents<DateTime64> execute(DecimalUtils::DecimalComponents<DateTime64> t, Int64 delta, const DateLUTImpl &)
{
return {t.whole + delta * 60, t.fractional};
}
static inline NO_SANITIZE_UNDEFINED UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl &) static inline NO_SANITIZE_UNDEFINED UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl &)
{ {
return t + delta * 60; return t + delta * 60;
@ -94,18 +72,18 @@ struct AddMinutesImpl : public AddOnDateTime64DefaultImpl<AddMinutesImpl>
static inline NO_SANITIZE_UNDEFINED UInt32 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) static inline NO_SANITIZE_UNDEFINED UInt32 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone)
{ {
return time_zone.fromDayNum(DayNum(d)) + delta * 60; return time_zone.fromDayNum(ExtendedDayNum(d)) + delta * 60;
} }
}; };
struct AddHoursImpl : public AddOnDateTime64DefaultImpl<AddHoursImpl> struct AddHoursImpl
{ {
using Base = AddOnDateTime64DefaultImpl<AddHoursImpl>;
using Base::Base;
using Base::execute;
static constexpr auto name = "addHours"; static constexpr auto name = "addHours";
static inline NO_SANITIZE_UNDEFINED DecimalUtils::DecimalComponents<DateTime64> execute(DecimalUtils::DecimalComponents<DateTime64> t, Int64 delta, const DateLUTImpl &)
{
return {t.whole + delta * 3600, t.fractional};
}
static inline NO_SANITIZE_UNDEFINED UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl &) static inline NO_SANITIZE_UNDEFINED UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl &)
{ {
return t + delta * 3600; return t + delta * 3600;
@ -113,19 +91,20 @@ struct AddHoursImpl : public AddOnDateTime64DefaultImpl<AddHoursImpl>
static inline NO_SANITIZE_UNDEFINED UInt32 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) static inline NO_SANITIZE_UNDEFINED UInt32 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone)
{ {
return time_zone.fromDayNum(DayNum(d)) + delta * 3600; return time_zone.fromDayNum(ExtendedDayNum(d)) + delta * 3600;
} }
}; };
struct AddDaysImpl : public AddOnDateTime64DefaultImpl<AddDaysImpl> struct AddDaysImpl
{ {
using Base = AddOnDateTime64DefaultImpl<AddDaysImpl>;
using Base::Base;
using Base::execute;
static constexpr auto name = "addDays"; static constexpr auto name = "addDays";
static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone) static inline NO_SANITIZE_UNDEFINED DecimalUtils::DecimalComponents<DateTime64> execute(DecimalUtils::DecimalComponents<DateTime64> t, Int64 delta, const DateLUTImpl & time_zone)
{
return {time_zone.addDays(t.whole, delta), t.fractional};
}
static inline NO_SANITIZE_UNDEFINED UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone)
{ {
return time_zone.addDays(t, delta); return time_zone.addDays(t, delta);
} }
@ -136,14 +115,15 @@ struct AddDaysImpl : public AddOnDateTime64DefaultImpl<AddDaysImpl>
} }
}; };
struct AddWeeksImpl : public AddOnDateTime64DefaultImpl<AddWeeksImpl> struct AddWeeksImpl
{ {
using Base = AddOnDateTime64DefaultImpl<AddWeeksImpl>;
using Base::Base;
using Base::execute;
static constexpr auto name = "addWeeks"; static constexpr auto name = "addWeeks";
static inline NO_SANITIZE_UNDEFINED DecimalUtils::DecimalComponents<DateTime64> execute(DecimalUtils::DecimalComponents<DateTime64> t, Int64 delta, const DateLUTImpl & time_zone)
{
return {time_zone.addWeeks(t.whole, delta), t.fractional};
}
static inline NO_SANITIZE_UNDEFINED UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone) static inline NO_SANITIZE_UNDEFINED UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone)
{ {
return time_zone.addWeeks(t, delta); return time_zone.addWeeks(t, delta);
@ -155,14 +135,15 @@ struct AddWeeksImpl : public AddOnDateTime64DefaultImpl<AddWeeksImpl>
} }
}; };
struct AddMonthsImpl : public AddOnDateTime64DefaultImpl<AddMonthsImpl> struct AddMonthsImpl
{ {
using Base = AddOnDateTime64DefaultImpl<AddMonthsImpl>;
using Base::Base;
using Base::execute;
static constexpr auto name = "addMonths"; static constexpr auto name = "addMonths";
static inline DecimalUtils::DecimalComponents<DateTime64> execute(DecimalUtils::DecimalComponents<DateTime64> t, Int64 delta, const DateLUTImpl & time_zone)
{
return {time_zone.addMonths(t.whole, delta), t.fractional};
}
static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone) static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone)
{ {
return time_zone.addMonths(t, delta); return time_zone.addMonths(t, delta);
@ -170,18 +151,19 @@ struct AddMonthsImpl : public AddOnDateTime64DefaultImpl<AddMonthsImpl>
static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone)
{ {
return time_zone.addMonths(DayNum(d), delta); return time_zone.addMonths(ExtendedDayNum(d), delta);
} }
}; };
struct AddQuartersImpl : public AddOnDateTime64DefaultImpl<AddQuartersImpl> struct AddQuartersImpl
{ {
using Base = AddOnDateTime64DefaultImpl<AddQuartersImpl>;
using Base::Base;
using Base::execute;
static constexpr auto name = "addQuarters"; static constexpr auto name = "addQuarters";
static inline DecimalUtils::DecimalComponents<DateTime64> execute(DecimalUtils::DecimalComponents<DateTime64> t, Int64 delta, const DateLUTImpl & time_zone)
{
return {time_zone.addQuarters(t.whole, delta), t.fractional};
}
static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone) static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone)
{ {
return time_zone.addQuarters(t, delta); return time_zone.addQuarters(t, delta);
@ -189,18 +171,19 @@ struct AddQuartersImpl : public AddOnDateTime64DefaultImpl<AddQuartersImpl>
static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone)
{ {
return time_zone.addQuarters(DayNum(d), delta); return time_zone.addQuarters(ExtendedDayNum(d), delta);
} }
}; };
struct AddYearsImpl : public AddOnDateTime64DefaultImpl<AddYearsImpl> struct AddYearsImpl
{ {
using Base = AddOnDateTime64DefaultImpl<AddYearsImpl>;
using Base::Base;
using Base::execute;
static constexpr auto name = "addYears"; static constexpr auto name = "addYears";
static inline DecimalUtils::DecimalComponents<DateTime64> execute(DecimalUtils::DecimalComponents<DateTime64> t, Int64 delta, const DateLUTImpl & time_zone)
{
return {time_zone.addYears(t.whole, delta), t.fractional};
}
static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone) static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone)
{ {
return time_zone.addYears(t, delta); return time_zone.addYears(t, delta);
@ -208,7 +191,7 @@ struct AddYearsImpl : public AddOnDateTime64DefaultImpl<AddYearsImpl>
static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone)
{ {
return time_zone.addYears(DayNum(d), delta); return time_zone.addYears(ExtendedDayNum(d), delta);
} }
}; };
@ -351,6 +334,7 @@ template <> struct ResultDataTypeMap<Int16> { using ResultDataType = DataTy
template <> struct ResultDataTypeMap<UInt32> { using ResultDataType = DataTypeDateTime; }; template <> struct ResultDataTypeMap<UInt32> { using ResultDataType = DataTypeDateTime; };
template <> struct ResultDataTypeMap<Int32> { using ResultDataType = DataTypeDateTime; }; template <> struct ResultDataTypeMap<Int32> { using ResultDataType = DataTypeDateTime; };
template <> struct ResultDataTypeMap<DateTime64> { using ResultDataType = DataTypeDateTime64; }; template <> struct ResultDataTypeMap<DateTime64> { using ResultDataType = DataTypeDateTime64; };
template <> struct ResultDataTypeMap<Int64> { using ResultDataType = DataTypeDateTime64; };
} }
template <typename Transform> template <typename Transform>
@ -417,10 +401,18 @@ public:
} }
} }
// TransformDateTime64 helps choosing correct overload of exec and does some transformations
// on input and output parameters to simplify support of DateTime64 in concrete Transform.
template <typename FieldType>
using TransformType = std::conditional_t<
std::is_same_v<FieldType, DateTime64>,
TransformDateTime64<Transform>,
Transform>;
/// Helper templates to deduce return type based on argument type, since some overloads may promote or denote types, /// Helper templates to deduce return type based on argument type, since some overloads may promote or denote types,
/// e.g. addSeconds(Date, 1) => DateTime /// e.g. addSeconds(Date, 1) => DateTime
template <typename FieldType> template <typename FieldType>
using TransformExecuteReturnType = decltype(std::declval<Transform>().execute(FieldType(), 0, std::declval<DateLUTImpl>())); using TransformExecuteReturnType = decltype(std::declval<TransformType<FieldType>>().execute(FieldType(), 0, std::declval<DateLUTImpl>()));
// Deduces RETURN DataType from INPUT DataType, based on return type of Transform{}.execute(INPUT_TYPE, UInt64, DateLUTImpl). // Deduces RETURN DataType from INPUT DataType, based on return type of Transform{}.execute(INPUT_TYPE, UInt64, DateLUTImpl).
// e.g. for Transform-type that has execute()-overload with 'UInt16' input and 'UInt32' return, // e.g. for Transform-type that has execute()-overload with 'UInt16' input and 'UInt32' return,
@ -475,8 +467,9 @@ public:
} }
else if (const auto * datetime64_type = assert_cast<const DataTypeDateTime64 *>(from_type)) else if (const auto * datetime64_type = assert_cast<const DataTypeDateTime64 *>(from_type))
{ {
return DateTimeAddIntervalImpl<DataTypeDateTime64, TransformResultDataType<DataTypeDateTime64>, Transform>::execute( using WrappedTransformType = TransformType<typename DataTypeDateTime64::FieldType>;
Transform{datetime64_type->getScale()}, arguments, result_type); return DateTimeAddIntervalImpl<DataTypeDateTime64, TransformResultDataType<DataTypeDateTime64>, WrappedTransformType>::execute(
WrappedTransformType{datetime64_type->getScale()}, arguments, result_type);
} }
else else
throw Exception("Illegal type " + arguments[0].type->getName() + " of first argument of function " + getName(), throw Exception("Illegal type " + arguments[0].type->getName() + " of first argument of function " + getName(),

View File

@ -5,6 +5,7 @@
#include <DataTypes/DataTypeDateTime64.h> #include <DataTypes/DataTypeDateTime64.h>
#include <Functions/extractTimeZoneFromFunctionArguments.h> #include <Functions/extractTimeZoneFromFunctionArguments.h>
#include <Functions/DateTimeTransforms.h> #include <Functions/DateTimeTransforms.h>
#include <Functions/TransformDateTime64.h>
#include <IO/WriteHelpers.h> #include <IO/WriteHelpers.h>
@ -107,6 +108,7 @@ public:
else if (which.isDateTime64()) else if (which.isDateTime64())
{ {
const auto scale = static_cast<const DataTypeDateTime64 *>(from_type)->getScale(); const auto scale = static_cast<const DataTypeDateTime64 *>(from_type)->getScale();
const TransformDateTime64<Transform> transformer(scale); const TransformDateTime64<Transform> transformer(scale);
return DateTimeTransformImpl<DataTypeDateTime64, ToDataType, decltype(transformer)>::execute(arguments, result_type, input_rows_count, transformer); return DateTimeTransformImpl<DataTypeDateTime64, ToDataType, decltype(transformer)>::execute(arguments, result_type, input_rows_count, transformer);
} }
@ -133,7 +135,6 @@ public:
/// This method is called only if the function has one argument. Therefore, we do not care about the non-local time zone. /// This method is called only if the function has one argument. Therefore, we do not care about the non-local time zone.
const DateLUTImpl & date_lut = DateLUT::instance(); const DateLUTImpl & date_lut = DateLUT::instance();
if (left.isNull() || right.isNull()) if (left.isNull() || right.isNull())
return is_not_monotonic; return is_not_monotonic;

View File

@ -44,6 +44,7 @@
#include <Functions/FunctionHelpers.h> #include <Functions/FunctionHelpers.h>
#include <Functions/DateTimeTransforms.h> #include <Functions/DateTimeTransforms.h>
#include <Functions/toFixedString.h> #include <Functions/toFixedString.h>
#include <Functions/TransformDateTime64.h>
#include <DataTypes/DataTypeLowCardinality.h> #include <DataTypes/DataTypeLowCardinality.h>
#include <Columns/ColumnLowCardinality.h> #include <Columns/ColumnLowCardinality.h>
#include <Interpreters/Context.h> #include <Interpreters/Context.h>
@ -310,10 +311,15 @@ struct ToDateTimeImpl
return time_zone.fromDayNum(DayNum(d)); return time_zone.fromDayNum(DayNum(d));
} }
// no-op conversion from DateTime to DateTime, used in DateTime64 to DateTime conversion. static inline UInt32 execute(UInt32 dt, const DateLUTImpl & /*time_zone*/)
static inline UInt32 execute(UInt32 d, const DateLUTImpl & /*time_zone*/)
{ {
return d; return dt;
}
// TODO: return UInt32 ???
static inline Int64 execute(Int64 dt64, const DateLUTImpl & /*time_zone*/)
{
return dt64;
} }
}; };
@ -329,6 +335,7 @@ struct ToDateTransform32Or64
static inline NO_SANITIZE_UNDEFINED ToType execute(const FromType & from, const DateLUTImpl & time_zone) static inline NO_SANITIZE_UNDEFINED ToType execute(const FromType & from, const DateLUTImpl & time_zone)
{ {
// since converting to Date, no need in values outside of default LUT range.
return (from < 0xFFFF) return (from < 0xFFFF)
? from ? from
: time_zone.toDayNum(std::min(time_t(from), time_t(0xFFFFFFFF))); : time_zone.toDayNum(std::min(time_t(from), time_t(0xFFFFFFFF)));
@ -342,6 +349,7 @@ struct ToDateTransform32Or64Signed
static inline NO_SANITIZE_UNDEFINED ToType execute(const FromType & from, const DateLUTImpl & time_zone) static inline NO_SANITIZE_UNDEFINED ToType execute(const FromType & from, const DateLUTImpl & time_zone)
{ {
// TODO: decide narrow or extended range based on FromType
/// The function should be monotonic (better for query optimizations), so we saturate instead of overflow. /// The function should be monotonic (better for query optimizations), so we saturate instead of overflow.
if (from < 0) if (from < 0)
return 0; return 0;
@ -447,35 +455,8 @@ template <typename Name> struct ConvertImpl<DataTypeFloat32, DataTypeDateTime, N
template <typename Name> struct ConvertImpl<DataTypeFloat64, DataTypeDateTime, Name> template <typename Name> struct ConvertImpl<DataTypeFloat64, DataTypeDateTime, Name>
: DateTimeTransformImpl<DataTypeFloat64, DataTypeDateTime, ToDateTimeTransform64Signed<Float64, UInt32>> {}; : DateTimeTransformImpl<DataTypeFloat64, DataTypeDateTime, ToDateTimeTransform64Signed<Float64, UInt32>> {};
const time_t LUT_MIN_TIME = -1420070400l; // 1925-01-01 UTC
/** Conversion of Date or DateTime to DateTime64: add zero sub-second part. const time_t LUT_MAX_TIME = 9877248000l; // 2282-12-31 UTC
*/
struct ToDateTime64Transform
{
static constexpr auto name = "toDateTime64";
const DateTime64::NativeType scale_multiplier = 1;
ToDateTime64Transform(UInt32 scale = 0)
: scale_multiplier(DecimalUtils::scaleMultiplier<DateTime64::NativeType>(scale))
{}
inline DateTime64::NativeType execute(UInt16 d, const DateLUTImpl & time_zone) const
{
const auto dt = ToDateTimeImpl::execute(d, time_zone);
return execute(dt, time_zone);
}
inline DateTime64::NativeType execute(UInt32 dt, const DateLUTImpl & /*time_zone*/) const
{
return DecimalUtils::decimalFromComponentsWithMultiplier<DateTime64>(dt, 0, scale_multiplier);
}
};
template <typename Name> struct ConvertImpl<DataTypeDate, DataTypeDateTime64, Name, ConvertDefaultBehaviorTag>
: DateTimeTransformImpl<DataTypeDate, DataTypeDateTime64, ToDateTime64Transform> {};
template <typename Name> struct ConvertImpl<DataTypeDateTime, DataTypeDateTime64, Name, ConvertDefaultBehaviorTag>
: DateTimeTransformImpl<DataTypeDateTime, DataTypeDateTime64, ToDateTime64Transform> {};
/** Conversion of numeric to DateTime64 /** Conversion of numeric to DateTime64
*/ */
@ -493,7 +474,7 @@ struct ToDateTime64TransformUnsigned
inline NO_SANITIZE_UNDEFINED DateTime64::NativeType execute(FromType from, const DateLUTImpl &) const inline NO_SANITIZE_UNDEFINED DateTime64::NativeType execute(FromType from, const DateLUTImpl &) const
{ {
from = std::min(time_t(from), time_t(0xFFFFFFFF)); from = std::min<time_t>(from, LUT_MAX_TIME);
return DecimalUtils::decimalFromComponentsWithMultiplier<DateTime64>(from, 0, scale_multiplier); return DecimalUtils::decimalFromComponentsWithMultiplier<DateTime64>(from, 0, scale_multiplier);
} }
}; };
@ -510,9 +491,8 @@ struct ToDateTime64TransformSigned
inline NO_SANITIZE_UNDEFINED DateTime64::NativeType execute(FromType from, const DateLUTImpl &) const inline NO_SANITIZE_UNDEFINED DateTime64::NativeType execute(FromType from, const DateLUTImpl &) const
{ {
if (from < 0) from = std::max<time_t>(from, LUT_MIN_TIME);
return 0; from = std::min<time_t>(from, LUT_MAX_TIME);
from = std::min(time_t(from), time_t(0xFFFFFFFF));
return DecimalUtils::decimalFromComponentsWithMultiplier<DateTime64>(from, 0, scale_multiplier); return DecimalUtils::decimalFromComponentsWithMultiplier<DateTime64>(from, 0, scale_multiplier);
} }
}; };
@ -551,6 +531,7 @@ template <typename Name> struct ConvertImpl<DataTypeFloat32, DataTypeDateTime64,
template <typename Name> struct ConvertImpl<DataTypeFloat64, DataTypeDateTime64, Name> template <typename Name> struct ConvertImpl<DataTypeFloat64, DataTypeDateTime64, Name>
: DateTimeTransformImpl<DataTypeFloat64, DataTypeDateTime64, ToDateTime64TransformFloat<DataTypeFloat64, Float64>> {}; : DateTimeTransformImpl<DataTypeFloat64, DataTypeDateTime64, ToDateTime64TransformFloat<DataTypeFloat64, Float64>> {};
/** Conversion of DateTime64 to Date or DateTime: discards fractional part. /** Conversion of DateTime64 to Date or DateTime: discards fractional part.
*/ */
template <typename Transform> template <typename Transform>
@ -571,10 +552,41 @@ struct FromDateTime64Transform
} }
}; };
/** Conversion of DateTime64 to Date or DateTime: discards fractional part.
*/
template <typename Name> struct ConvertImpl<DataTypeDateTime64, DataTypeDate, Name, ConvertDefaultBehaviorTag> template <typename Name> struct ConvertImpl<DataTypeDateTime64, DataTypeDate, Name, ConvertDefaultBehaviorTag>
: DateTimeTransformImpl<DataTypeDateTime64, DataTypeDate, FromDateTime64Transform<ToDateImpl>> {}; : DateTimeTransformImpl<DataTypeDateTime64, DataTypeDate, TransformDateTime64<ToDateImpl>> {};
template <typename Name> struct ConvertImpl<DataTypeDateTime64, DataTypeDateTime, Name, ConvertDefaultBehaviorTag> template <typename Name> struct ConvertImpl<DataTypeDateTime64, DataTypeDateTime, Name, ConvertDefaultBehaviorTag>
: DateTimeTransformImpl<DataTypeDateTime64, DataTypeDateTime, FromDateTime64Transform<ToDateTimeImpl>> {}; : DateTimeTransformImpl<DataTypeDateTime64, DataTypeDateTime, TransformDateTime64<ToDateTimeImpl>> {};
struct ToDateTime64Transform
{
static constexpr auto name = "toDateTime64";
const DateTime64::NativeType scale_multiplier = 1;
ToDateTime64Transform(UInt32 scale = 0)
: scale_multiplier(DecimalUtils::scaleMultiplier<DateTime64::NativeType>(scale))
{}
inline DateTime64::NativeType execute(UInt16 d, const DateLUTImpl & time_zone) const
{
const auto dt = ToDateTimeImpl::execute(d, time_zone);
return execute(dt, time_zone);
}
inline DateTime64::NativeType execute(UInt32 dt, const DateLUTImpl & /*time_zone*/) const
{
return DecimalUtils::decimalFromComponentsWithMultiplier<DateTime64>(dt, 0, scale_multiplier);
}
};
/** Conversion of Date or DateTime to DateTime64: add zero sub-second part.
*/
template <typename Name> struct ConvertImpl<DataTypeDate, DataTypeDateTime64, Name, ConvertDefaultBehaviorTag>
: DateTimeTransformImpl<DataTypeDate, DataTypeDateTime64, ToDateTime64Transform> {};
template <typename Name> struct ConvertImpl<DataTypeDateTime, DataTypeDateTime64, Name, ConvertDefaultBehaviorTag>
: DateTimeTransformImpl<DataTypeDateTime, DataTypeDateTime64, ToDateTime64Transform> {};
/** Transformation of numbers, dates, datetimes to strings: through formatting. /** Transformation of numbers, dates, datetimes to strings: through formatting.
@ -658,7 +670,6 @@ struct ConvertImpl<FromDataType, std::enable_if_t<!std::is_same_v<FromDataType,
const auto & type = static_cast<const FromDataType &>(*col_with_type_and_name.type); const auto & type = static_cast<const FromDataType &>(*col_with_type_and_name.type);
const DateLUTImpl * time_zone = nullptr; const DateLUTImpl * time_zone = nullptr;
/// For argument of DateTime type, second argument with time zone could be specified. /// For argument of DateTime type, second argument with time zone could be specified.
if constexpr (std::is_same_v<FromDataType, DataTypeDateTime> || std::is_same_v<FromDataType, DataTypeDateTime64>) if constexpr (std::is_same_v<FromDataType, DataTypeDateTime> || std::is_same_v<FromDataType, DataTypeDateTime64>)
time_zone = &extractTimeZoneFromFunctionArguments(arguments, 1, 0); time_zone = &extractTimeZoneFromFunctionArguments(arguments, 1, 0);
@ -754,14 +765,18 @@ inline void parseImpl<DataTypeDate>(DataTypeDate::FieldType & x, ReadBuffer & rb
x = tmp; x = tmp;
} }
// NOTE: no need of extra overload of DateTime64, since readDateTimeText64 has different signature and that case is explicitly handled in the calling code.
template <> template <>
inline void parseImpl<DataTypeDateTime>(DataTypeDateTime::FieldType & x, ReadBuffer & rb, const DateLUTImpl * time_zone) inline void parseImpl<DataTypeDateTime>(DataTypeDateTime::FieldType & x, ReadBuffer & rb, const DateLUTImpl * time_zone)
{ {
time_t tmp = 0; time_t time = 0;
readDateTimeText(tmp, rb, *time_zone); readDateTimeText(time, rb, *time_zone);
x = tmp; if (time < 0)
time = 0;
x = time;
} }
template <> template <>
inline void parseImpl<DataTypeUUID>(DataTypeUUID::FieldType & x, ReadBuffer & rb, const DateLUTImpl *) inline void parseImpl<DataTypeUUID>(DataTypeUUID::FieldType & x, ReadBuffer & rb, const DateLUTImpl *)
{ {
@ -989,9 +1004,18 @@ struct ConvertThroughParsing
} }
else if constexpr (parsing_mode == ConvertFromStringParsingMode::BestEffortUS) else if constexpr (parsing_mode == ConvertFromStringParsingMode::BestEffortUS)
{ {
time_t res; if constexpr (to_datetime64)
parseDateTimeBestEffortUS(res, read_buffer, *local_time_zone, *utc_time_zone); {
vec_to[i] = res; DateTime64 res = 0;
parseDateTime64BestEffortUS(res, vec_to.getScale(), read_buffer, *local_time_zone, *utc_time_zone);
vec_to[i] = res;
}
else
{
time_t res;
parseDateTimeBestEffortUS(res, read_buffer, *local_time_zone, *utc_time_zone);
vec_to[i] = res;
}
} }
else else
{ {

View File

@ -0,0 +1,92 @@
#pragma once
#include <Core/Types.h>
#include <Core/DecimalFunctions.h>
namespace DB
{
/** Tansform-type wrapper for DateTime64, simplifies DateTime64 support for given Transform.
*
* Depending on what overloads of Transform::execute() are available, when called with DateTime64 value,
* invokes Transform::execute() with either:
* * whole part of DateTime64 value, discarding fractional part (1)
* * DateTime64 value and scale factor (2)
* * DateTime64 broken down to components, result of execute is then re-assembled back into DateTime64 value (3)
*
* Suitable Transfotm-types are commonly used in Date/DateTime manipulation functions,
* and should implement static (or const) function with following signatures:
* 1:
* R execute(Int64 whole_value, ... )
* 2:
* R execute(DateTime64 value, Int64 scale_multiplier, ... )
* 3:
* R execute(DecimalUtils::DecimalComponents<DateTime64> components, ... )
*
* Where R could be of arbitrary type, in case of (3) if R is DecimalUtils::DecimalComponents<DateTime64>, result is re-assembed back into DateTime64.
*/
template <typename Transform>
class TransformDateTime64
{
private:
// Detect if Transform::execute is const or static method
// with signature defined by template args (ignoring result type).
template<typename = void, typename... Args>
struct TransformHasExecuteOverload : std::false_type {};
template<typename... Args>
struct TransformHasExecuteOverload<std::void_t<decltype(std::declval<Transform>().execute(std::declval<Args>()...))>, Args...>
: std::true_type {};
template<typename... Args>
static constexpr bool TransformHasExecuteOverload_v = TransformHasExecuteOverload<void, Args...>::value;
public:
static constexpr auto name = Transform::name;
// non-explicit constructor to allow creating from scale value (or with no scale at all), indispensable in some contexts.
TransformDateTime64(UInt32 scale_ = 0)
: scale_multiplier(DecimalUtils::scaleMultiplier<DateTime64::NativeType>(scale_))
{}
template <typename ... Args>
inline auto execute(const DateTime64 & t, Args && ... args) const
{
if constexpr (TransformHasExecuteOverload_v<DateTime64, decltype(scale_multiplier), Args...>)
{
return wrapped_transform.execute(t, scale_multiplier, std::forward<Args>(args)...);
}
else if constexpr (TransformHasExecuteOverload_v<DecimalUtils::DecimalComponents<DateTime64>, Args...>)
{
auto components = DecimalUtils::splitWithScaleMultiplier(t, scale_multiplier);
const auto result = wrapped_transform.execute(components, std::forward<Args>(args)...);
using ResultType = std::decay_t<decltype(result)>;
if constexpr (std::is_same_v<DecimalUtils::DecimalComponents<DateTime64>, ResultType>)
{
return DecimalUtils::decimalFromComponentsWithMultiplier<DateTime64>(result, scale_multiplier);
}
else
{
return result;
}
}
else
{
const auto components = DecimalUtils::splitWithScaleMultiplier(t, scale_multiplier);
return wrapped_transform.execute(static_cast<Int64>(components.whole), std::forward<Args>(args)...);
}
}
template <typename T, typename ... Args, typename = std::enable_if_t<!std::is_same_v<T, DateTime64>>>
inline auto execute(const T & t, Args && ... args) const
{
return wrapped_transform.execute(t, std::forward<Args>(args)...);
}
private:
DateTime64::NativeType scale_multiplier = 1;
Transform wrapped_transform = {};
};
}

View File

@ -97,8 +97,8 @@ public:
size_t rows = input_rows_count; size_t rows = input_rows_count;
auto res = ColumnInt64::create(rows); auto res = ColumnInt64::create(rows);
const DateLUTImpl & timezone_x = extractTimeZoneFromFunctionArguments(arguments, 3, 1); const auto & timezone_x = extractTimeZoneFromFunctionArguments(arguments, 3, 1);
const DateLUTImpl & timezone_y = extractTimeZoneFromFunctionArguments(arguments, 3, 2); const auto & timezone_y = extractTimeZoneFromFunctionArguments(arguments, 3, 2);
if (unit == "year" || unit == "yy" || unit == "yyyy") if (unit == "year" || unit == "yy" || unit == "yyyy")
dispatchForColumns<ToRelativeYearNumImpl>(x, y, timezone_x, timezone_y, res->getData()); dispatchForColumns<ToRelativeYearNumImpl>(x, y, timezone_x, timezone_y, res->getData());

View File

@ -66,10 +66,11 @@ const DateLUTImpl & extractTimeZoneFromFunctionArguments(const ColumnsWithTypeAn
if (arguments.empty()) if (arguments.empty())
return DateLUT::instance(); return DateLUT::instance();
const auto & dt_arg = arguments[datetime_arg_num].type.get();
/// If time zone is attached to an argument of type DateTime. /// If time zone is attached to an argument of type DateTime.
if (const auto * type = checkAndGetDataType<DataTypeDateTime>(arguments[datetime_arg_num].type.get())) if (const auto * type = checkAndGetDataType<DataTypeDateTime>(dt_arg))
return type->getTimeZone(); return type->getTimeZone();
if (const auto * type = checkAndGetDataType<DataTypeDateTime64>(arguments[datetime_arg_num].type.get())) if (const auto * type = checkAndGetDataType<DataTypeDateTime64>(dt_arg))
return type->getTimeZone(); return type->getTimeZone();
return DateLUT::instance(); return DateLUT::instance();

View File

@ -46,9 +46,8 @@ template <> struct ActionValueTypeMap<DataTypeInt64> { using ActionValueTyp
template <> struct ActionValueTypeMap<DataTypeUInt64> { using ActionValueType = UInt32; }; template <> struct ActionValueTypeMap<DataTypeUInt64> { using ActionValueType = UInt32; };
template <> struct ActionValueTypeMap<DataTypeDate> { using ActionValueType = UInt16; }; template <> struct ActionValueTypeMap<DataTypeDate> { using ActionValueType = UInt16; };
template <> struct ActionValueTypeMap<DataTypeDateTime> { using ActionValueType = UInt32; }; template <> struct ActionValueTypeMap<DataTypeDateTime> { using ActionValueType = UInt32; };
// TODO(vnemkov): once there is support for Int64 in LUT, make that Int64.
// TODO(vnemkov): to add sub-second format instruction, make that DateTime64 and do some math in Action<T>. // TODO(vnemkov): to add sub-second format instruction, make that DateTime64 and do some math in Action<T>.
template <> struct ActionValueTypeMap<DataTypeDateTime64> { using ActionValueType = UInt32; }; template <> struct ActionValueTypeMap<DataTypeDateTime64> { using ActionValueType = Int64; };
/** formatDateTime(time, 'pattern') /** formatDateTime(time, 'pattern')
@ -434,7 +433,6 @@ public:
time_zone_tmp = &DateLUT::instance(); time_zone_tmp = &DateLUT::instance();
const DateLUTImpl & time_zone = *time_zone_tmp; const DateLUTImpl & time_zone = *time_zone_tmp;
const auto & vec = times->getData(); const auto & vec = times->getData();
UInt32 scale [[maybe_unused]] = 0; UInt32 scale [[maybe_unused]] = 0;
@ -519,6 +517,8 @@ public:
{ {
if constexpr (std::is_same_v<T, UInt32>) if constexpr (std::is_same_v<T, UInt32>)
instructions.emplace_back(func, shift); instructions.emplace_back(func, shift);
else if constexpr (std::is_same_v<T, Int64>)
instructions.emplace_back(func, shift);
else else
add_shift(shift); add_shift(shift);
}; };

View File

@ -30,7 +30,7 @@ Field nowSubsecond(UInt32 scale)
if (clock_gettime(CLOCK_REALTIME, &spec)) if (clock_gettime(CLOCK_REALTIME, &spec))
throwFromErrno("Cannot clock_gettime.", ErrorCodes::CANNOT_CLOCK_GETTIME); throwFromErrno("Cannot clock_gettime.", ErrorCodes::CANNOT_CLOCK_GETTIME);
DecimalUtils::DecimalComponents<DateTime64::NativeType> components{spec.tv_sec, spec.tv_nsec}; DecimalUtils::DecimalComponents<DateTime64> components{spec.tv_sec, spec.tv_nsec};
// clock_gettime produces subsecond part in nanoseconds, but decimalFromComponents fractional is scale-dependent. // clock_gettime produces subsecond part in nanoseconds, but decimalFromComponents fractional is scale-dependent.
// Andjust fractional to scale, e.g. for 123456789 nanoseconds: // Andjust fractional to scale, e.g. for 123456789 nanoseconds:

View File

@ -7,6 +7,7 @@
#include <Functions/DateTimeTransforms.h> #include <Functions/DateTimeTransforms.h>
#include <Functions/FunctionFactory.h> #include <Functions/FunctionFactory.h>
#include <Functions/IFunctionImpl.h> #include <Functions/IFunctionImpl.h>
#include <Functions/TransformDateTime64.h>
#include <IO/WriteHelpers.h> #include <IO/WriteHelpers.h>
@ -35,13 +36,18 @@ namespace
static UInt16 execute(UInt16 d, UInt64 years, const DateLUTImpl & time_zone) static UInt16 execute(UInt16 d, UInt64 years, const DateLUTImpl & time_zone)
{ {
return time_zone.toStartOfYearInterval(DayNum(d), years); return time_zone.toStartOfYearInterval(ExtendedDayNum(d), years);
} }
static UInt16 execute(UInt32 t, UInt64 years, const DateLUTImpl & time_zone) static UInt16 execute(UInt32 t, UInt64 years, const DateLUTImpl & time_zone)
{ {
return time_zone.toStartOfYearInterval(time_zone.toDayNum(t), years); return time_zone.toStartOfYearInterval(time_zone.toDayNum(t), years);
} }
static UInt16 execute(Int64 t, UInt64 years, const DateLUTImpl & time_zone)
{
return time_zone.toStartOfYearInterval(time_zone.toDayNum(t), years);
}
}; };
template <> template <>
@ -51,13 +57,18 @@ namespace
static UInt16 execute(UInt16 d, UInt64 quarters, const DateLUTImpl & time_zone) static UInt16 execute(UInt16 d, UInt64 quarters, const DateLUTImpl & time_zone)
{ {
return time_zone.toStartOfQuarterInterval(DayNum(d), quarters); return time_zone.toStartOfQuarterInterval(ExtendedDayNum(d), quarters);
} }
static UInt16 execute(UInt32 t, UInt64 quarters, const DateLUTImpl & time_zone) static UInt16 execute(UInt32 t, UInt64 quarters, const DateLUTImpl & time_zone)
{ {
return time_zone.toStartOfQuarterInterval(time_zone.toDayNum(t), quarters); return time_zone.toStartOfQuarterInterval(time_zone.toDayNum(t), quarters);
} }
static UInt16 execute(Int64 t, UInt64 quarters, const DateLUTImpl & time_zone)
{
return time_zone.toStartOfQuarterInterval(time_zone.toDayNum(t), quarters);
}
}; };
template <> template <>
@ -67,13 +78,18 @@ namespace
static UInt16 execute(UInt16 d, UInt64 months, const DateLUTImpl & time_zone) static UInt16 execute(UInt16 d, UInt64 months, const DateLUTImpl & time_zone)
{ {
return time_zone.toStartOfMonthInterval(DayNum(d), months); return time_zone.toStartOfMonthInterval(ExtendedDayNum(d), months);
} }
static UInt16 execute(UInt32 t, UInt64 months, const DateLUTImpl & time_zone) static UInt16 execute(UInt32 t, UInt64 months, const DateLUTImpl & time_zone)
{ {
return time_zone.toStartOfMonthInterval(time_zone.toDayNum(t), months); return time_zone.toStartOfMonthInterval(time_zone.toDayNum(t), months);
} }
static UInt16 execute(Int64 t, UInt64 months, const DateLUTImpl & time_zone)
{
return time_zone.toStartOfMonthInterval(time_zone.toDayNum(t), months);
}
}; };
template <> template <>
@ -83,13 +99,18 @@ namespace
static UInt16 execute(UInt16 d, UInt64 weeks, const DateLUTImpl & time_zone) static UInt16 execute(UInt16 d, UInt64 weeks, const DateLUTImpl & time_zone)
{ {
return time_zone.toStartOfWeekInterval(DayNum(d), weeks); return time_zone.toStartOfWeekInterval(ExtendedDayNum(d), weeks);
} }
static UInt16 execute(UInt32 t, UInt64 weeks, const DateLUTImpl & time_zone) static UInt16 execute(UInt32 t, UInt64 weeks, const DateLUTImpl & time_zone)
{ {
return time_zone.toStartOfWeekInterval(time_zone.toDayNum(t), weeks); return time_zone.toStartOfWeekInterval(time_zone.toDayNum(t), weeks);
} }
static UInt16 execute(Int64 t, UInt64 weeks, const DateLUTImpl & time_zone)
{
return time_zone.toStartOfWeekInterval(time_zone.toDayNum(t), weeks);
}
}; };
template <> template <>
@ -99,13 +120,18 @@ namespace
static UInt32 execute(UInt16 d, UInt64 days, const DateLUTImpl & time_zone) static UInt32 execute(UInt16 d, UInt64 days, const DateLUTImpl & time_zone)
{ {
return time_zone.toStartOfDayInterval(DayNum(d), days); return time_zone.toStartOfDayInterval(ExtendedDayNum(d), days);
} }
static UInt32 execute(UInt32 t, UInt64 days, const DateLUTImpl & time_zone) static UInt32 execute(UInt32 t, UInt64 days, const DateLUTImpl & time_zone)
{ {
return time_zone.toStartOfDayInterval(time_zone.toDayNum(t), days); return time_zone.toStartOfDayInterval(time_zone.toDayNum(t), days);
} }
static UInt32 execute(Int64 t, UInt64 days, const DateLUTImpl & time_zone)
{
return time_zone.toStartOfDayInterval(time_zone.toDayNum(t), days);
}
}; };
template <> template <>
@ -114,8 +140,8 @@ namespace
static constexpr auto name = function_name; static constexpr auto name = function_name;
static UInt32 execute(UInt16, UInt64, const DateLUTImpl &) { return dateIsNotSupported(function_name); } static UInt32 execute(UInt16, UInt64, const DateLUTImpl &) { return dateIsNotSupported(function_name); }
static UInt32 execute(UInt32 t, UInt64 hours, const DateLUTImpl & time_zone) { return time_zone.toStartOfHourInterval(t, hours); } static UInt32 execute(UInt32 t, UInt64 hours, const DateLUTImpl & time_zone) { return time_zone.toStartOfHourInterval(t, hours); }
static UInt32 execute(Int64 t, UInt64 hours, const DateLUTImpl & time_zone) { return time_zone.toStartOfHourInterval(t, hours); }
}; };
template <> template <>
@ -129,6 +155,11 @@ namespace
{ {
return time_zone.toStartOfMinuteInterval(t, minutes); return time_zone.toStartOfMinuteInterval(t, minutes);
} }
static UInt32 execute(Int64 t, UInt64 minutes, const DateLUTImpl & time_zone)
{
return time_zone.toStartOfMinuteInterval(t, minutes);
}
}; };
template <> template <>
@ -142,6 +173,11 @@ namespace
{ {
return time_zone.toStartOfSecondInterval(t, seconds); return time_zone.toStartOfSecondInterval(t, seconds);
} }
static Int64 execute(Int64 t, UInt64 seconds, const DateLUTImpl & time_zone)
{
return time_zone.toStartOfSecondInterval(t, seconds);
}
}; };
@ -230,7 +266,7 @@ public:
{ {
const auto & time_column = arguments[0]; const auto & time_column = arguments[0];
const auto & interval_column = arguments[1]; const auto & interval_column = arguments[1];
const DateLUTImpl & time_zone = extractTimeZoneFromFunctionArguments(arguments, 2, 0); const auto & time_zone = extractTimeZoneFromFunctionArguments(arguments, 2, 0);
auto result_column = dispatchForColumns(time_column, interval_column, time_zone); auto result_column = dispatchForColumns(time_column, interval_column, time_zone);
return result_column; return result_column;
} }

View File

@ -77,7 +77,7 @@ public:
FunctionBaseImplPtr build(const ColumnsWithTypeAndName &, const DataTypePtr &) const override FunctionBaseImplPtr build(const ColumnsWithTypeAndName &, const DataTypePtr &) const override
{ {
return std::make_unique<FunctionBaseToday>(DateLUT::instance().toDayNum(time(nullptr))); return std::make_unique<FunctionBaseToday>(DayNum(DateLUT::instance().toDayNum(time(nullptr)).toUnderType()));
} }
}; };

View File

@ -64,12 +64,12 @@ struct Progress
std::atomic<size_t> written_rows {0}; std::atomic<size_t> written_rows {0};
std::atomic<size_t> written_bytes {0}; std::atomic<size_t> written_bytes {0};
Progress() {} Progress() = default;
Progress(size_t read_rows_, size_t read_bytes_, size_t total_rows_to_read_ = 0) Progress(size_t read_rows_, size_t read_bytes_, size_t total_rows_to_read_ = 0)
: read_rows(read_rows_), read_bytes(read_bytes_), total_rows_to_read(total_rows_to_read_) {} : read_rows(read_rows_), read_bytes(read_bytes_), total_rows_to_read(total_rows_to_read_) {}
Progress(ReadProgress read_progress) explicit Progress(ReadProgress read_progress)
: read_rows(read_progress.read_rows), read_bytes(read_progress.read_bytes), total_rows_to_read(read_progress.total_rows_to_read) {} : read_rows(read_progress.read_rows), read_bytes(read_progress.read_bytes), total_rows_to_read(read_progress.total_rows_to_read) {}
Progress(WriteProgress write_progress) explicit Progress(WriteProgress write_progress)
: written_rows(write_progress.written_rows), written_bytes(write_progress.written_bytes) {} : written_rows(write_progress.written_rows), written_bytes(write_progress.written_bytes) {}
void read(ReadBuffer & in, UInt64 server_revision); void read(ReadBuffer & in, UInt64 server_revision);
@ -86,7 +86,7 @@ struct Progress
written_rows += rhs.written_rows; written_rows += rhs.written_rows;
written_bytes += rhs.written_bytes; written_bytes += rhs.written_bytes;
return rhs.read_rows || rhs.written_rows ? true : false; return rhs.read_rows || rhs.written_rows;
} }
void reset() void reset()

View File

@ -747,7 +747,7 @@ inline ReturnType readDateTimeTextImpl(DateTime64 & datetime64, UInt32 scale, Re
return ReturnType(false); return ReturnType(false);
} }
DB::DecimalUtils::DecimalComponents<DateTime64::NativeType> components{static_cast<DateTime64::NativeType>(whole), 0}; DB::DecimalUtils::DecimalComponents<DateTime64> components{static_cast<DateTime64::NativeType>(whole), 0};
if (!buf.eof() && *buf.position() == '.') if (!buf.eof() && *buf.position() == '.')
{ {
@ -791,9 +791,9 @@ inline ReturnType readDateTimeTextImpl(DateTime64 & datetime64, UInt32 scale, Re
return ReturnType(true); return ReturnType(true);
} }
inline void readDateTimeText(time_t & datetime, ReadBuffer & buf, const DateLUTImpl & date_lut = DateLUT::instance()) inline void readDateTimeText(time_t & datetime, ReadBuffer & buf, const DateLUTImpl & time_zone = DateLUT::instance())
{ {
readDateTimeTextImpl<void>(datetime, buf, date_lut); readDateTimeTextImpl<void>(datetime, buf, time_zone);
} }
inline void readDateTime64Text(DateTime64 & datetime64, UInt32 scale, ReadBuffer & buf, const DateLUTImpl & date_lut = DateLUT::instance()) inline void readDateTime64Text(DateTime64 & datetime64, UInt32 scale, ReadBuffer & buf, const DateLUTImpl & date_lut = DateLUT::instance())
@ -801,9 +801,9 @@ inline void readDateTime64Text(DateTime64 & datetime64, UInt32 scale, ReadBuffer
readDateTimeTextImpl<void>(datetime64, scale, buf, date_lut); readDateTimeTextImpl<void>(datetime64, scale, buf, date_lut);
} }
inline bool tryReadDateTimeText(time_t & datetime, ReadBuffer & buf, const DateLUTImpl & date_lut = DateLUT::instance()) inline bool tryReadDateTimeText(time_t & datetime, ReadBuffer & buf, const DateLUTImpl & time_zone = DateLUT::instance())
{ {
return readDateTimeTextImpl<bool>(datetime, buf, date_lut); return readDateTimeTextImpl<bool>(datetime, buf, time_zone);
} }
inline bool tryReadDateTime64Text(DateTime64 & datetime64, UInt32 scale, ReadBuffer & buf, const DateLUTImpl & date_lut = DateLUT::instance()) inline bool tryReadDateTime64Text(DateTime64 & datetime64, UInt32 scale, ReadBuffer & buf, const DateLUTImpl & date_lut = DateLUT::instance())

View File

@ -86,7 +86,6 @@ PocoHTTPClient::PocoHTTPClient(const PocoHTTPClientConfiguration & clientConfigu
)) ))
, remote_host_filter(clientConfiguration.remote_host_filter) , remote_host_filter(clientConfiguration.remote_host_filter)
, s3_max_redirects(clientConfiguration.s3_max_redirects) , s3_max_redirects(clientConfiguration.s3_max_redirects)
, max_connections(clientConfiguration.maxConnections)
{ {
} }
@ -156,19 +155,24 @@ void PocoHTTPClient::makeRequestInternal(
for (unsigned int attempt = 0; attempt <= s3_max_redirects; ++attempt) for (unsigned int attempt = 0; attempt <= s3_max_redirects; ++attempt)
{ {
Poco::URI target_uri(uri); Poco::URI target_uri(uri);
Poco::URI proxy_uri;
auto request_configuration = per_request_configuration(request);
if (!request_configuration.proxyHost.empty())
{
proxy_uri.setScheme(Aws::Http::SchemeMapper::ToString(request_configuration.proxyScheme));
proxy_uri.setHost(request_configuration.proxyHost);
proxy_uri.setPort(request_configuration.proxyPort);
}
/// Reverse proxy can replace host header with resolved ip address instead of host name. /// Reverse proxy can replace host header with resolved ip address instead of host name.
/// This can lead to request signature difference on S3 side. /// This can lead to request signature difference on S3 side.
auto session = makePooledHTTPSession(target_uri, proxy_uri, timeouts, max_connections, false); auto session = makeHTTPSession(target_uri, timeouts, false);
auto request_configuration = per_request_configuration(request);
if (!request_configuration.proxyHost.empty())
{
bool use_tunnel = request_configuration.proxyScheme == Aws::Http::Scheme::HTTP && target_uri.getScheme() == "https";
session->setProxy(
request_configuration.proxyHost,
request_configuration.proxyPort,
Aws::Http::SchemeMapper::ToString(request_configuration.proxyScheme),
use_tunnel
);
}
Poco::Net::HTTPRequest poco_request(Poco::Net::HTTPRequest::HTTP_1_1); Poco::Net::HTTPRequest poco_request(Poco::Net::HTTPRequest::HTTP_1_1);

View File

@ -40,7 +40,7 @@ private:
class PocoHTTPResponse : public Aws::Http::Standard::StandardHttpResponse class PocoHTTPResponse : public Aws::Http::Standard::StandardHttpResponse
{ {
public: public:
using SessionPtr = PooledHTTPSessionPtr; using SessionPtr = HTTPSessionPtr;
PocoHTTPResponse(const std::shared_ptr<const Aws::Http::HttpRequest> request) PocoHTTPResponse(const std::shared_ptr<const Aws::Http::HttpRequest> request)
: Aws::Http::Standard::StandardHttpResponse(request) : Aws::Http::Standard::StandardHttpResponse(request)
@ -91,7 +91,6 @@ private:
ConnectionTimeouts timeouts; ConnectionTimeouts timeouts;
const RemoteHostFilter & remote_host_filter; const RemoteHostFilter & remote_host_filter;
unsigned int s3_max_redirects; unsigned int s3_max_redirects;
unsigned int max_connections;
}; };
} }

View File

@ -107,7 +107,14 @@ void WriteBufferFromS3::finalizeImpl()
WriteBufferFromS3::~WriteBufferFromS3() WriteBufferFromS3::~WriteBufferFromS3()
{ {
finalizeImpl(); try
{
finalizeImpl();
}
catch (...)
{
tryLogCurrentException(__PRETTY_FUNCTION__);
}
} }
void WriteBufferFromS3::createMultipartUpload() void WriteBufferFromS3::createMultipartUpload()

View File

@ -819,26 +819,20 @@ inline void writeDateTimeText(const LocalDateTime & datetime, WriteBuffer & buf)
/// In the format YYYY-MM-DD HH:MM:SS, according to the specified time zone. /// In the format YYYY-MM-DD HH:MM:SS, according to the specified time zone.
template <char date_delimeter = '-', char time_delimeter = ':', char between_date_time_delimiter = ' '> template <char date_delimeter = '-', char time_delimeter = ':', char between_date_time_delimiter = ' '>
inline void writeDateTimeText(time_t datetime, WriteBuffer & buf, const DateLUTImpl & date_lut = DateLUT::instance()) inline void writeDateTimeText(time_t datetime, WriteBuffer & buf, const DateLUTImpl & time_zone = DateLUT::instance())
{ {
const auto & values = date_lut.getValues(datetime); writeDateTimeText<date_delimeter, time_delimeter, between_date_time_delimiter>(LocalDateTime(datetime, time_zone), buf);
writeDateTimeText<date_delimeter, time_delimeter, between_date_time_delimiter>(
LocalDateTime(values.year, values.month, values.day_of_month,
date_lut.toHour(datetime), date_lut.toMinute(datetime), date_lut.toSecond(datetime)), buf);
} }
/// In the format YYYY-MM-DD HH:MM:SS.NNNNNNNNN, according to the specified time zone. /// In the format YYYY-MM-DD HH:MM:SS.NNNNNNNNN, according to the specified time zone.
template <char date_delimeter = '-', char time_delimeter = ':', char between_date_time_delimiter = ' ', char fractional_time_delimiter = '.'> template <char date_delimeter = '-', char time_delimeter = ':', char between_date_time_delimiter = ' ', char fractional_time_delimiter = '.'>
inline void writeDateTimeText(DateTime64 datetime64, UInt32 scale, WriteBuffer & buf, const DateLUTImpl & date_lut = DateLUT::instance()) inline void writeDateTimeText(DateTime64 datetime64, UInt32 scale, WriteBuffer & buf, const DateLUTImpl & time_zone = DateLUT::instance())
{ {
static constexpr UInt32 MaxScale = DecimalUtils::max_precision<DateTime64>; static constexpr UInt32 MaxScale = DecimalUtils::max_precision<DateTime64>;
scale = scale > MaxScale ? MaxScale : scale; scale = scale > MaxScale ? MaxScale : scale;
auto components = DecimalUtils::split(datetime64, scale); auto components = DecimalUtils::split(datetime64, scale);
const auto & values = date_lut.getValues(components.whole); writeDateTimeText<date_delimeter, time_delimeter, between_date_time_delimiter>(LocalDateTime(components.whole, time_zone), buf);
writeDateTimeText<date_delimeter, time_delimeter, between_date_time_delimiter>(
LocalDateTime(values.year, values.month, values.day_of_month,
date_lut.toHour(components.whole), date_lut.toMinute(components.whole), date_lut.toSecond(components.whole)), buf);
if (scale > 0) if (scale > 0)
{ {
@ -849,9 +843,9 @@ inline void writeDateTimeText(DateTime64 datetime64, UInt32 scale, WriteBuffer &
/// In the RFC 1123 format: "Tue, 03 Dec 2019 00:11:50 GMT". You must provide GMT DateLUT. /// In the RFC 1123 format: "Tue, 03 Dec 2019 00:11:50 GMT". You must provide GMT DateLUT.
/// This is needed for HTTP requests. /// This is needed for HTTP requests.
inline void writeDateTimeTextRFC1123(time_t datetime, WriteBuffer & buf, const DateLUTImpl & date_lut) inline void writeDateTimeTextRFC1123(time_t datetime, WriteBuffer & buf, const DateLUTImpl & time_zone = DateLUT::instance())
{ {
const auto & values = date_lut.getValues(datetime); const auto & values = time_zone.getValues(datetime);
static const char week_days[3 * 8 + 1] = "XXX" "Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun"; static const char week_days[3 * 8 + 1] = "XXX" "Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun";
static const char months[3 * 13 + 1] = "XXX" "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"; static const char months[3 * 13 + 1] = "XXX" "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec";
@ -865,11 +859,11 @@ inline void writeDateTimeTextRFC1123(time_t datetime, WriteBuffer & buf, const D
buf.write(&digits100[values.year / 100 * 2], 2); buf.write(&digits100[values.year / 100 * 2], 2);
buf.write(&digits100[values.year % 100 * 2], 2); buf.write(&digits100[values.year % 100 * 2], 2);
buf.write(' '); buf.write(' ');
buf.write(&digits100[date_lut.toHour(datetime) * 2], 2); buf.write(&digits100[time_zone.toHour(datetime) * 2], 2);
buf.write(':'); buf.write(':');
buf.write(&digits100[date_lut.toMinute(datetime) * 2], 2); buf.write(&digits100[time_zone.toMinute(datetime) * 2], 2);
buf.write(':'); buf.write(':');
buf.write(&digits100[date_lut.toSecond(datetime) * 2], 2); buf.write(&digits100[time_zone.toSecond(datetime) * 2], 2);
buf.write(" GMT", 4); buf.write(" GMT", 4);
} }

Some files were not shown because too many files have changed in this diff Show More