mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-24 00:22:29 +00:00
Merge
This commit is contained in:
commit
28f67084a4
@ -6,7 +6,7 @@
|
|||||||
#include <climits>
|
#include <climits>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stats/ReservoirSampler.h>
|
#include <stats/ReservoirSampler.h>
|
||||||
#include <Yandex/Common.h>
|
#include <common/Common.h>
|
||||||
#include <DB/Common/HashTable/Hash.h>
|
#include <DB/Common/HashTable/Hash.h>
|
||||||
#include <DB/IO/ReadBuffer.h>
|
#include <DB/IO/ReadBuffer.h>
|
||||||
#include <DB/IO/ReadHelpers.h>
|
#include <DB/IO/ReadHelpers.h>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
#include <Poco/Net/StreamSocket.h>
|
#include <Poco/Net/StreamSocket.h>
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <Poco/SharedPtr.h>
|
#include <Poco/SharedPtr.h>
|
||||||
#include <Yandex/likely.h>
|
#include <common/likely.h>
|
||||||
#include <DB/Common/ProfileEvents.h>
|
#include <DB/Common/ProfileEvents.h>
|
||||||
#include <DB/Common/Allocator.h>
|
#include <DB/Common/Allocator.h>
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
#include <Yandex/likely.h>
|
#include <common/likely.h>
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include <DB/Core/Exception.h>
|
#include <DB/Core/Exception.h>
|
||||||
#include <DB/IO/WriteHelpers.h>
|
#include <DB/IO/WriteHelpers.h>
|
||||||
|
|
||||||
#include <Yandex/Common.h>
|
#include <common/Common.h>
|
||||||
|
|
||||||
#include <Poco/String.h>
|
#include <Poco/String.h>
|
||||||
|
|
||||||
|
@ -1,15 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
#include <statdaemons/JSON.h>
|
|
||||||
#include <Poco/Path.h>
|
|
||||||
#include <Poco/File.h>
|
#include <Poco/File.h>
|
||||||
#include <DB/IO/WriteBufferFromFile.h>
|
|
||||||
#include <DB/IO/WriteBufferFromString.h>
|
|
||||||
#include <DB/IO/ReadBufferFromFile.h>
|
|
||||||
#include <DB/IO/copyData.h>
|
|
||||||
#include <DB/Common/escapeForFileName.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
@ -23,146 +16,21 @@ private:
|
|||||||
using Map = std::map<std::string, size_t>;
|
using Map = std::map<std::string, size_t>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FileChecker(const std::string & file_info_path_) :
|
|
||||||
files_info_path(file_info_path_)
|
|
||||||
{
|
|
||||||
Poco::Path path(files_info_path);
|
|
||||||
tmp_files_info_path = path.parent().toString() + "tmp_" + path.getFileName();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setPath(const std::string & file_info_path_)
|
|
||||||
{
|
|
||||||
files_info_path = file_info_path_;
|
|
||||||
}
|
|
||||||
|
|
||||||
using Files = std::vector<Poco::File>;
|
using Files = std::vector<Poco::File>;
|
||||||
|
|
||||||
void update(const Poco::File & file)
|
FileChecker(const std::string & file_info_path_);
|
||||||
{
|
void setPath(const std::string & file_info_path_);
|
||||||
initialize();
|
void update(const Poco::File & file);
|
||||||
updateImpl(file);
|
void update(const Files::const_iterator & begin, const Files::const_iterator & end);
|
||||||
save();
|
|
||||||
}
|
|
||||||
|
|
||||||
void update(const Files::iterator & begin, const Files::iterator & end)
|
|
||||||
{
|
|
||||||
initialize();
|
|
||||||
for (auto it = begin; it != end; ++it)
|
|
||||||
updateImpl(*it);
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Проверяем файлы, параметры которых указаны в sizes.json
|
/// Проверяем файлы, параметры которых указаны в sizes.json
|
||||||
bool check() const
|
bool check() const;
|
||||||
{
|
|
||||||
/** Читаем файлы заново при каждом вызове check - чтобы не нарушать константность.
|
|
||||||
* Метод check вызывается редко.
|
|
||||||
*/
|
|
||||||
Map local_map;
|
|
||||||
load(local_map);
|
|
||||||
|
|
||||||
if (local_map.empty())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
for (const auto & name_size : local_map)
|
|
||||||
{
|
|
||||||
Poco::File file(Poco::Path(files_info_path).parent().toString() + "/" + name_size.first);
|
|
||||||
if (!file.exists())
|
|
||||||
{
|
|
||||||
LOG_ERROR(log, "File " << file.path() << " doesn't exist");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t real_size = file.getSize();
|
|
||||||
if (real_size != name_size.second)
|
|
||||||
{
|
|
||||||
LOG_ERROR(log, "Size of " << file.path() << " is wrong. Size is " << real_size << " but should be " << name_size.second);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initialize()
|
void initialize();
|
||||||
{
|
void updateImpl(const Poco::File & file);
|
||||||
if (initialized)
|
void save() const;
|
||||||
return;
|
void load(Map & map) const;
|
||||||
|
|
||||||
load(map);
|
|
||||||
initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateImpl(const Poco::File & file)
|
|
||||||
{
|
|
||||||
map[Poco::Path(file.path()).getFileName()] = file.getSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
void save() const
|
|
||||||
{
|
|
||||||
{
|
|
||||||
WriteBufferFromFile out(tmp_files_info_path);
|
|
||||||
|
|
||||||
/// Столь сложная структура JSON-а - для совместимости со старым форматом.
|
|
||||||
writeCString("{\"yandex\":{", out);
|
|
||||||
|
|
||||||
for (auto it = map.begin(); it != map.end(); ++it)
|
|
||||||
{
|
|
||||||
if (it != map.begin())
|
|
||||||
writeString(",", out);
|
|
||||||
|
|
||||||
/// escapeForFileName на самом деле не нужен. Но он оставлен для совместимости со старым кодом.
|
|
||||||
writeJSONString(escapeForFileName(it->first), out);
|
|
||||||
writeString(":{\"size\":\"", out);
|
|
||||||
writeIntText(it->second, out);
|
|
||||||
writeString("\"}", out);
|
|
||||||
}
|
|
||||||
|
|
||||||
writeCString("}}", out);
|
|
||||||
out.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
Poco::File current_file(files_info_path);
|
|
||||||
|
|
||||||
if (current_file.exists())
|
|
||||||
{
|
|
||||||
std::string old_file_name = files_info_path + ".old";
|
|
||||||
current_file.renameTo(old_file_name);
|
|
||||||
Poco::File(tmp_files_info_path).renameTo(files_info_path);
|
|
||||||
Poco::File(old_file_name).remove();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Poco::File(tmp_files_info_path).renameTo(files_info_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void load(Map & map) const
|
|
||||||
{
|
|
||||||
map.clear();
|
|
||||||
|
|
||||||
if (!Poco::File(files_info_path).exists())
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::string content;
|
|
||||||
{
|
|
||||||
ReadBufferFromFile in(files_info_path);
|
|
||||||
WriteBufferFromString out(content);
|
|
||||||
|
|
||||||
/// Библиотека JSON не поддерживает пробельные символы. Удаляем их. Неэффективно.
|
|
||||||
while (!in.eof())
|
|
||||||
{
|
|
||||||
char c;
|
|
||||||
readChar(c, in);
|
|
||||||
if (!isspace(c))
|
|
||||||
writeChar(c, out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JSON json(content);
|
|
||||||
|
|
||||||
JSON files = json["yandex"];
|
|
||||||
for (const auto & name_value : files)
|
|
||||||
map[unescapeForFileName(name_value.getName())] = name_value.getValue()["size"].toUInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string files_info_path;
|
std::string files_info_path;
|
||||||
std::string tmp_files_info_path;
|
std::string tmp_files_info_path;
|
||||||
@ -173,4 +41,5 @@ private:
|
|||||||
|
|
||||||
Logger * log = &Logger::get("FileChecker");
|
Logger * log = &Logger::get("FileChecker");
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
|
|
||||||
#include <Yandex/likely.h>
|
#include <common/likely.h>
|
||||||
|
|
||||||
#include <stats/IntHash.h>
|
#include <stats/IntHash.h>
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Yandex/Common.h>
|
#include <common/Common.h>
|
||||||
|
|
||||||
|
|
||||||
/** Отслеживает потребление памяти.
|
/** Отслеживает потребление памяти.
|
||||||
|
@ -8,8 +8,8 @@
|
|||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <boost/iterator_adaptors.hpp>
|
#include <boost/iterator_adaptors.hpp>
|
||||||
|
|
||||||
#include <Yandex/likely.h>
|
#include <common/likely.h>
|
||||||
#include <Yandex/strong_typedef.h>
|
#include <common/strong_typedef.h>
|
||||||
|
|
||||||
#include <DB/Common/Allocator.h>
|
#include <DB/Common/Allocator.h>
|
||||||
#include <DB/Core/Exception.h>
|
#include <DB/Core/Exception.h>
|
||||||
|
@ -21,7 +21,7 @@ template <typename F, F* f>
|
|||||||
class SimpleCache
|
class SimpleCache
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
using Key = typename function_traits<F>::arguments_remove_reference;
|
using Key = typename function_traits<F>::arguments_decay;
|
||||||
using Result = typename function_traits<F>::result;
|
using Result = typename function_traits<F>::result;
|
||||||
|
|
||||||
std::map<Key, Result> cache;
|
std::map<Key, Result> cache;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <Poco/Types.h>
|
#include <Poco/Types.h>
|
||||||
#include <Yandex/strong_typedef.h>
|
#include <common/strong_typedef.h>
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Yandex/Common.h> /// VisitID_t
|
#include <common/Common.h> /// VisitID_t
|
||||||
|
|
||||||
#include <DB/Core/Field.h>
|
#include <DB/Core/Field.h>
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
#include <DB/Core/Row.h>
|
#include <DB/Core/Row.h>
|
||||||
#include <DB/Core/ColumnNumbers.h>
|
#include <DB/Core/ColumnNumbers.h>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
#include <DB/DataStreams/IProfilingBlockInputStream.h>
|
#include <DB/DataStreams/IProfilingBlockInputStream.h>
|
||||||
#include <DB/Core/SortDescription.h>
|
#include <DB/Core/SortDescription.h>
|
||||||
#include <DB/Columns/ColumnsNumber.h>
|
#include <DB/Columns/ColumnsNumber.h>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
#include <DB/DataStreams/MergingSortedBlockInputStream.h>
|
#include <DB/DataStreams/MergingSortedBlockInputStream.h>
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include <queue>
|
#include <queue>
|
||||||
#include <Poco/TemporaryFile.h>
|
#include <Poco/TemporaryFile.h>
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
#include <DB/Core/SortDescription.h>
|
#include <DB/Core/SortDescription.h>
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
#include <DB/Core/Row.h>
|
#include <DB/Core/Row.h>
|
||||||
#include <DB/Core/SortDescription.h>
|
#include <DB/Core/SortDescription.h>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
#include <DB/DataStreams/IProfilingBlockInputStream.h>
|
#include <DB/DataStreams/IProfilingBlockInputStream.h>
|
||||||
#include <DB/Common/setThreadName.h>
|
#include <DB/Common/setThreadName.h>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
#include <DB/DataStreams/IProfilingBlockInputStream.h>
|
#include <DB/DataStreams/IProfilingBlockInputStream.h>
|
||||||
#include <DB/DataStreams/OneBlockInputStream.h>
|
#include <DB/DataStreams/OneBlockInputStream.h>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include <DB/DataStreams/IBlockOutputStream.h>
|
#include <DB/DataStreams/IBlockOutputStream.h>
|
||||||
|
|
||||||
#include <DB/Client/Connection.h>
|
#include <DB/Client/Connection.h>
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
#include <statdaemons/NetException.h>
|
#include <statdaemons/NetException.h>
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
#include <DB/Core/Row.h>
|
#include <DB/Core/Row.h>
|
||||||
#include <DB/Core/ColumnNumbers.h>
|
#include <DB/Core/ColumnNumbers.h>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
#include <DB/Common/ConcurrentBoundedQueue.h>
|
#include <DB/Common/ConcurrentBoundedQueue.h>
|
||||||
#include <DB/DataStreams/IProfilingBlockInputStream.h>
|
#include <DB/DataStreams/IProfilingBlockInputStream.h>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include <Yandex/singleton.h>
|
#include <common/singleton.h>
|
||||||
|
|
||||||
#include <Poco/RegularExpression.h>
|
#include <Poco/RegularExpression.h>
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include <DB/Dictionaries/MySQLDictionarySource.h>
|
#include <DB/Dictionaries/MySQLDictionarySource.h>
|
||||||
#include <DB/Dictionaries/ClickHouseDictionarySource.h>
|
#include <DB/Dictionaries/ClickHouseDictionarySource.h>
|
||||||
#include <DB/DataTypes/DataTypesNumberFixed.h>
|
#include <DB/DataTypes/DataTypesNumberFixed.h>
|
||||||
#include <Yandex/singleton.h>
|
#include <common/singleton.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <DB/Functions/IFunction.h>
|
#include <DB/Functions/IFunction.h>
|
||||||
#include <Yandex/singleton.h>
|
#include <common/singleton.h>
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Poco/Net/DNS.h>
|
#include <Poco/Net/DNS.h>
|
||||||
#include <Yandex/Revision.h>
|
#include <common/Revision.h>
|
||||||
|
|
||||||
#include <DB/Core/Defines.h>
|
#include <DB/Core/Defines.h>
|
||||||
#include <DB/IO/WriteBufferFromString.h>
|
#include <DB/IO/WriteBufferFromString.h>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
#include <DB/IO/ReadBuffer.h>
|
#include <DB/IO/ReadBuffer.h>
|
||||||
#include <DB/IO/ReadBufferFromIStream.h>
|
#include <DB/IO/ReadBufferFromIStream.h>
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
#define DEFAULT_HTTP_READ_BUFFER_TIMEOUT 1800
|
#define DEFAULT_HTTP_READ_BUFFER_TIMEOUT 1800
|
||||||
#define DEFAULT_HTTP_READ_BUFFER_CONNECTION_TIMEOUT 1
|
#define DEFAULT_HTTP_READ_BUFFER_CONNECTION_TIMEOUT 1
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include <Yandex/Common.h>
|
#include <common/Common.h>
|
||||||
#include <Yandex/DateLUT.h>
|
#include <common/DateLUT.h>
|
||||||
|
|
||||||
#include <mysqlxx/Date.h>
|
#include <mysqlxx/Date.h>
|
||||||
#include <mysqlxx/DateTime.h>
|
#include <mysqlxx/DateTime.h>
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
#include <DB/IO/WriteBufferFromOStream.h>
|
#include <DB/IO/WriteBufferFromOStream.h>
|
||||||
#include <DB/IO/HashingWriteBuffer.h>
|
#include <DB/IO/HashingWriteBuffer.h>
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
#define DEFAULT_REMOTE_WRITE_BUFFER_CONNECTION_TIMEOUT 1
|
#define DEFAULT_REMOTE_WRITE_BUFFER_CONNECTION_TIMEOUT 1
|
||||||
#define DEFAULT_REMOTE_WRITE_BUFFER_RECEIVE_TIMEOUT 1800
|
#define DEFAULT_REMOTE_WRITE_BUFFER_RECEIVE_TIMEOUT 1800
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include <Yandex/Common.h>
|
#include <common/Common.h>
|
||||||
#include <Yandex/DateLUT.h>
|
#include <common/DateLUT.h>
|
||||||
|
|
||||||
#include <mysqlxx/Row.h>
|
#include <mysqlxx/Row.h>
|
||||||
#include <mysqlxx/Null.h>
|
#include <mysqlxx/Null.h>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include <boost/type_traits.hpp>
|
#include <boost/type_traits.hpp>
|
||||||
#include <boost/utility/enable_if.hpp>
|
#include <boost/utility/enable_if.hpp>
|
||||||
|
|
||||||
#include <Yandex/likely.h>
|
#include <common/likely.h>
|
||||||
|
|
||||||
#include <DB/Core/Types.h>
|
#include <DB/Core/Types.h>
|
||||||
#include <DB/IO/WriteBuffer.h>
|
#include <DB/IO/WriteBuffer.h>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
#include <statdaemons/threadpool.hpp>
|
#include <statdaemons/threadpool.hpp>
|
||||||
|
|
||||||
#include <DB/Core/StringRef.h>
|
#include <DB/Core/StringRef.h>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
#include <statdaemons/threadpool.hpp>
|
#include <statdaemons/threadpool.hpp>
|
||||||
|
|
||||||
#include <DB/Core/Types.h>
|
#include <DB/Core/Types.h>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <Yandex/MultiVersion.h>
|
#include <common/MultiVersion.h>
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
#include <statdaemons/RegionsHierarchies.h>
|
#include <statdaemons/RegionsHierarchies.h>
|
||||||
#include <statdaemons/TechDataHierarchy.h>
|
#include <statdaemons/TechDataHierarchy.h>
|
||||||
#include <statdaemons/RegionsNames.h>
|
#include <statdaemons/RegionsNames.h>
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
#include <DB/Core/Exception.h>
|
#include <DB/Core/Exception.h>
|
||||||
#include <DB/Core/ErrorCodes.h>
|
#include <DB/Core/ErrorCodes.h>
|
||||||
#include <DB/Common/setThreadName.h>
|
#include <DB/Common/setThreadName.h>
|
||||||
#include <Yandex/MultiVersion.h>
|
#include <common/MultiVersion.h>
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
#include <Poco/Event.h>
|
#include <Poco/Event.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
#include <DB/Parsers/ASTJoin.h>
|
#include <DB/Parsers/ASTJoin.h>
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ public:
|
|||||||
/** Заменить все довольно длинные однородные OR-цепочки expr = x1 OR ... OR expr = xN
|
/** Заменить все довольно длинные однородные OR-цепочки expr = x1 OR ... OR expr = xN
|
||||||
* на выражения expr IN (x1, ..., xN).
|
* на выражения expr IN (x1, ..., xN).
|
||||||
*/
|
*/
|
||||||
void optimizeDisjunctiveEqualityChains();
|
void perform();
|
||||||
|
|
||||||
LogicalExpressionsOptimizer(const LogicalExpressionsOptimizer &) = delete;
|
LogicalExpressionsOptimizer(const LogicalExpressionsOptimizer &) = delete;
|
||||||
LogicalExpressionsOptimizer & operator=(const LogicalExpressionsOptimizer &) = delete;
|
LogicalExpressionsOptimizer & operator=(const LogicalExpressionsOptimizer &) = delete;
|
||||||
@ -83,7 +83,6 @@ private:
|
|||||||
private:
|
private:
|
||||||
ASTSelectQuery * select_query;
|
ASTSelectQuery * select_query;
|
||||||
const Settings & settings;
|
const Settings & settings;
|
||||||
bool hasOptimizedDisjunctiveEqualityChains = false;
|
|
||||||
/// Информация про OR-цепочки внутри запроса.
|
/// Информация про OR-цепочки внутри запроса.
|
||||||
DisjunctiveEqualityChainsMap disjunctive_equality_chains_map;
|
DisjunctiveEqualityChainsMap disjunctive_equality_chains_map;
|
||||||
/// Количество обработанных OR-цепочек.
|
/// Количество обработанных OR-цепочек.
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
#include <Poco/Net/IPAddress.h>
|
#include <Poco/Net/IPAddress.h>
|
||||||
|
|
||||||
#include <Yandex/Common.h>
|
#include <common/Common.h>
|
||||||
|
|
||||||
#include <DB/Core/Types.h>
|
#include <DB/Core/Types.h>
|
||||||
#include <DB/Core/Exception.h>
|
#include <DB/Core/Exception.h>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
#include <DB/Core/ColumnNumbers.h>
|
#include <DB/Core/ColumnNumbers.h>
|
||||||
#include <DB/Common/Arena.h>
|
#include <DB/Common/Arena.h>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
#include <openssl/sha.h>
|
#include <openssl/sha.h>
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include <Poco/SharedPtr.h>
|
#include <Poco/SharedPtr.h>
|
||||||
|
|
||||||
#include <Yandex/Common.h>
|
#include <common/Common.h>
|
||||||
|
|
||||||
#include <DB/Core/Types.h>
|
#include <DB/Core/Types.h>
|
||||||
#include <DB/Core/Exception.h>
|
#include <DB/Core/Exception.h>
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
#include <statdaemons/Increment.h>
|
#include <statdaemons/Increment.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <Yandex/Revision.h>
|
#include <common/Revision.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
#include <DB/Core/Defines.h>
|
#include <DB/Core/Defines.h>
|
||||||
#include <DB/Core/Names.h>
|
#include <DB/Core/Names.h>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <Poco/Mutex.h>
|
#include <Poco/Mutex.h>
|
||||||
#include <Poco/RegularExpression.h>
|
#include <Poco/RegularExpression.h>
|
||||||
#include <Yandex/DateLUT.h>
|
#include <common/DateLUT.h>
|
||||||
#include <DB/Core/Types.h>
|
#include <DB/Core/Types.h>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include <Poco/Mutex.h>
|
#include <Poco/Mutex.h>
|
||||||
#include <sys/statvfs.h>
|
#include <sys/statvfs.h>
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
#include <DB/Core/Exception.h>
|
#include <DB/Core/Exception.h>
|
||||||
#include <DB/Core/ErrorCodes.h>
|
#include <DB/Core/ErrorCodes.h>
|
||||||
#include <DB/IO/WriteHelpers.h>
|
#include <DB/IO/WriteHelpers.h>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Poco/Event.h>
|
#include <Poco/Event.h>
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
#include <DB/Core/Types.h>
|
#include <DB/Core/Types.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <DB/Storages/IStorage.h>
|
#include <DB/Storages/IStorage.h>
|
||||||
#include <Yandex/singleton.h>
|
#include <common/singleton.h>
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
#include <Poco/SharedPtr.h>
|
#include <Poco/SharedPtr.h>
|
||||||
#include <Poco/Util/Application.h>
|
#include <Poco/Util/Application.h>
|
||||||
|
|
||||||
#include <Yandex/Revision.h>
|
#include <common/Revision.h>
|
||||||
|
|
||||||
#include <statdaemons/Stopwatch.h>
|
#include <statdaemons/Stopwatch.h>
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <Poco/Net/NetException.h>
|
#include <Poco/Net/NetException.h>
|
||||||
|
|
||||||
#include <Yandex/Revision.h>
|
#include <common/Revision.h>
|
||||||
|
|
||||||
#include <DB/Core/Defines.h>
|
#include <DB/Core/Defines.h>
|
||||||
#include <DB/Core/Exception.h>
|
#include <DB/Core/Exception.h>
|
||||||
|
@ -205,8 +205,7 @@ void ParallelReplicas::registerReplica(Connection * connection)
|
|||||||
if (!res.second)
|
if (!res.second)
|
||||||
throw Exception("Invalid set of connections.", ErrorCodes::LOGICAL_ERROR);
|
throw Exception("Invalid set of connections.", ErrorCodes::LOGICAL_ERROR);
|
||||||
|
|
||||||
if (throttler)
|
connection->setThrottler(throttler);
|
||||||
connection->setThrottler(throttler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connection::Packet ParallelReplicas::receivePacketUnlocked()
|
Connection::Packet ParallelReplicas::receivePacketUnlocked()
|
||||||
|
153
dbms/src/Common/FileChecker.cpp
Normal file
153
dbms/src/Common/FileChecker.cpp
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
#include <common/JSON.h>
|
||||||
|
#include <Poco/Path.h>
|
||||||
|
#include <DB/IO/WriteBufferFromFile.h>
|
||||||
|
#include <DB/IO/WriteBufferFromString.h>
|
||||||
|
#include <DB/IO/ReadBufferFromFile.h>
|
||||||
|
#include <DB/IO/copyData.h>
|
||||||
|
#include <DB/Common/escapeForFileName.h>
|
||||||
|
|
||||||
|
#include <DB/Common/FileChecker.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
FileChecker::FileChecker(const std::string & file_info_path_) :
|
||||||
|
files_info_path(file_info_path_)
|
||||||
|
{
|
||||||
|
Poco::Path path(files_info_path);
|
||||||
|
tmp_files_info_path = path.parent().toString() + "tmp_" + path.getFileName();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileChecker::setPath(const std::string & file_info_path_)
|
||||||
|
{
|
||||||
|
files_info_path = file_info_path_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileChecker::update(const Poco::File & file)
|
||||||
|
{
|
||||||
|
initialize();
|
||||||
|
updateImpl(file);
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileChecker::update(const Files::const_iterator & begin, const Files::const_iterator & end)
|
||||||
|
{
|
||||||
|
initialize();
|
||||||
|
for (auto it = begin; it != end; ++it)
|
||||||
|
updateImpl(*it);
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileChecker::check() const
|
||||||
|
{
|
||||||
|
/** Читаем файлы заново при каждом вызове check - чтобы не нарушать константность.
|
||||||
|
* Метод check вызывается редко.
|
||||||
|
*/
|
||||||
|
Map local_map;
|
||||||
|
load(local_map);
|
||||||
|
|
||||||
|
if (local_map.empty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (const auto & name_size : local_map)
|
||||||
|
{
|
||||||
|
Poco::File file(Poco::Path(files_info_path).parent().toString() + "/" + name_size.first);
|
||||||
|
if (!file.exists())
|
||||||
|
{
|
||||||
|
LOG_ERROR(log, "File " << file.path() << " doesn't exist");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t real_size = file.getSize();
|
||||||
|
if (real_size != name_size.second)
|
||||||
|
{
|
||||||
|
LOG_ERROR(log, "Size of " << file.path() << " is wrong. Size is " << real_size << " but should be " << name_size.second);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileChecker::initialize()
|
||||||
|
{
|
||||||
|
if (initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
load(map);
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileChecker::updateImpl(const Poco::File & file)
|
||||||
|
{
|
||||||
|
map[Poco::Path(file.path()).getFileName()] = file.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileChecker::save() const
|
||||||
|
{
|
||||||
|
{
|
||||||
|
WriteBufferFromFile out(tmp_files_info_path);
|
||||||
|
|
||||||
|
/// Столь сложная структура JSON-а - для совместимости со старым форматом.
|
||||||
|
writeCString("{\"yandex\":{", out);
|
||||||
|
|
||||||
|
for (auto it = map.begin(); it != map.end(); ++it)
|
||||||
|
{
|
||||||
|
if (it != map.begin())
|
||||||
|
writeString(",", out);
|
||||||
|
|
||||||
|
/// escapeForFileName на самом деле не нужен. Но он оставлен для совместимости со старым кодом.
|
||||||
|
writeJSONString(escapeForFileName(it->first), out);
|
||||||
|
writeString(":{\"size\":\"", out);
|
||||||
|
writeIntText(it->second, out);
|
||||||
|
writeString("\"}", out);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeCString("}}", out);
|
||||||
|
out.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
Poco::File current_file(files_info_path);
|
||||||
|
|
||||||
|
if (current_file.exists())
|
||||||
|
{
|
||||||
|
std::string old_file_name = files_info_path + ".old";
|
||||||
|
current_file.renameTo(old_file_name);
|
||||||
|
Poco::File(tmp_files_info_path).renameTo(files_info_path);
|
||||||
|
Poco::File(old_file_name).remove();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Poco::File(tmp_files_info_path).renameTo(files_info_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileChecker::load(Map & map) const
|
||||||
|
{
|
||||||
|
map.clear();
|
||||||
|
|
||||||
|
if (!Poco::File(files_info_path).exists())
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string content;
|
||||||
|
{
|
||||||
|
ReadBufferFromFile in(files_info_path);
|
||||||
|
WriteBufferFromString out(content);
|
||||||
|
|
||||||
|
/// Библиотека JSON не поддерживает пробельные символы. Удаляем их. Неэффективно.
|
||||||
|
while (!in.eof())
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
readChar(c, in);
|
||||||
|
if (!isspace(c))
|
||||||
|
writeChar(c, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON json(content);
|
||||||
|
|
||||||
|
JSON files = json["yandex"];
|
||||||
|
for (const auto & name_value : files)
|
||||||
|
map[unescapeForFileName(name_value.getName())] = name_value.getValue()["size"].toUInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
#include <Yandex/likely.h>
|
#include <common/likely.h>
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
#include <DB/Core/Exception.h>
|
#include <DB/Core/Exception.h>
|
||||||
#include <DB/Common/formatReadable.h>
|
#include <DB/Common/formatReadable.h>
|
||||||
#include <DB/IO/WriteHelpers.h>
|
#include <DB/IO/WriteHelpers.h>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <cxxabi.h>
|
#include <cxxabi.h>
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
#include <DB/IO/WriteHelpers.h>
|
#include <DB/IO/WriteHelpers.h>
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
#include <DB/Storages/StorageLog.h>
|
#include <DB/Storages/StorageLog.h>
|
||||||
|
|
||||||
#include <DB/Interpreters/Context.h>
|
#include <DB/Interpreters/Context.h>
|
||||||
#include <Yandex/Revision.h>
|
#include <common/Revision.h>
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char ** argv)
|
int main(int argc, char ** argv)
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include <boost/type_traits.hpp>
|
#include <boost/type_traits.hpp>
|
||||||
|
|
||||||
#include <Yandex/Common.h>
|
#include <common/Common.h>
|
||||||
|
|
||||||
#include <DB/IO/ReadHelpers.h>
|
#include <DB/IO/ReadHelpers.h>
|
||||||
#include <DB/IO/WriteHelpers.h>
|
#include <DB/IO/WriteHelpers.h>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <Poco/DirectoryIterator.h>
|
#include <Poco/DirectoryIterator.h>
|
||||||
#include <Yandex/Revision.h>
|
#include <common/Revision.h>
|
||||||
#include <statdaemons/ext/unlock_guard.hpp>
|
#include <statdaemons/ext/unlock_guard.hpp>
|
||||||
|
|
||||||
#include <DB/Common/SipHash.h>
|
#include <DB/Common/SipHash.h>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#include <Poco/File.h>
|
#include <Poco/File.h>
|
||||||
#include <Poco/UUIDGenerator.h>
|
#include <Poco/UUIDGenerator.h>
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
#include <DB/Common/Macros.h>
|
#include <DB/Common/Macros.h>
|
||||||
#include <DB/Common/escapeForFileName.h>
|
#include <DB/Common/escapeForFileName.h>
|
||||||
|
@ -114,7 +114,7 @@ void ExpressionAnalyzer::init()
|
|||||||
InJoinSubqueriesPreprocessor<>(select_query, context, storage).perform();
|
InJoinSubqueriesPreprocessor<>(select_query, context, storage).perform();
|
||||||
|
|
||||||
/// Оптимизирует логические выражения.
|
/// Оптимизирует логические выражения.
|
||||||
LogicalExpressionsOptimizer(select_query, settings).optimizeDisjunctiveEqualityChains();
|
LogicalExpressionsOptimizer(select_query, settings).perform();
|
||||||
|
|
||||||
/// Добавляет в множество известных алиасов те, которые объявлены в структуре таблицы (ALIAS-столбцы).
|
/// Добавляет в множество известных алиасов те, которые объявлены в структуре таблицы (ALIAS-столбцы).
|
||||||
addStorageAliases();
|
addStorageAliases();
|
||||||
|
@ -27,9 +27,11 @@ LogicalExpressionsOptimizer::LogicalExpressionsOptimizer(ASTSelectQuery * select
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogicalExpressionsOptimizer::optimizeDisjunctiveEqualityChains()
|
void LogicalExpressionsOptimizer::perform()
|
||||||
{
|
{
|
||||||
if ((select_query == nullptr) || hasOptimizedDisjunctiveEqualityChains)
|
if (select_query == nullptr)
|
||||||
|
return;
|
||||||
|
if (select_query->attributes & IAST::IsVisited)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
collectDisjunctiveEqualityChains();
|
collectDisjunctiveEqualityChains();
|
||||||
@ -50,8 +52,6 @@ void LogicalExpressionsOptimizer::optimizeDisjunctiveEqualityChains()
|
|||||||
cleanupOrExpressions();
|
cleanupOrExpressions();
|
||||||
fixBrokenOrExpressions();
|
fixBrokenOrExpressions();
|
||||||
}
|
}
|
||||||
|
|
||||||
hasOptimizedDisjunctiveEqualityChains = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogicalExpressionsOptimizer::collectDisjunctiveEqualityChains()
|
void LogicalExpressionsOptimizer::collectDisjunctiveEqualityChains()
|
||||||
@ -62,7 +62,7 @@ void LogicalExpressionsOptimizer::collectDisjunctiveEqualityChains()
|
|||||||
using Edge = std::pair<IAST *, IAST *>;
|
using Edge = std::pair<IAST *, IAST *>;
|
||||||
std::deque<Edge> to_visit;
|
std::deque<Edge> to_visit;
|
||||||
|
|
||||||
to_visit.push_back(Edge(nullptr, select_query));
|
to_visit.emplace_back(nullptr, select_query);
|
||||||
while (!to_visit.empty())
|
while (!to_visit.empty())
|
||||||
{
|
{
|
||||||
auto edge = to_visit.back();
|
auto edge = to_visit.back();
|
||||||
@ -70,7 +70,6 @@ void LogicalExpressionsOptimizer::collectDisjunctiveEqualityChains()
|
|||||||
auto to_node = edge.second;
|
auto to_node = edge.second;
|
||||||
|
|
||||||
to_visit.pop_back();
|
to_visit.pop_back();
|
||||||
to_node->attributes |= IAST::IsVisited;
|
|
||||||
|
|
||||||
bool found_chain = false;
|
bool found_chain = false;
|
||||||
|
|
||||||
@ -104,6 +103,8 @@ void LogicalExpressionsOptimizer::collectDisjunctiveEqualityChains()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
to_node->attributes |= IAST::IsVisited;
|
||||||
|
|
||||||
if (found_chain)
|
if (found_chain)
|
||||||
{
|
{
|
||||||
if (from_node != nullptr)
|
if (from_node != nullptr)
|
||||||
@ -196,6 +197,14 @@ void LogicalExpressionsOptimizer::addInExpression(const DisjunctiveEqualityChain
|
|||||||
value_list->children.push_back(operands[1]);
|
value_list->children.push_back(operands[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Отсортировать литералы.
|
||||||
|
std::sort(value_list->children.begin(), value_list->children.end(), [](const DB::ASTPtr & lhs, const DB::ASTPtr & rhs)
|
||||||
|
{
|
||||||
|
const auto val_lhs = static_cast<const ASTLiteral *>(&*lhs);
|
||||||
|
const auto val_rhs = static_cast<const ASTLiteral *>(&*rhs);
|
||||||
|
return val_lhs->value < val_rhs->value;
|
||||||
|
});
|
||||||
|
|
||||||
/// Получить выражение expr из цепочки expr = x1 OR ... OR expr = xN
|
/// Получить выражение expr из цепочки expr = x1 OR ... OR expr = xN
|
||||||
ASTPtr equals_expr_lhs;
|
ASTPtr equals_expr_lhs;
|
||||||
{
|
{
|
||||||
@ -241,7 +250,7 @@ void LogicalExpressionsOptimizer::cleanupOrExpressions()
|
|||||||
|
|
||||||
const auto & or_with_expression = chain.first;
|
const auto & or_with_expression = chain.first;
|
||||||
auto & operands = getFunctionOperands(or_with_expression.or_function);
|
auto & operands = getFunctionOperands(or_with_expression.or_function);
|
||||||
garbage_map.insert(std::make_pair(or_with_expression.or_function, operands.end()));
|
garbage_map.emplace(or_with_expression.or_function, operands.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Собрать мусор.
|
/// Собрать мусор.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
#include <DB/Common/SipHash.h>
|
#include <DB/Common/SipHash.h>
|
||||||
#include <DB/IO/ReadHelpers.h>
|
#include <DB/IO/ReadHelpers.h>
|
||||||
|
@ -59,6 +59,58 @@ static void executeCreateQuery(const String & query, Context & context, const St
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct Table
|
||||||
|
{
|
||||||
|
String database_name;
|
||||||
|
String dir_name;
|
||||||
|
String file_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static constexpr size_t MIN_TABLES_TO_PARALLEL_LOAD = 1;
|
||||||
|
static constexpr size_t PRINT_MESSAGE_EACH_N_TABLES = 256;
|
||||||
|
static constexpr size_t PRINT_MESSAGE_EACH_N_SECONDS = 5;
|
||||||
|
static constexpr size_t METADATA_FILE_BUFFER_SIZE = 32768;
|
||||||
|
static constexpr size_t TABLES_PARALLEL_LOAD_BUNCH_SIZE = 100;
|
||||||
|
|
||||||
|
|
||||||
|
static void loadTable(Context & context, const String & path, const Table & table)
|
||||||
|
{
|
||||||
|
Logger * log = &Logger::get("loadTable");
|
||||||
|
|
||||||
|
const String path_to_metadata = path + "/" + table.dir_name + "/" + table.file_name;
|
||||||
|
|
||||||
|
String s;
|
||||||
|
{
|
||||||
|
char in_buf[METADATA_FILE_BUFFER_SIZE];
|
||||||
|
ReadBufferFromFile in(path_to_metadata, METADATA_FILE_BUFFER_SIZE, -1, in_buf);
|
||||||
|
WriteBufferFromString out(s);
|
||||||
|
copyData(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Пустые файлы с метаданными образуются после грубого перезапуска сервера.
|
||||||
|
* Удаляем эти файлы, чтобы чуть-чуть уменьшить работу админов по запуску.
|
||||||
|
*/
|
||||||
|
if (s.empty())
|
||||||
|
{
|
||||||
|
LOG_ERROR(log, "File " << path_to_metadata << " is empty. Removing.");
|
||||||
|
Poco::File(path_to_metadata).remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
executeCreateQuery(s, context, table.database_name, path_to_metadata);
|
||||||
|
}
|
||||||
|
catch (const Exception & e)
|
||||||
|
{
|
||||||
|
throw Exception("Cannot create table from metadata file " + path_to_metadata + ", error: " + e.displayText() +
|
||||||
|
", stack trace:\n" + e.getStackTrace().toString(),
|
||||||
|
ErrorCodes::CANNOT_CREATE_TABLE_FROM_METADATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void loadMetadata(Context & context)
|
void loadMetadata(Context & context)
|
||||||
{
|
{
|
||||||
Logger * log = &Logger::get("loadMetadata");
|
Logger * log = &Logger::get("loadMetadata");
|
||||||
@ -66,16 +118,15 @@ void loadMetadata(Context & context)
|
|||||||
/// Здесь хранятся определения таблиц
|
/// Здесь хранятся определения таблиц
|
||||||
String path = context.getPath() + "metadata";
|
String path = context.getPath() + "metadata";
|
||||||
|
|
||||||
struct Table
|
|
||||||
{
|
|
||||||
String database_name;
|
|
||||||
String dir_name;
|
|
||||||
String file_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
using Tables = std::vector<Table>;
|
using Tables = std::vector<Table>;
|
||||||
Tables tables;
|
Tables tables;
|
||||||
|
|
||||||
|
/** Часть таблиц должны быть загружены раньше других, так как используются в конструкторе этих других.
|
||||||
|
* Это таблицы, имя которых начинается на .inner.
|
||||||
|
* NOTE Это довольно криво. Можно сделать лучше.
|
||||||
|
*/
|
||||||
|
Tables tables_to_load_first;
|
||||||
|
|
||||||
/// Цикл по базам данных
|
/// Цикл по базам данных
|
||||||
Poco::DirectoryIterator dir_end;
|
Poco::DirectoryIterator dir_end;
|
||||||
for (Poco::DirectoryIterator it(path); it != dir_end; ++it)
|
for (Poco::DirectoryIterator it(path); it != dir_end; ++it)
|
||||||
@ -116,26 +167,33 @@ void loadMetadata(Context & context)
|
|||||||
std::sort(file_names.begin(), file_names.end());
|
std::sort(file_names.begin(), file_names.end());
|
||||||
|
|
||||||
for (const auto & name : file_names)
|
for (const auto & name : file_names)
|
||||||
tables.emplace_back(Table{
|
{
|
||||||
.database_name = database,
|
(0 == name.compare(0, strlen("%2Einner%2E"), "%2Einner%2E")
|
||||||
.dir_name = it.name(),
|
? tables_to_load_first
|
||||||
.file_name = name});
|
: tables).emplace_back(
|
||||||
|
Table{
|
||||||
|
.database_name = database,
|
||||||
|
.dir_name = it.name(),
|
||||||
|
.file_name = name});
|
||||||
|
}
|
||||||
|
|
||||||
LOG_INFO(log, "Found " << file_names.size() << " tables.");
|
LOG_INFO(log, "Found " << file_names.size() << " tables.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tables_to_load_first.empty())
|
||||||
|
{
|
||||||
|
LOG_INFO(log, "Loading inner tables for materialized views (total " << tables_to_load_first.size() << " tables).");
|
||||||
|
|
||||||
|
for (const auto & table : tables_to_load_first)
|
||||||
|
loadTable(context, path, table);
|
||||||
|
}
|
||||||
|
|
||||||
size_t total_tables = tables.size();
|
size_t total_tables = tables.size();
|
||||||
LOG_INFO(log, "Total " << total_tables << " tables.");
|
LOG_INFO(log, "Total " << total_tables << " tables.");
|
||||||
|
|
||||||
StopwatchWithLock watch;
|
StopwatchWithLock watch;
|
||||||
size_t tables_processed = 0;
|
size_t tables_processed = 0;
|
||||||
|
|
||||||
static constexpr size_t MIN_TABLES_TO_PARALLEL_LOAD = 1;
|
|
||||||
static constexpr size_t PRINT_MESSAGE_EACH_N_TABLES = 256;
|
|
||||||
static constexpr size_t PRINT_MESSAGE_EACH_N_SECONDS = 5;
|
|
||||||
static constexpr size_t METADATA_FILE_BUFFER_SIZE = 32768;
|
|
||||||
static constexpr size_t TABLES_PARALLEL_LOAD_BUNCH_SIZE = 100;
|
|
||||||
|
|
||||||
size_t num_threads = std::min(total_tables, SettingMaxThreads().getAutoValue());
|
size_t num_threads = std::min(total_tables, SettingMaxThreads().getAutoValue());
|
||||||
|
|
||||||
std::unique_ptr<boost::threadpool::pool> thread_pool;
|
std::unique_ptr<boost::threadpool::pool> thread_pool;
|
||||||
@ -151,7 +209,6 @@ void loadMetadata(Context & context)
|
|||||||
for (Tables::const_iterator it = begin; it != end; ++it)
|
for (Tables::const_iterator it = begin; it != end; ++it)
|
||||||
{
|
{
|
||||||
const Table & table = *it;
|
const Table & table = *it;
|
||||||
const String path_to_metadata = path + "/" + table.dir_name + "/" + table.file_name;
|
|
||||||
|
|
||||||
/// Сообщения, чтобы было не скучно ждать, когда сервер долго загружается.
|
/// Сообщения, чтобы было не скучно ждать, когда сервер долго загружается.
|
||||||
if (__sync_add_and_fetch(&tables_processed, 1) % PRINT_MESSAGE_EACH_N_TABLES == 0
|
if (__sync_add_and_fetch(&tables_processed, 1) % PRINT_MESSAGE_EACH_N_TABLES == 0
|
||||||
@ -161,34 +218,7 @@ void loadMetadata(Context & context)
|
|||||||
watch.restart();
|
watch.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
String s;
|
loadTable(context, path, table);
|
||||||
{
|
|
||||||
char in_buf[METADATA_FILE_BUFFER_SIZE];
|
|
||||||
ReadBufferFromFile in(path_to_metadata, METADATA_FILE_BUFFER_SIZE, -1, in_buf);
|
|
||||||
WriteBufferFromString out(s);
|
|
||||||
copyData(in, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Пустые файлы с метаданными образуются после грубого перезапуска сервера.
|
|
||||||
* Удаляем эти файлы, чтобы чуть-чуть уменьшить работу админов по запуску.
|
|
||||||
*/
|
|
||||||
if (s.empty())
|
|
||||||
{
|
|
||||||
LOG_ERROR(log, "File " << path_to_metadata << " is empty. Removing.");
|
|
||||||
Poco::File(path_to_metadata).remove();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
executeCreateQuery(s, context, table.database_name, path_to_metadata);
|
|
||||||
}
|
|
||||||
catch (const Exception & e)
|
|
||||||
{
|
|
||||||
throw Exception("Cannot create table from metadata file " + path_to_metadata + ", error: " + e.displayText() +
|
|
||||||
", stack trace:\n" + e.getStackTrace().toString(),
|
|
||||||
ErrorCodes::CANNOT_CREATE_TABLE_FROM_METADATA);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ TestResult check(const TestEntry & entry)
|
|||||||
settings.optimize_min_equality_disjunction_chain_length = entry.limit;
|
settings.optimize_min_equality_disjunction_chain_length = entry.limit;
|
||||||
|
|
||||||
DB::LogicalExpressionsOptimizer optimizer(select_query, settings);
|
DB::LogicalExpressionsOptimizer optimizer(select_query, settings);
|
||||||
optimizer.optimizeDisjunctiveEqualityChains();
|
optimizer.perform();
|
||||||
|
|
||||||
/// Парсить ожидаемый результат.
|
/// Парсить ожидаемый результат.
|
||||||
DB::ASTPtr ast_expected;
|
DB::ASTPtr ast_expected;
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
#include <DB/IO/WriteHelpers.h>
|
#include <DB/IO/WriteHelpers.h>
|
||||||
|
|
||||||
#include <Yandex/DateLUT.h>
|
#include <common/DateLUT.h>
|
||||||
#include <strconvert/hash64.h>
|
#include <strconvert/hash64.h>
|
||||||
#include <statdaemons/RegionsHierarchy.h>
|
#include <statdaemons/RegionsHierarchy.h>
|
||||||
#include <statdaemons/TechDataHierarchy.h>
|
#include <statdaemons/TechDataHierarchy.h>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include <Yandex/DateLUT.h>
|
#include <common/DateLUT.h>
|
||||||
#include <Poco/DateTimeParser.h>
|
#include <Poco/DateTimeParser.h>
|
||||||
#include <Poco/AutoPtr.h>
|
#include <Poco/AutoPtr.h>
|
||||||
|
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
#include <Poco/RegularExpression.h>
|
#include <Poco/RegularExpression.h>
|
||||||
#include <Poco/AutoPtr.h>
|
#include <Poco/AutoPtr.h>
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
#include <Yandex/Common.h>
|
#include <common/Common.h>
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
|
@ -5,9 +5,9 @@
|
|||||||
#include <Poco/Net/DNS.h>
|
#include <Poco/Net/DNS.h>
|
||||||
#include <Poco/Util/XMLConfiguration.h>
|
#include <Poco/Util/XMLConfiguration.h>
|
||||||
|
|
||||||
#include <Yandex/ApplicationServerExt.h>
|
#include <common/ApplicationServerExt.h>
|
||||||
#include <Yandex/ErrorHandlers.h>
|
#include <common/ErrorHandlers.h>
|
||||||
#include <Yandex/Revision.h>
|
#include <common/Revision.h>
|
||||||
|
|
||||||
#include <statdaemons/ConfigProcessor.h>
|
#include <statdaemons/ConfigProcessor.h>
|
||||||
#include <statdaemons/ext/scope_guard.hpp>
|
#include <statdaemons/ext/scope_guard.hpp>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
#include <Poco/Net/TCPServerConnectionFactory.h>
|
#include <Poco/Net/TCPServerConnectionFactory.h>
|
||||||
#include <Poco/Net/TCPServerConnection.h>
|
#include <Poco/Net/TCPServerConnection.h>
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
#include <statdaemons/daemon.h>
|
#include <statdaemons/daemon.h>
|
||||||
#include <statdaemons/HTMLForm.h>
|
#include <statdaemons/HTMLForm.h>
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <Poco/Net/NetException.h>
|
#include <Poco/Net/NetException.h>
|
||||||
|
|
||||||
#include <Yandex/Revision.h>
|
#include <common/Revision.h>
|
||||||
|
|
||||||
#include <statdaemons/Stopwatch.h>
|
#include <statdaemons/Stopwatch.h>
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include <DB/Core/ErrorCodes.h>
|
#include <DB/Core/ErrorCodes.h>
|
||||||
#include <DB/Common/setThreadName.h>
|
#include <DB/Common/setThreadName.h>
|
||||||
#include <DB/IO/WriteHelpers.h>
|
#include <DB/IO/WriteHelpers.h>
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
#include <DB/Storages/MergeTree/BackgroundProcessingPool.h>
|
#include <DB/Storages/MergeTree/BackgroundProcessingPool.h>
|
||||||
|
|
||||||
|
|
||||||
|
40
libs/libcommon/include/common/ApplicationExt.h
Normal file
40
libs/libcommon/include/common/ApplicationExt.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @author Sergey N. Yatskevich
|
||||||
|
* @brief YANDEX_APP_MAIN macros
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* $Id$
|
||||||
|
*/
|
||||||
|
#ifndef __APPLICATION_EXT_H
|
||||||
|
#define __APPLICATION_EXT_H
|
||||||
|
|
||||||
|
#include <Poco/TextEncoding.h>
|
||||||
|
#include <Poco/Util/Application.h>
|
||||||
|
|
||||||
|
#define YANDEX_APP_MAIN(AppClassName) \
|
||||||
|
int \
|
||||||
|
main (int _argc, char* _argv[]) \
|
||||||
|
{ \
|
||||||
|
AppClassName app; \
|
||||||
|
try \
|
||||||
|
{ \
|
||||||
|
app.init (_argc, _argv); \
|
||||||
|
return app.run (); \
|
||||||
|
} \
|
||||||
|
catch (const Poco::Exception& _ex) \
|
||||||
|
{ \
|
||||||
|
app.logger ().log (_ex); \
|
||||||
|
} \
|
||||||
|
catch (const std::exception& _ex) \
|
||||||
|
{ \
|
||||||
|
app.logger ().error (Poco::Logger::format ("Got exception: $0", _ex.what ())); \
|
||||||
|
} \
|
||||||
|
catch (...) \
|
||||||
|
{ \
|
||||||
|
app.logger ().error ("Unknown exception"); \
|
||||||
|
} \
|
||||||
|
return Poco::Util::Application::EXIT_CONFIG; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
32
libs/libcommon/include/common/ApplicationServerExt.h
Normal file
32
libs/libcommon/include/common/ApplicationServerExt.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <Poco/TextEncoding.h>
|
||||||
|
#include <Poco/Util/ServerApplication.h>
|
||||||
|
|
||||||
|
#define YANDEX_APP_SERVER_MAIN(AppServerClassName) \
|
||||||
|
int \
|
||||||
|
main (int _argc, char* _argv[]) \
|
||||||
|
{ \
|
||||||
|
AppServerClassName app; \
|
||||||
|
try \
|
||||||
|
{ \
|
||||||
|
return app.run (_argc, _argv); \
|
||||||
|
} \
|
||||||
|
catch (const Poco::Exception& _ex) \
|
||||||
|
{ \
|
||||||
|
std::cerr << "POCO ERROR: " << _ex.displayText() << std::endl; \
|
||||||
|
app.logger().log (_ex); \
|
||||||
|
} \
|
||||||
|
catch (const std::exception& _ex) \
|
||||||
|
{ \
|
||||||
|
std::cerr << "STD ERROR: " << _ex.what() << std::endl; \
|
||||||
|
app.logger().error (Poco::Logger::format ("Got exception: $0", _ex.what ())); \
|
||||||
|
} \
|
||||||
|
catch (...) \
|
||||||
|
{ \
|
||||||
|
std::cerr << "UNKNOWN ERROR" << std::endl; \
|
||||||
|
app.logger().error ("Unknown exception"); \
|
||||||
|
} \
|
||||||
|
return Poco::Util::Application::EXIT_CONFIG; \
|
||||||
|
}
|
55
libs/libcommon/include/common/Common.h
Normal file
55
libs/libcommon/include/common/Common.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <Poco/Types.h>
|
||||||
|
|
||||||
|
#include <common/strong_typedef.h>
|
||||||
|
|
||||||
|
|
||||||
|
typedef Poco::Int8 Int8;
|
||||||
|
typedef Poco::Int16 Int16;
|
||||||
|
typedef Poco::Int32 Int32;
|
||||||
|
typedef Poco::Int64 Int64;
|
||||||
|
|
||||||
|
typedef Poco::UInt8 UInt8;
|
||||||
|
typedef Poco::UInt16 UInt16;
|
||||||
|
typedef Poco::UInt32 UInt32;
|
||||||
|
typedef Poco::UInt64 UInt64;
|
||||||
|
|
||||||
|
|
||||||
|
/// Обход проблемы с тем, что KDevelop не видит time_t и size_t (для подсветки синтаксиса).
|
||||||
|
#ifdef IN_KDEVELOP_PARSER
|
||||||
|
typedef Int64 time_t;
|
||||||
|
typedef UInt64 size_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/** Тип данных для хранения идентификатора пользователя. */
|
||||||
|
typedef UInt64 UserID_t;
|
||||||
|
|
||||||
|
/** Тип данных для хранения идентификатора счетчика. */
|
||||||
|
typedef UInt32 CounterID_t;
|
||||||
|
|
||||||
|
/** Идентификатор хита */
|
||||||
|
typedef UInt64 WatchID_t;
|
||||||
|
|
||||||
|
/** Идентификатор визита */
|
||||||
|
STRONG_TYPEDEF(UInt64, VisitID_t);
|
||||||
|
|
||||||
|
/** Идентификатор клика */
|
||||||
|
typedef UInt64 ClickID_t;
|
||||||
|
|
||||||
|
/** Идентификатор цели */
|
||||||
|
typedef UInt32 GoalID_t;
|
||||||
|
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template<>
|
||||||
|
struct hash<VisitID_t> : public unary_function<VisitID_t, size_t>
|
||||||
|
{
|
||||||
|
size_t operator()(VisitID_t x) const { return x; }
|
||||||
|
};
|
||||||
|
}
|
77
libs/libcommon/include/common/DateLUT.h
Normal file
77
libs/libcommon/include/common/DateLUT.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <common/DateLUTImpl.h>
|
||||||
|
#include <common/singleton.h>
|
||||||
|
#include <Poco/Exception.h>
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
/** Этот класс предоставляет метод для того, чтобы создать объект DateLUTImpl
|
||||||
|
* для заданного часового пояса, если он не существует.
|
||||||
|
*/
|
||||||
|
class DateLUT : public Singleton<DateLUT>
|
||||||
|
{
|
||||||
|
friend class Singleton<DateLUT>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DateLUT(const DateLUT &) = delete;
|
||||||
|
DateLUT & operator=(const DateLUT &) = delete;
|
||||||
|
|
||||||
|
/// Вернуть единственный экземпляр объекта DateLUTImpl для часового пояса по-умолчанию.
|
||||||
|
static __attribute__((__always_inline__)) const DateLUTImpl & instance()
|
||||||
|
{
|
||||||
|
const auto & date_lut = Singleton<DateLUT>::instance();
|
||||||
|
return *date_lut.default_date_lut_impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Вернуть единственный экземпляр объекта DateLUTImpl для заданного часового пояса.
|
||||||
|
static __attribute__((__always_inline__)) const DateLUTImpl & instance(const std::string & time_zone)
|
||||||
|
{
|
||||||
|
const auto & date_lut = Singleton<DateLUT>::instance();
|
||||||
|
return date_lut.get(time_zone);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Отображение часового пояса в группу эквивалентных часовый поясов.
|
||||||
|
/// Два часовых пояса эквивалентные, если они обладают одними и теми же свойствами.
|
||||||
|
using TimeZoneToGroup = std::unordered_map<std::string, size_t>;
|
||||||
|
/// Хранилище для lookup таблиц DateLUTImpl.
|
||||||
|
using DateLUTImplList = std::vector<std::atomic<DateLUTImpl *> >;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
DateLUT();
|
||||||
|
|
||||||
|
private:
|
||||||
|
__attribute__((__always_inline__)) const DateLUTImpl & get(const std::string & time_zone) const
|
||||||
|
{
|
||||||
|
if (time_zone.empty())
|
||||||
|
return *default_date_lut_impl;
|
||||||
|
|
||||||
|
auto it = time_zone_to_group.find(time_zone);
|
||||||
|
if (it == time_zone_to_group.end())
|
||||||
|
throw Poco::Exception("Invalid time zone " + time_zone);
|
||||||
|
|
||||||
|
const auto & group_id = it->second;
|
||||||
|
if (group_id == default_group_id)
|
||||||
|
return *default_date_lut_impl;
|
||||||
|
|
||||||
|
return getImplementation(time_zone, group_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const DateLUTImpl & getImplementation(const std::string & time_zone, size_t group_id) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Указатель на реализацию для часового пояса по-умолчанию.
|
||||||
|
DateLUTImpl * default_date_lut_impl;
|
||||||
|
/// Соответствующиая группа часовых поясов по-умолчанию.
|
||||||
|
size_t default_group_id;
|
||||||
|
///
|
||||||
|
TimeZoneToGroup time_zone_to_group;
|
||||||
|
/// Lookup таблица для каждой группы часовых поясов.
|
||||||
|
mutable std::unique_ptr<DateLUTImplList> date_lut_impl_list;
|
||||||
|
mutable std::mutex mutex;
|
||||||
|
};
|
501
libs/libcommon/include/common/DateLUTImpl.h
Normal file
501
libs/libcommon/include/common/DateLUTImpl.h
Normal file
@ -0,0 +1,501 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <common/Common.h>
|
||||||
|
#include <common/singleton.h>
|
||||||
|
#include <common/likely.h>
|
||||||
|
#include <common/strong_typedef.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
#define DATE_LUT_MIN 0
|
||||||
|
#define DATE_LUT_MAX (0x7FFFFFFF - 86400)
|
||||||
|
#define DATE_LUT_MAX_DAY_NUM (0x7FFFFFFF / 86400)
|
||||||
|
#define DATE_LUT_MIN_YEAR 1970
|
||||||
|
#define DATE_LUT_MAX_YEAR 2037 /// Последний полный год
|
||||||
|
#define DATE_LUT_YEARS 68 /// Количество лет в lookup таблице
|
||||||
|
|
||||||
|
|
||||||
|
STRONG_TYPEDEF(UInt16, DayNum_t);
|
||||||
|
|
||||||
|
|
||||||
|
/** Lookup таблица для преобразования времени в дату, а также в месяц или в год или в день недели или в день месяца.
|
||||||
|
* Сейчас она используется для ускорения OLAPServer-а, который делает такие преобразования миллиардами.
|
||||||
|
*/
|
||||||
|
class DateLUTImpl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DateLUTImpl(const std::string & time_zone);
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct Values
|
||||||
|
{
|
||||||
|
/// 32 бита из time_t начала дня.
|
||||||
|
/// Знаковость важна, чтобы поддержать начало 1970-01-01 MSK, которое имело time_t == -10800.
|
||||||
|
/// Измените на time_t, если надо поддержать времена после 2038 года.
|
||||||
|
Int32 date;
|
||||||
|
|
||||||
|
UInt16 year;
|
||||||
|
UInt8 month;
|
||||||
|
UInt8 day_of_month;
|
||||||
|
UInt8 day_of_week;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Сравнительно много данных. То есть, лучше не класть объект на стек.
|
||||||
|
/// По сравнению с std::vector, на один indirection меньше.
|
||||||
|
Values lut[DATE_LUT_MAX_DAY_NUM + 1];
|
||||||
|
|
||||||
|
/// lookup таблица начал годов
|
||||||
|
DayNum_t years_lut[DATE_LUT_YEARS];
|
||||||
|
|
||||||
|
/// Смещение от UTC в начале Unix эпохи.
|
||||||
|
time_t offset_at_start_of_epoch;
|
||||||
|
|
||||||
|
|
||||||
|
inline size_t findIndex(time_t t) const
|
||||||
|
{
|
||||||
|
/// первое приближение
|
||||||
|
size_t precision = t / 86400;
|
||||||
|
if (precision >= DATE_LUT_MAX_DAY_NUM)
|
||||||
|
return 0;
|
||||||
|
if (t >= lut[precision].date && t < lut[precision + 1].date)
|
||||||
|
return precision;
|
||||||
|
|
||||||
|
for (size_t i = 1;; ++i)
|
||||||
|
{
|
||||||
|
if (precision + i >= DATE_LUT_MAX_DAY_NUM)
|
||||||
|
return 0;
|
||||||
|
if (t >= lut[precision + i].date && t < lut[precision + i + 1].date)
|
||||||
|
return precision + i;
|
||||||
|
if (precision < i)
|
||||||
|
return 0;
|
||||||
|
if (t >= lut[precision - i].date && t < lut[precision - i + 1].date)
|
||||||
|
return precision - i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const Values & find(time_t t) const
|
||||||
|
{
|
||||||
|
return lut[findIndex(t)];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline DayNum_t fixDay(DayNum_t day)
|
||||||
|
{
|
||||||
|
return day > DATE_LUT_MAX_DAY_NUM ? static_cast<DayNum_t>(0) : day;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// всё ниже thread-safe; корректность входных данных не проверяется
|
||||||
|
|
||||||
|
inline time_t toDate(time_t t) const { return find(t).date; }
|
||||||
|
inline unsigned toMonth(time_t t) const { return find(t).month; }
|
||||||
|
inline unsigned toYear(time_t t) const { return find(t).year; }
|
||||||
|
inline unsigned toDayOfWeek(time_t t) const { return find(t).day_of_week; }
|
||||||
|
inline unsigned toDayOfMonth(time_t t) const { return find(t).day_of_month; }
|
||||||
|
|
||||||
|
/// номер недели, начиная с какой-то недели в прошлом; неделя начинается с понедельника
|
||||||
|
/// (переводим к понедельнику и делим DayNum на 7; будем исходить из допущения,
|
||||||
|
/// что в области применения этой функции не было и не будет недель, состоящих не из семи дней)
|
||||||
|
inline unsigned toRelativeWeekNum(DayNum_t d) const
|
||||||
|
{
|
||||||
|
return (d - (lut[d].day_of_week - 1)) / 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned toRelativeWeekNum(time_t t) const
|
||||||
|
{
|
||||||
|
size_t index = findIndex(t);
|
||||||
|
return (index - (lut[index].day_of_week - 1)) / 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// номер месяца, начиная с какого-то месяца в прошлом (год * 12 + номер месяца в году)
|
||||||
|
inline unsigned toRelativeMonthNum(DayNum_t d) const
|
||||||
|
{
|
||||||
|
return lut[d].year * 12 + lut[d].month;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned toRelativeMonthNum(time_t t) const
|
||||||
|
{
|
||||||
|
size_t index = findIndex(t);
|
||||||
|
return lut[index].year * 12 + lut[index].month;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// делим unix timestamp на 3600;
|
||||||
|
/// (таким образом, учитываются прошедшие интервалы времени длительностью в час, не зависимо от перевода стрелок;
|
||||||
|
/// поддерживаются только часовые пояса, в которых перевод стрелок осуществлялся только на целое число часов)
|
||||||
|
inline time_t toRelativeHourNum(time_t t) const
|
||||||
|
{
|
||||||
|
return t / 3600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// делим unix timestamp на 60
|
||||||
|
inline time_t toRelativeMinuteNum(time_t t) const
|
||||||
|
{
|
||||||
|
return t / 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// округление вниз до понедельника
|
||||||
|
inline time_t toFirstDayOfWeek(time_t t) const
|
||||||
|
{
|
||||||
|
size_t index = findIndex(t);
|
||||||
|
return lut[index - (lut[index].day_of_week - 1)].date;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline DayNum_t toFirstDayNumOfWeek(DayNum_t d) const
|
||||||
|
{
|
||||||
|
return DayNum_t(d - (lut[d].day_of_week - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline DayNum_t toFirstDayNumOfWeek(time_t t) const
|
||||||
|
{
|
||||||
|
size_t index = findIndex(t);
|
||||||
|
return DayNum_t(index - (lut[index].day_of_week - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// округление вниз до первого числа месяца
|
||||||
|
inline time_t toFirstDayOfMonth(time_t t) const
|
||||||
|
{
|
||||||
|
size_t index = findIndex(t);
|
||||||
|
return lut[index - (lut[index].day_of_month - 1)].date;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline DayNum_t toFirstDayNumOfMonth(DayNum_t d) const
|
||||||
|
{
|
||||||
|
return DayNum_t(d - (lut[fixDay(d)].day_of_month - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline DayNum_t toFirstDayNumOfMonth(time_t t) const
|
||||||
|
{
|
||||||
|
size_t index = findIndex(t);
|
||||||
|
return DayNum_t(index - (lut[index].day_of_month - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// округление до первого числа квартала
|
||||||
|
inline time_t toFirstDayOfQuarter(time_t t) const
|
||||||
|
{
|
||||||
|
size_t index = findIndex(t);
|
||||||
|
switch (lut[index].month % 3)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
index = index - lut[index].day_of_month;
|
||||||
|
case 2:
|
||||||
|
index = index - lut[index].day_of_month;
|
||||||
|
case 1:
|
||||||
|
index = index - lut[index].day_of_month + 1;
|
||||||
|
}
|
||||||
|
return DayNum_t(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline DayNum_t toFirstDayNumOfQuarter(DayNum_t d) const
|
||||||
|
{
|
||||||
|
size_t index = fixDay(d);
|
||||||
|
switch (lut[index].month % 3)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
index = index - lut[index].day_of_month;
|
||||||
|
case 2:
|
||||||
|
index = index - lut[index].day_of_month;
|
||||||
|
case 1:
|
||||||
|
index = index - lut[index].day_of_month + 1;
|
||||||
|
}
|
||||||
|
return DayNum_t(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline DayNum_t toFirstDayNumOfQuarter(time_t t) const
|
||||||
|
{
|
||||||
|
size_t index = findIndex(t);
|
||||||
|
switch (lut[index].month % 3)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
index = index - lut[index].day_of_month;
|
||||||
|
case 2:
|
||||||
|
index = index - lut[index].day_of_month;
|
||||||
|
case 1:
|
||||||
|
index = index - lut[index].day_of_month + 1;
|
||||||
|
}
|
||||||
|
return DayNum_t(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// округление вниз до первого числа года
|
||||||
|
inline time_t toFirstDayOfYear(time_t t) const
|
||||||
|
{
|
||||||
|
return lut[years_lut[lut[findIndex(t)].year - DATE_LUT_MIN_YEAR]].date;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline DayNum_t toFirstDayNumOfYear(DayNum_t d) const
|
||||||
|
{
|
||||||
|
return years_lut[lut[fixDay(d)].year - DATE_LUT_MIN_YEAR];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline time_t toFirstDayNumOfYear(time_t t) const
|
||||||
|
{
|
||||||
|
return lut[years_lut[lut[findIndex(t)].year - DATE_LUT_MIN_YEAR]].date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// первое число следующего месяца
|
||||||
|
inline time_t toFirstDayOfNextMonth(time_t t) const
|
||||||
|
{
|
||||||
|
size_t index = findIndex(t);
|
||||||
|
index += 32 - lut[index].day_of_month;
|
||||||
|
return lut[index - (lut[index].day_of_month - 1)].date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// первое число предыдущего месяца
|
||||||
|
inline time_t toFirstDayOfPrevMonth(time_t t) const
|
||||||
|
{
|
||||||
|
size_t index = findIndex(t);
|
||||||
|
index -= lut[index].day_of_month;
|
||||||
|
return lut[index - (lut[index].day_of_month - 1)].date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// количество дней в месяце
|
||||||
|
inline size_t daysInMonth(time_t t) const
|
||||||
|
{
|
||||||
|
size_t today = findIndex(t);
|
||||||
|
size_t start_of_month = today - (lut[today].day_of_month - 1);
|
||||||
|
size_t next_month = start_of_month + 31;
|
||||||
|
size_t start_of_next_month = next_month - (lut[next_month].day_of_month - 1);
|
||||||
|
return start_of_next_month - start_of_month;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Округление до даты; затем сдвиг на указанное количество дней.
|
||||||
|
* Замечание: результат сдвига должен находиться в пределах LUT.
|
||||||
|
*/
|
||||||
|
inline time_t toDateAndShift(time_t t, int days = 1) const
|
||||||
|
{
|
||||||
|
return lut[findIndex(t) + days].date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** функции ниже исходят из допущения, что перевод стрелок вперёд, если осуществляется, то на час, в два часа ночи,
|
||||||
|
* а перевод стрелок назад, если осуществляется, то на час, в три часа ночи.
|
||||||
|
* (что, в общем, не верно, так как в Москве один раз перевод стрелок был осуществлён в другое время)
|
||||||
|
*/
|
||||||
|
|
||||||
|
inline time_t toTimeInaccurate(time_t t) const
|
||||||
|
{
|
||||||
|
size_t index = findIndex(t);
|
||||||
|
time_t day_length = lut[index + 1].date - lut[index].date;
|
||||||
|
|
||||||
|
time_t res = t - lut[index].date;
|
||||||
|
|
||||||
|
if (unlikely(day_length == 90000 && res >= 10800)) /// был произведён перевод стрелок назад
|
||||||
|
res -= 3600;
|
||||||
|
else if (unlikely(day_length == 82800 && res >= 7200)) /// был произведён перевод стрелок вперёд
|
||||||
|
res += 3600;
|
||||||
|
|
||||||
|
return res - offset_at_start_of_epoch; /// Отсчёт от 1970-01-01 00:00:00 по локальному времени
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned toHourInaccurate(time_t t) const
|
||||||
|
{
|
||||||
|
size_t index = findIndex(t);
|
||||||
|
time_t day_length = lut[index + 1].date - lut[index].date;
|
||||||
|
unsigned res = (t - lut[index].date) / 3600;
|
||||||
|
|
||||||
|
if (unlikely(day_length == 90000 && res >= 3)) /// был произведён перевод стрелок назад
|
||||||
|
--res;
|
||||||
|
else if (unlikely(day_length == 82800 && res >= 2)) /// был произведён перевод стрелок вперёд
|
||||||
|
++res;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned toMinute(time_t t) const { return ((t - find(t).date) % 3600) / 60; }
|
||||||
|
inline unsigned toSecond(time_t t) const { return (t - find(t).date) % 60; }
|
||||||
|
|
||||||
|
inline unsigned toStartOfMinute(time_t t) const
|
||||||
|
{
|
||||||
|
time_t date = find(t).date;
|
||||||
|
return date + (t - date) / 60 * 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned toStartOfHour(time_t t) const
|
||||||
|
{
|
||||||
|
time_t date = find(t).date;
|
||||||
|
return date + (t - date) / 3600 * 3600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Только для часовых поясов, отличающихся от UTC на значение, кратное часу и без перевода стрелок не значение не кратное часу */
|
||||||
|
|
||||||
|
inline unsigned toMinuteInaccurate(time_t t) const { return (t / 60) % 60; }
|
||||||
|
inline unsigned toSecondInaccurate(time_t t) const { return t % 60; }
|
||||||
|
|
||||||
|
inline unsigned toStartOfMinuteInaccurate(time_t t) const { return t / 60 * 60; }
|
||||||
|
inline unsigned toStartOfFiveMinuteInaccurate(time_t t) const { return t / 300 * 300; }
|
||||||
|
inline unsigned toStartOfHourInaccurate(time_t t) const { return t / 3600 * 3600; }
|
||||||
|
|
||||||
|
/// Номер дня в пределах UNIX эпохи (и немного больше) - позволяет хранить дату в двух байтах
|
||||||
|
|
||||||
|
inline DayNum_t toDayNum(time_t t) const { return static_cast<DayNum_t>(findIndex(t)); }
|
||||||
|
inline time_t fromDayNum(DayNum_t d) const { return lut[fixDay(d)].date; }
|
||||||
|
|
||||||
|
inline time_t toDate(DayNum_t d) const { return lut[fixDay(d)].date; }
|
||||||
|
inline unsigned toMonth(DayNum_t d) const { return lut[fixDay(d)].month; }
|
||||||
|
inline unsigned toYear(DayNum_t d) const { return lut[fixDay(d)].year; }
|
||||||
|
inline unsigned toDayOfWeek(DayNum_t d) const { return lut[fixDay(d)].day_of_week; }
|
||||||
|
inline unsigned toDayOfMonth(DayNum_t d) const { return lut[fixDay(d)].day_of_month; }
|
||||||
|
|
||||||
|
inline const Values & getValues(DayNum_t d) const { return lut[fixDay(d)]; }
|
||||||
|
inline const Values & getValues(time_t t) const { return lut[findIndex(t)]; }
|
||||||
|
|
||||||
|
/// получает DayNum_t из года, месяца, дня
|
||||||
|
inline DayNum_t makeDayNum(short year, char month, char day_of_month) const
|
||||||
|
{
|
||||||
|
if (unlikely(year < DATE_LUT_MIN_YEAR || year > DATE_LUT_MAX_YEAR || month < 1 || month > 12 || day_of_month < 1 || day_of_month > 31))
|
||||||
|
return DayNum_t(0);
|
||||||
|
DayNum_t any_day_of_month(years_lut[year - DATE_LUT_MIN_YEAR] + 31 * (month - 1));
|
||||||
|
return DayNum_t(any_day_of_month - toDayOfMonth(any_day_of_month) + day_of_month);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline time_t makeDate(short year, char month, char day_of_month) const
|
||||||
|
{
|
||||||
|
return lut[makeDayNum(year, month, day_of_month)].date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Функция ниже исходит из допущения, что перевод стрелок вперёд, если осуществляется, то на час, в два часа ночи,
|
||||||
|
* а перевод стрелок назад, если осуществляется, то на час, в три часа ночи.
|
||||||
|
* (что, в общем, не верно, так как в Москве один раз перевод стрелок был осуществлён в другое время).
|
||||||
|
* Также, выдаётся лишь один из двух возможных вариантов при переводе стрелок назад.
|
||||||
|
*/
|
||||||
|
inline time_t makeDateTime(short year, char month, char day_of_month, char hour, char minute, char second) const
|
||||||
|
{
|
||||||
|
size_t index = makeDayNum(year, month, day_of_month);
|
||||||
|
time_t res = lut[index].date + hour * 3600 + minute * 60 + second;
|
||||||
|
time_t day_length = lut[index + 1].date - lut[index].date;
|
||||||
|
|
||||||
|
if (unlikely(day_length == 90000 && hour >= 3)) /// был произведён перевод стрелок назад
|
||||||
|
res += 3600;
|
||||||
|
else if (unlikely(day_length == 82800 && hour >= 2)) /// был произведён перевод стрелок вперёд
|
||||||
|
res -= 3600;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline UInt32 toNumYYYYMMDD(time_t t) const
|
||||||
|
{
|
||||||
|
const Values & values = find(t);
|
||||||
|
return values.year * 10000 + values.month * 100 + values.day_of_month;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline UInt32 toNumYYYYMMDD(DayNum_t d) const
|
||||||
|
{
|
||||||
|
const Values & values = lut[fixDay(d)];
|
||||||
|
return values.year * 10000 + values.month * 100 + values.day_of_month;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline time_t YYYYMMDDToDate(UInt32 num) const
|
||||||
|
{
|
||||||
|
return makeDate(num / 10000, num / 100 % 100, num % 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline DayNum_t YYYYMMDDToDayNum(UInt32 num) const
|
||||||
|
{
|
||||||
|
return makeDayNum(num / 10000, num / 100 % 100, num % 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline UInt64 toNumYYYYMMDDhhmmss(time_t t) const
|
||||||
|
{
|
||||||
|
const Values & values = find(t);
|
||||||
|
return
|
||||||
|
toSecondInaccurate(t)
|
||||||
|
+ toMinuteInaccurate(t) * 100
|
||||||
|
+ toHourInaccurate(t) * 10000
|
||||||
|
+ UInt64(values.day_of_month) * 1000000
|
||||||
|
+ UInt64(values.month) * 100000000
|
||||||
|
+ UInt64(values.year) * 10000000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline time_t YYYYMMDDhhmmssToTime(UInt64 num) const
|
||||||
|
{
|
||||||
|
return makeDateTime(
|
||||||
|
num / 10000000000,
|
||||||
|
num / 100000000 % 100,
|
||||||
|
num / 1000000 % 100,
|
||||||
|
num / 10000 % 100,
|
||||||
|
num / 100 % 100,
|
||||||
|
num % 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline std::string timeToString(time_t t) const
|
||||||
|
{
|
||||||
|
const Values & values = find(t);
|
||||||
|
|
||||||
|
std::string s {"0000-00-00 00:00:00"};
|
||||||
|
|
||||||
|
s[0] += values.year / 1000;
|
||||||
|
s[1] += (values.year / 100) % 10;
|
||||||
|
s[2] += (values.year / 10) % 10;
|
||||||
|
s[3] += values.year % 10;
|
||||||
|
s[5] += values.month / 10;
|
||||||
|
s[6] += values.month % 10;
|
||||||
|
s[8] += values.day_of_month / 10;
|
||||||
|
s[9] += values.day_of_month % 10;
|
||||||
|
|
||||||
|
auto hour = toHourInaccurate(t);
|
||||||
|
auto minute = toMinuteInaccurate(t);
|
||||||
|
auto second = toSecondInaccurate(t);
|
||||||
|
|
||||||
|
s[11] += hour / 10;
|
||||||
|
s[12] += hour % 10;
|
||||||
|
s[14] += minute / 10;
|
||||||
|
s[15] += minute % 10;
|
||||||
|
s[17] += second / 10;
|
||||||
|
s[18] += second % 10;
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string dateToString(time_t t) const
|
||||||
|
{
|
||||||
|
const Values & values = find(t);
|
||||||
|
|
||||||
|
std::string s {"0000-00-00"};
|
||||||
|
|
||||||
|
s[0] += values.year / 1000;
|
||||||
|
s[1] += (values.year / 100) % 10;
|
||||||
|
s[2] += (values.year / 10) % 10;
|
||||||
|
s[3] += values.year % 10;
|
||||||
|
s[5] += values.month / 10;
|
||||||
|
s[6] += values.month % 10;
|
||||||
|
s[8] += values.day_of_month / 10;
|
||||||
|
s[9] += values.day_of_month % 10;
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string dateToString(DayNum_t d) const
|
||||||
|
{
|
||||||
|
const Values & values = lut[fixDay(d)];
|
||||||
|
|
||||||
|
std::string s {"0000-00-00"};
|
||||||
|
|
||||||
|
s[0] += values.year / 1000;
|
||||||
|
s[1] += (values.year / 100) % 10;
|
||||||
|
s[2] += (values.year / 10) % 10;
|
||||||
|
s[3] += values.year % 10;
|
||||||
|
s[5] += values.month / 10;
|
||||||
|
s[6] += values.month % 10;
|
||||||
|
s[8] += values.day_of_month / 10;
|
||||||
|
s[9] += values.day_of_month % 10;
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template<>
|
||||||
|
struct hash<DayNum_t>
|
||||||
|
{
|
||||||
|
size_t operator() (DayNum_t x) const
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
37
libs/libcommon/include/common/ErrorHandlers.h
Normal file
37
libs/libcommon/include/common/ErrorHandlers.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Poco/ErrorHandler.h>
|
||||||
|
#include <Poco/Net/SocketDefs.h>
|
||||||
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
|
|
||||||
|
/** ErrorHandler для потоков, который в случае неперехваченного исключения,
|
||||||
|
* выводит ошибку в лог и завершает демон.
|
||||||
|
*/
|
||||||
|
class KillingErrorHandler : public Poco::ErrorHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void exception(const Poco::Exception & e) { std::terminate(); }
|
||||||
|
void exception(const std::exception & e) { std::terminate(); }
|
||||||
|
void exception() { std::terminate(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** То же самое, но не завершает работу в случае эксепшена типа Socket is not connected.
|
||||||
|
* Этот эксепшен возникает внутри реализаций Poco::Net::HTTPServer, Poco::Net::TCPServer,
|
||||||
|
* и иначе его не удаётся перехватить, и сервер завершает работу.
|
||||||
|
*/
|
||||||
|
class ServerErrorHandler : public KillingErrorHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void exception(const Poco::Exception & e)
|
||||||
|
{
|
||||||
|
if (e.code() == POCO_ENOTCONN)
|
||||||
|
LOG_WARNING(log, "Client has gone away.");
|
||||||
|
else
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Logger * log = &Logger::get("ServerErrorHandler");
|
||||||
|
};
|
197
libs/libcommon/include/common/JSON.h
Normal file
197
libs/libcommon/include/common/JSON.h
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <typeinfo>
|
||||||
|
#include <Poco/Exception.h>
|
||||||
|
#include <DB/Core/StringRef.h>
|
||||||
|
#include <common/Common.h>
|
||||||
|
|
||||||
|
#define PURE __attribute__((pure))
|
||||||
|
|
||||||
|
|
||||||
|
/** Очень простой класс для чтения JSON (или его кусочков).
|
||||||
|
* Представляет собой ссылку на кусок памяти, в котором содержится JSON (или его кусочек).
|
||||||
|
* Не создаёт никаких структур данных в оперативке. Не выделяет память (кроме std::string).
|
||||||
|
* Не парсит JSON до конца (парсит только часть, необходимую для выполнения вызванного метода).
|
||||||
|
* Парсинг необходимой части запускается каждый раз при вызове методов.
|
||||||
|
* Может работать с обрезанным JSON-ом.
|
||||||
|
* При этом, (в отличие от SAX-подобных парсеров), предоставляет удобные методы для работы.
|
||||||
|
*
|
||||||
|
* Эта структура данных более оптимальна, если нужно доставать несколько элементов из большого количества маленьких JSON-ов.
|
||||||
|
* То есть, подходит для обработки "параметров визитов" и "параметров интернет магазинов" в Яндекс.Метрике.
|
||||||
|
* Если нужно много работать с одним большим JSON-ом, то этот класс может быть менее оптимальным.
|
||||||
|
*
|
||||||
|
* Имеются следующие соглашения:
|
||||||
|
* 1. Предполагается, что в JSON-е нет пробельных символов.
|
||||||
|
* 2. Предполагается, что строки в JSON в кодировке UTF-8; также могут использоваться \u-последовательности.
|
||||||
|
* Строки возвращаются в кодировке UTF-8, \u-последовательности переводятся в UTF-8.
|
||||||
|
* 3. Но суррогатная пара из двух \uXXXX\uYYYY переводится не в UTF-8, а в CESU-8.
|
||||||
|
* 4. Корректный JSON парсится корректно.
|
||||||
|
* При работе с некорректным JSON-ом, кидается исключение или возвращаются неверные результаты.
|
||||||
|
* (пример: считается, что если встретился символ 'n', то после него идёт 'ull' (null);
|
||||||
|
* если после него идёт ',1,', то исключение не кидается, и, таким образом, возвращается неверный результат)
|
||||||
|
* 5. Глубина вложенности JSON ограничена (см. MAX_JSON_DEPTH в cpp файле).
|
||||||
|
* При необходимости спуститься на большую глубину, кидается исключение.
|
||||||
|
* 6. В отличие от JSON, пользоволяет парсить значения вида 64-битное число, со знаком, или без.
|
||||||
|
* При этом, если число дробное - то дробная часть тихо отбрасывается.
|
||||||
|
* 7. Числа с плавающей запятой парсятся не с максимальной точностью.
|
||||||
|
*
|
||||||
|
* Подходит только для чтения JSON, модификация не предусмотрена.
|
||||||
|
* Все методы immutable, кроме operator++.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
POCO_DECLARE_EXCEPTION(Foundation_API, JSONException, Poco::Exception);
|
||||||
|
|
||||||
|
|
||||||
|
class JSON
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
typedef const char * Pos;
|
||||||
|
Pos ptr_begin;
|
||||||
|
Pos ptr_end;
|
||||||
|
unsigned level;
|
||||||
|
|
||||||
|
public:
|
||||||
|
JSON(Pos ptr_begin_, Pos ptr_end_, unsigned level_ = 0) : ptr_begin(ptr_begin_), ptr_end(ptr_end_), level(level_)
|
||||||
|
{
|
||||||
|
checkInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON(const std::string & s) : ptr_begin(s.data()), ptr_end(s.data() + s.size()), level(0)
|
||||||
|
{
|
||||||
|
checkInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON(const JSON & rhs) : ptr_begin(rhs.ptr_begin), ptr_end(rhs.ptr_end), level(rhs.level) {}
|
||||||
|
|
||||||
|
/// Для вставки в контейнеры (создаёт некорректный объект)
|
||||||
|
JSON() : ptr_begin(nullptr), ptr_end(ptr_begin + 1) {}
|
||||||
|
|
||||||
|
const char * data() const PURE { return ptr_begin; }
|
||||||
|
const char * dataEnd() const PURE { return ptr_end; }
|
||||||
|
|
||||||
|
enum ElementType
|
||||||
|
{
|
||||||
|
TYPE_OBJECT,
|
||||||
|
TYPE_ARRAY,
|
||||||
|
TYPE_NUMBER,
|
||||||
|
TYPE_STRING,
|
||||||
|
TYPE_BOOL,
|
||||||
|
TYPE_NULL,
|
||||||
|
TYPE_NAME_VALUE_PAIR,
|
||||||
|
TYPE_NOTYPE,
|
||||||
|
};
|
||||||
|
|
||||||
|
ElementType getType() const PURE;
|
||||||
|
|
||||||
|
bool isObject() const PURE { return getType() == TYPE_OBJECT; };
|
||||||
|
bool isArray() const PURE { return getType() == TYPE_ARRAY; };
|
||||||
|
bool isNumber() const PURE { return getType() == TYPE_NUMBER; };
|
||||||
|
bool isString() const PURE { return getType() == TYPE_STRING; };
|
||||||
|
bool isBool() const PURE { return getType() == TYPE_BOOL; };
|
||||||
|
bool isNull() const PURE { return getType() == TYPE_NULL; };
|
||||||
|
bool isNameValuePair() const PURE { return getType() == TYPE_NAME_VALUE_PAIR; };
|
||||||
|
|
||||||
|
/// Количество элементов в массиве или объекте; если элемент - не массив или объект, то исключение.
|
||||||
|
size_t size() const PURE;
|
||||||
|
|
||||||
|
/// Является ли массив или объект пустыми; если элемент - не массив или объект, то исключение.
|
||||||
|
bool empty() const PURE;
|
||||||
|
|
||||||
|
/// Получить элемент массива по индексу; если элемент - не массив, то исключение.
|
||||||
|
JSON operator[] (size_t n) const PURE;
|
||||||
|
|
||||||
|
/// Получить элемент объекта по имени; если элемент - не объект, то исключение.
|
||||||
|
JSON operator[] (const std::string & name) const PURE;
|
||||||
|
|
||||||
|
/// Есть ли в объекте элемент с заданным именем; если элемент - не объект, то исключение.
|
||||||
|
bool has(const std::string & name) const PURE { return has(name.data(), name.size()); }
|
||||||
|
bool has(const char * data, size_t size) const PURE;
|
||||||
|
|
||||||
|
/// Получить значение элемента; исключение, если элемент имеет неправильный тип.
|
||||||
|
template <class T>
|
||||||
|
T get() const PURE;
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
T getWithDefault(const std::string & key, const T & default_ = T()) const PURE;
|
||||||
|
|
||||||
|
double getDouble() const PURE;
|
||||||
|
Int64 getInt() const PURE; /// Отбросить дробную часть.
|
||||||
|
UInt64 getUInt() const PURE; /// Отбросить дробную часть. Если число отрицательное - исключение.
|
||||||
|
std::string getString() const PURE;
|
||||||
|
bool getBool() const PURE;
|
||||||
|
std::string getName() const PURE; /// Получить имя name-value пары.
|
||||||
|
JSON getValue() const PURE; /// Получить значение name-value пары.
|
||||||
|
|
||||||
|
StringRef getRawString() const PURE;
|
||||||
|
StringRef getRawName() const PURE;
|
||||||
|
|
||||||
|
/// Получить значение элемента; если элемент - строка, то распарсить значение из строки; если не строка или число - то исключение.
|
||||||
|
double toDouble() const PURE;
|
||||||
|
Int64 toInt() const PURE;
|
||||||
|
UInt64 toUInt() const PURE;
|
||||||
|
|
||||||
|
/** Преобразовать любой элемент в строку.
|
||||||
|
* Для строки возвращается её значение, для всех остальных элементов - сериализованное представление.
|
||||||
|
*/
|
||||||
|
std::string toString() const PURE;
|
||||||
|
|
||||||
|
/// Класс JSON одновременно является итератором по самому себе.
|
||||||
|
typedef JSON iterator;
|
||||||
|
typedef JSON const_iterator;
|
||||||
|
|
||||||
|
iterator operator* () const PURE { return *this; }
|
||||||
|
const JSON * operator-> () const PURE { return this; }
|
||||||
|
bool operator== (const JSON & rhs) const PURE { return ptr_begin == rhs.ptr_begin; }
|
||||||
|
bool operator!= (const JSON & rhs) const PURE { return ptr_begin != rhs.ptr_begin; }
|
||||||
|
|
||||||
|
/** Если элемент - массив или объект, то begin() возвращает iterator,
|
||||||
|
* который указывает на первый элемент массива или первую name-value пару объекта.
|
||||||
|
*/
|
||||||
|
iterator begin() const PURE;
|
||||||
|
|
||||||
|
/** end() - значение, которое нельзя использовать; сигнализирует о том, что элементы закончились.
|
||||||
|
*/
|
||||||
|
iterator end() const PURE;
|
||||||
|
|
||||||
|
/// Перейти к следующему элементу массива или следующей name-value паре объекта.
|
||||||
|
iterator & operator++();
|
||||||
|
iterator operator++(int);
|
||||||
|
|
||||||
|
/// Есть ли в строке escape-последовательности
|
||||||
|
bool hasEscapes() const PURE;
|
||||||
|
|
||||||
|
/// Есть ли в строке спец-символы из набора \, ', \0, \b, \f, \r, \n, \t, возможно, заэскейпленные.
|
||||||
|
bool hasSpecialChars() const PURE;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Проверить глубину рекурсии, а также корректность диапазона памяти.
|
||||||
|
void checkInit() const PURE;
|
||||||
|
/// Проверить, что pos лежит внутри диапазона памяти.
|
||||||
|
void checkPos(Pos pos) const PURE;
|
||||||
|
|
||||||
|
/// Вернуть позицию после заданного элемента.
|
||||||
|
Pos skipString() const PURE;
|
||||||
|
Pos skipNumber() const PURE;
|
||||||
|
Pos skipBool() const PURE;
|
||||||
|
Pos skipNull() const PURE;
|
||||||
|
Pos skipNameValuePair() const PURE;
|
||||||
|
Pos skipObject() const PURE;
|
||||||
|
Pos skipArray() const PURE;
|
||||||
|
|
||||||
|
Pos skipElement() const PURE;
|
||||||
|
|
||||||
|
/// Найти name-value пару с заданным именем в объекте.
|
||||||
|
Pos searchField(const std::string & name) const PURE { return searchField(name.data(), name.size()); }
|
||||||
|
Pos searchField(const char * data, size_t size) const PURE;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
T JSON::getWithDefault(const std::string & key, const T & default_) const
|
||||||
|
{
|
||||||
|
if (has(key))
|
||||||
|
return (*this)[key].get<T>();
|
||||||
|
else
|
||||||
|
return default_;
|
||||||
|
}
|
||||||
|
#undef PURE
|
62
libs/libcommon/include/common/MultiVersion.h
Normal file
62
libs/libcommon/include/common/MultiVersion.h
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Poco/Mutex.h>
|
||||||
|
#include <Poco/SharedPtr.h>
|
||||||
|
|
||||||
|
|
||||||
|
/** Позволяет хранить некоторый объект, использовать его read-only в разных потоках,
|
||||||
|
* и заменять его на другой в других потоках.
|
||||||
|
* Замена производится атомарно, при этом, читающие потоки могут работать с разными версиями объекта.
|
||||||
|
*
|
||||||
|
* Использование:
|
||||||
|
* MultiVersion<T> x;
|
||||||
|
* - при обновлении данных:
|
||||||
|
* x.set(new value);
|
||||||
|
* - при использовании данных для чтения в разных потоках:
|
||||||
|
* {
|
||||||
|
* MultiVersion<T>::Version current_version = x.get();
|
||||||
|
* // используем для чего-нибудь *current_version
|
||||||
|
* } // здесь перестаём владеть версией; если версия устарела, и её никто больше не использует - она будет уничтожена
|
||||||
|
*
|
||||||
|
* Все методы thread-safe.
|
||||||
|
*/
|
||||||
|
template <typename T, typename Ptr = Poco::SharedPtr<T> >
|
||||||
|
class MultiVersion
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Конкретная версия объекта для использования. SharedPtr определяет время жизни версии.
|
||||||
|
typedef Ptr Version;
|
||||||
|
|
||||||
|
/// Инициализация по-умолчанию (NULL-ом).
|
||||||
|
MultiVersion() = default;
|
||||||
|
|
||||||
|
/// Инициализация первой версией.
|
||||||
|
MultiVersion(const Version & value)
|
||||||
|
{
|
||||||
|
set(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiVersion(Version && value)
|
||||||
|
{
|
||||||
|
set(std::move(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Получить текущую версию для использования. Возвращает SharedPtr, который определяет время жизни версии.
|
||||||
|
const Version get() const
|
||||||
|
{
|
||||||
|
/// TODO: можно ли заменять SharedPtr lock-free? (Можно, если сделать свою реализацию с использованием cmpxchg16b.)
|
||||||
|
Poco::ScopedLock<Poco::FastMutex> lock(mutex);
|
||||||
|
return current_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Обновить объект новой версией.
|
||||||
|
void set(Version value)
|
||||||
|
{
|
||||||
|
Poco::ScopedLock<Poco::FastMutex> lock(mutex);
|
||||||
|
current_version = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Version current_version;
|
||||||
|
mutable Poco::FastMutex mutex;
|
||||||
|
};
|
6
libs/libcommon/include/common/Revision.h
Normal file
6
libs/libcommon/include/common/Revision.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Revision
|
||||||
|
{
|
||||||
|
unsigned get();
|
||||||
|
}
|
4
libs/libcommon/include/common/likely.h
Normal file
4
libs/libcommon/include/common/likely.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define likely(x) (__builtin_expect(!!(x), 1))
|
||||||
|
#define unlikely(x) (__builtin_expect(!!(x), 0))
|
61
libs/libcommon/include/common/logger_useful.h
Normal file
61
libs/libcommon/include/common/logger_useful.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
/// Вспомогательные определения облегчающие работу с PoCo logging.
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <Poco/Logger.h>
|
||||||
|
|
||||||
|
#ifndef QUERY_PREVIEW_LENGTH
|
||||||
|
#define QUERY_PREVIEW_LENGTH 160
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using Poco::Logger;
|
||||||
|
|
||||||
|
/// Logs a message to a specified logger with that level.
|
||||||
|
|
||||||
|
#define LOG_TRACE(logger, message) do { \
|
||||||
|
if ((logger)->trace()) {\
|
||||||
|
std::stringstream oss; \
|
||||||
|
oss << message; \
|
||||||
|
(logger)->trace(oss.str());}} while(0)
|
||||||
|
|
||||||
|
#define LOG_DEBUG(logger, message) do { \
|
||||||
|
if ((logger)->debug()) {\
|
||||||
|
std::stringstream oss; \
|
||||||
|
oss << message; \
|
||||||
|
(logger)->debug(oss.str());}} while(0)
|
||||||
|
|
||||||
|
#define LOG_INFO(logger, message) do { \
|
||||||
|
if ((logger)->information()) {\
|
||||||
|
std::stringstream oss; \
|
||||||
|
oss << message; \
|
||||||
|
(logger)->information(oss.str());}} while(0)
|
||||||
|
|
||||||
|
#define LOG_NOTICE(logger, message) do { \
|
||||||
|
if ((logger)->notice()) {\
|
||||||
|
std::stringstream oss; \
|
||||||
|
oss << message; \
|
||||||
|
(logger)->notice(oss.str());}} while(0)
|
||||||
|
|
||||||
|
#define LOG_WARNING(logger, message) do { \
|
||||||
|
if ((logger)->warning()) {\
|
||||||
|
std::stringstream oss; \
|
||||||
|
oss << message; \
|
||||||
|
(logger)->warning(oss.str());}} while(0)
|
||||||
|
|
||||||
|
#define LOG_ERROR(logger, message) do { \
|
||||||
|
if ((logger)->error()) {\
|
||||||
|
std::stringstream oss; \
|
||||||
|
oss << message; \
|
||||||
|
(logger)->error(oss.str());}} while(0)
|
||||||
|
|
||||||
|
#define LOG_CRITICAL(logger, message) do { \
|
||||||
|
if ((logger)->critical()) {\
|
||||||
|
std::stringstream oss; \
|
||||||
|
oss << message; \
|
||||||
|
(logger)->critical(oss.str());}} while(0)
|
||||||
|
|
||||||
|
#define LOG_FATAL(logger, message) do { \
|
||||||
|
if ((logger)->fatal()) {\
|
||||||
|
std::stringstream oss; \
|
||||||
|
oss << message; \
|
||||||
|
(logger)->fatal(oss.str());}} while(0)
|
38
libs/libcommon/include/common/singleton.h
Normal file
38
libs/libcommon/include/common/singleton.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/** Пример:
|
||||||
|
*
|
||||||
|
* class Derived : public Singleton<Derived>
|
||||||
|
* {
|
||||||
|
* friend class Singleton<Derived>;
|
||||||
|
* ...
|
||||||
|
* protected:
|
||||||
|
* Derived() {};
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* Или так:
|
||||||
|
*
|
||||||
|
* class Some
|
||||||
|
* {
|
||||||
|
* ...
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* class SomeSingleton : public Some, public Singleton<SomeSingleton> {}
|
||||||
|
*/
|
||||||
|
template<class Subject> class Singleton
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static Subject & instance()
|
||||||
|
{
|
||||||
|
/// Нормально при включенных thread safe statics в gcc (по-умолчанию).
|
||||||
|
static Subject instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Singleton(){};
|
||||||
|
|
||||||
|
private:
|
||||||
|
Singleton(const Singleton&);
|
||||||
|
Singleton& operator=(const Singleton&);
|
||||||
|
};
|
23
libs/libcommon/include/common/strong_typedef.h
Normal file
23
libs/libcommon/include/common/strong_typedef.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/operators.hpp>
|
||||||
|
|
||||||
|
/** https://svn.boost.org/trac/boost/ticket/5182
|
||||||
|
*/
|
||||||
|
#define STRONG_TYPEDEF(T, D) \
|
||||||
|
struct D \
|
||||||
|
: boost::totally_ordered1< D \
|
||||||
|
, boost::totally_ordered2< D, T \
|
||||||
|
> > \
|
||||||
|
{ \
|
||||||
|
T t; \
|
||||||
|
explicit D(const T t_) : t(t_) {}; \
|
||||||
|
D(): t() {}; \
|
||||||
|
D(const D & t_) : t(t_.t){} \
|
||||||
|
D & operator=(const D & rhs) { t = rhs.t; return *this;} \
|
||||||
|
D & operator=(const T & rhs) { t = rhs; return *this;} \
|
||||||
|
operator const T & () const {return t; } \
|
||||||
|
operator T & () { return t; } \
|
||||||
|
bool operator==(const D & rhs) const { return t == rhs.t; } \
|
||||||
|
bool operator<(const D & rhs) const { return t < rhs.t; } \
|
||||||
|
};
|
@ -1,4 +1,4 @@
|
|||||||
#include <Yandex/DateLUT.h>
|
#include <common/DateLUT.h>
|
||||||
|
|
||||||
#include <unicode/timezone.h>
|
#include <unicode/timezone.h>
|
||||||
#include <unicode/unistr.h>
|
#include <unicode/unistr.h>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include <Yandex/DateLUTImpl.h>
|
#include <common/DateLUTImpl.h>
|
||||||
#include <Poco/Exception.h>
|
#include <Poco/Exception.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
788
libs/libcommon/src/JSON.cpp
Normal file
788
libs/libcommon/src/JSON.cpp
Normal file
@ -0,0 +1,788 @@
|
|||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <Poco/UTF8Encoding.h>
|
||||||
|
#include <Poco/NumberFormatter.h>
|
||||||
|
#include <Poco/NumberParser.h>
|
||||||
|
#include <common/JSON.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#define JSON_MAX_DEPTH 100
|
||||||
|
|
||||||
|
|
||||||
|
POCO_IMPLEMENT_EXCEPTION(JSONException, Poco::Exception, "JSONException")
|
||||||
|
|
||||||
|
|
||||||
|
/// Прочитать беззнаковое целое в простом формате из не-0-terminated строки.
|
||||||
|
static UInt64 readUIntText(const char * buf, const char * end)
|
||||||
|
{
|
||||||
|
UInt64 x = 0;
|
||||||
|
|
||||||
|
if (buf == end)
|
||||||
|
throw JSONException("JSON: cannot parse unsigned integer: unexpected end of data.");
|
||||||
|
|
||||||
|
while (buf != end)
|
||||||
|
{
|
||||||
|
switch (*buf)
|
||||||
|
{
|
||||||
|
case '+':
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
x *= 10;
|
||||||
|
x += *buf - '0';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
++buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Прочитать знаковое целое в простом формате из не-0-terminated строки.
|
||||||
|
static Int64 readIntText(const char * buf, const char * end)
|
||||||
|
{
|
||||||
|
bool negative = false;
|
||||||
|
Int64 x = 0;
|
||||||
|
|
||||||
|
if (buf == end)
|
||||||
|
throw JSONException("JSON: cannot parse signed integer: unexpected end of data.");
|
||||||
|
|
||||||
|
bool run = true;
|
||||||
|
while (buf != end && run)
|
||||||
|
{
|
||||||
|
switch (*buf)
|
||||||
|
{
|
||||||
|
case '+':
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
negative = true;
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
x *= 10;
|
||||||
|
x += *buf - '0';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
run = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++buf;
|
||||||
|
}
|
||||||
|
if (negative)
|
||||||
|
x = -x;
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Прочитать число с плавающей запятой в простом формате, с грубым округлением, из не-0-terminated строки.
|
||||||
|
static double readFloatText(const char * buf, const char * end)
|
||||||
|
{
|
||||||
|
bool negative = false;
|
||||||
|
double x = 0;
|
||||||
|
bool after_point = false;
|
||||||
|
double power_of_ten = 1;
|
||||||
|
|
||||||
|
if (buf == end)
|
||||||
|
throw JSONException("JSON: cannot parse floating point number: unexpected end of data.");
|
||||||
|
|
||||||
|
bool run = true;
|
||||||
|
while (buf != end && run)
|
||||||
|
{
|
||||||
|
switch (*buf)
|
||||||
|
{
|
||||||
|
case '+':
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
negative = true;
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
after_point = true;
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
if (after_point)
|
||||||
|
{
|
||||||
|
power_of_ten /= 10;
|
||||||
|
x += (*buf - '0') * power_of_ten;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
x *= 10;
|
||||||
|
x += *buf - '0';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
case 'E':
|
||||||
|
{
|
||||||
|
++buf;
|
||||||
|
Int32 exponent = readIntText(buf, end);
|
||||||
|
x *= exp10(exponent);
|
||||||
|
|
||||||
|
run = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
run = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++buf;
|
||||||
|
}
|
||||||
|
if (negative)
|
||||||
|
x = -x;
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void JSON::checkInit() const
|
||||||
|
{
|
||||||
|
if (!(ptr_begin < ptr_end))
|
||||||
|
throw JSONException("JSON: begin >= end.");
|
||||||
|
|
||||||
|
if (level > JSON_MAX_DEPTH)
|
||||||
|
throw JSONException("JSON: too deep.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSON::ElementType JSON::getType() const
|
||||||
|
{
|
||||||
|
switch (*ptr_begin)
|
||||||
|
{
|
||||||
|
case '{':
|
||||||
|
return TYPE_OBJECT;
|
||||||
|
case '[':
|
||||||
|
return TYPE_ARRAY;
|
||||||
|
case 't':
|
||||||
|
case 'f':
|
||||||
|
return TYPE_BOOL;
|
||||||
|
case 'n':
|
||||||
|
return TYPE_NULL;
|
||||||
|
case '-':
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
return TYPE_NUMBER;
|
||||||
|
case '"':
|
||||||
|
{
|
||||||
|
/// Проверим - это просто строка или name-value pair
|
||||||
|
Pos after_string = skipString();
|
||||||
|
if (after_string < ptr_end && *after_string == ':')
|
||||||
|
return TYPE_NAME_VALUE_PAIR;
|
||||||
|
else
|
||||||
|
return TYPE_STRING;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw JSONException(std::string("JSON: unexpected char ") + *ptr_begin + ", expected one of '{[tfn-0123456789\"'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void JSON::checkPos(Pos pos) const
|
||||||
|
{
|
||||||
|
if (pos >= ptr_end)
|
||||||
|
throw JSONException("JSON: unexpected end of data.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSON::Pos JSON::skipString() const
|
||||||
|
{
|
||||||
|
//std::cerr << "skipString()\t" << data() << std::endl;
|
||||||
|
|
||||||
|
Pos pos = ptr_begin;
|
||||||
|
if (*pos != '"')
|
||||||
|
throw JSONException(std::string("JSON: expected \", got ") + *pos);
|
||||||
|
++pos;
|
||||||
|
|
||||||
|
/// fast path: находим следующую двойную кавычку. Если перед ней нет бэкслеша - значит это конец строки (при допущении корректности JSON).
|
||||||
|
Pos closing_quote = reinterpret_cast<const char *>(memchr(reinterpret_cast<const void *>(pos), '\"', ptr_end - pos));
|
||||||
|
if (nullptr != closing_quote && closing_quote[-1] != '\\')
|
||||||
|
return closing_quote + 1;
|
||||||
|
|
||||||
|
/// slow path
|
||||||
|
while (pos < ptr_end && *pos != '"')
|
||||||
|
{
|
||||||
|
if (*pos == '\\')
|
||||||
|
{
|
||||||
|
++pos;
|
||||||
|
checkPos(pos);
|
||||||
|
if (*pos == 'u')
|
||||||
|
{
|
||||||
|
pos += 4;
|
||||||
|
checkPos(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*pos != '"')
|
||||||
|
throw JSONException(std::string("JSON: expected \", got ") + *pos);
|
||||||
|
++pos;
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSON::Pos JSON::skipNumber() const
|
||||||
|
{
|
||||||
|
//std::cerr << "skipNumber()\t" << data() << std::endl;
|
||||||
|
|
||||||
|
Pos pos = ptr_begin;
|
||||||
|
|
||||||
|
if (*pos == '-')
|
||||||
|
++pos;
|
||||||
|
checkPos(pos);
|
||||||
|
|
||||||
|
while (pos < ptr_end && *pos >= '0' && *pos <= '9')
|
||||||
|
++pos;
|
||||||
|
if (pos < ptr_end && *pos == '.')
|
||||||
|
++pos;
|
||||||
|
while (pos < ptr_end && *pos >= '0' && *pos <= '9')
|
||||||
|
++pos;
|
||||||
|
if (pos < ptr_end && (*pos == 'e' || *pos == 'E'))
|
||||||
|
++pos;
|
||||||
|
if (pos < ptr_end && *pos == '-')
|
||||||
|
++pos;
|
||||||
|
while (pos < ptr_end && *pos >= '0' && *pos <= '9')
|
||||||
|
++pos;
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSON::Pos JSON::skipBool() const
|
||||||
|
{
|
||||||
|
//std::cerr << "skipBool()\t" << data() << std::endl;
|
||||||
|
|
||||||
|
Pos pos = ptr_begin;
|
||||||
|
|
||||||
|
if (*ptr_begin == 't')
|
||||||
|
pos += 4;
|
||||||
|
else if (*ptr_begin == 'f')
|
||||||
|
pos += 5;
|
||||||
|
else
|
||||||
|
throw JSONException("JSON: expected true or false.");
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSON::Pos JSON::skipNull() const
|
||||||
|
{
|
||||||
|
//std::cerr << "skipNull()\t" << data() << std::endl;
|
||||||
|
|
||||||
|
return ptr_begin + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSON::Pos JSON::skipNameValuePair() const
|
||||||
|
{
|
||||||
|
//std::cerr << "skipNameValuePair()\t" << data() << std::endl;
|
||||||
|
|
||||||
|
Pos pos = skipString();
|
||||||
|
checkPos(pos);
|
||||||
|
|
||||||
|
if (*pos != ':')
|
||||||
|
throw JSONException("JSON: expected :.");
|
||||||
|
++pos;
|
||||||
|
|
||||||
|
return JSON(pos, ptr_end, level + 1).skipElement();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSON::Pos JSON::skipArray() const
|
||||||
|
{
|
||||||
|
//std::cerr << "skipArray()\t" << data() << std::endl;
|
||||||
|
|
||||||
|
if (!isArray())
|
||||||
|
throw JSONException("JSON: expected [");
|
||||||
|
Pos pos = ptr_begin;
|
||||||
|
++pos;
|
||||||
|
checkPos(pos);
|
||||||
|
if (*pos == ']')
|
||||||
|
return ++pos;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
pos = JSON(pos, ptr_end, level + 1).skipElement();
|
||||||
|
if (*pos != ',')
|
||||||
|
break;
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
return ++pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSON::Pos JSON::skipObject() const
|
||||||
|
{
|
||||||
|
//std::cerr << "skipObject()\t" << data() << std::endl;
|
||||||
|
|
||||||
|
if (!isObject())
|
||||||
|
throw JSONException("JSON: expected {");
|
||||||
|
Pos pos = ptr_begin;
|
||||||
|
++pos;
|
||||||
|
checkPos(pos);
|
||||||
|
if (*pos == '}')
|
||||||
|
return ++pos;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
pos = JSON(pos, ptr_end, level + 1).skipNameValuePair();
|
||||||
|
if (*pos != ',')
|
||||||
|
break;
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
return ++pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSON::Pos JSON::skipElement() const
|
||||||
|
{
|
||||||
|
//std::cerr << "skipElement()\t" << data() << std::endl;
|
||||||
|
|
||||||
|
ElementType type = getType();
|
||||||
|
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case TYPE_NULL:
|
||||||
|
return skipNull();
|
||||||
|
case TYPE_BOOL:
|
||||||
|
return skipBool();
|
||||||
|
case TYPE_NUMBER:
|
||||||
|
return skipNumber();
|
||||||
|
case TYPE_STRING:
|
||||||
|
return skipString();
|
||||||
|
case TYPE_NAME_VALUE_PAIR:
|
||||||
|
return skipNameValuePair();
|
||||||
|
case TYPE_ARRAY:
|
||||||
|
return skipArray();
|
||||||
|
case TYPE_OBJECT:
|
||||||
|
return skipObject();
|
||||||
|
default:
|
||||||
|
throw JSONException("Logical error in JSON: unknown element type: " + Poco::NumberFormatter::format(type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t JSON::size() const
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
for (const_iterator it = begin(); it != end(); ++it)
|
||||||
|
++i;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool JSON::empty() const
|
||||||
|
{
|
||||||
|
return size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSON JSON::operator[] (size_t n) const
|
||||||
|
{
|
||||||
|
ElementType type = getType();
|
||||||
|
|
||||||
|
if (type != TYPE_ARRAY)
|
||||||
|
throw JSONException("JSON: not array when calling operator[](size_t) method.");
|
||||||
|
|
||||||
|
Pos pos = ptr_begin;
|
||||||
|
++pos;
|
||||||
|
checkPos(pos);
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
const_iterator it = begin();
|
||||||
|
while (i < n && it != end())
|
||||||
|
++it, ++i;
|
||||||
|
|
||||||
|
if (i != n)
|
||||||
|
throw JSONException("JSON: array index " + Poco::NumberFormatter::format(n) + " out of bounds.");
|
||||||
|
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSON::Pos JSON::searchField(const char * data, size_t size) const
|
||||||
|
{
|
||||||
|
ElementType type = getType();
|
||||||
|
|
||||||
|
if (type != TYPE_OBJECT)
|
||||||
|
throw JSONException("JSON: not object when calling operator[](const char *) or has(const char *) method.");
|
||||||
|
|
||||||
|
const_iterator it = begin();
|
||||||
|
for (; it != end(); ++it)
|
||||||
|
{
|
||||||
|
if (!it->hasEscapes())
|
||||||
|
{
|
||||||
|
if (static_cast<int>(size) + 2 > it->dataEnd() - it->data())
|
||||||
|
continue;
|
||||||
|
if (!strncmp(data, it->data() + 1, size))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string current_name = it->getName();
|
||||||
|
if (current_name.size() == size && 0 == memcmp(current_name.data(), data, size))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it == end())
|
||||||
|
return nullptr;
|
||||||
|
else
|
||||||
|
return it->data();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool JSON::hasEscapes() const
|
||||||
|
{
|
||||||
|
Pos pos = ptr_begin + 1;
|
||||||
|
while (pos < ptr_end && *pos != '"' && *pos != '\\')
|
||||||
|
++pos;
|
||||||
|
|
||||||
|
if (*pos == '"')
|
||||||
|
return false;
|
||||||
|
else if (*pos == '\\')
|
||||||
|
return true;
|
||||||
|
throw JSONException("JSON: unexpected end of data.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool JSON::hasSpecialChars() const
|
||||||
|
{
|
||||||
|
Pos pos = ptr_begin + 1;
|
||||||
|
while (pos < ptr_end && *pos != '"'
|
||||||
|
&& *pos != '\\' && *pos != '\r' && *pos != '\n' && *pos != '\t'
|
||||||
|
&& *pos != '\f' && *pos != '\b' && *pos != '\0' && *pos != '\'')
|
||||||
|
++pos;
|
||||||
|
|
||||||
|
if (*pos == '"')
|
||||||
|
return false;
|
||||||
|
else if (pos < ptr_end)
|
||||||
|
return true;
|
||||||
|
throw JSONException("JSON: unexpected end of data.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSON JSON::operator[] (const std::string & name) const
|
||||||
|
{
|
||||||
|
Pos pos = searchField(name);
|
||||||
|
if (!pos)
|
||||||
|
throw JSONException("JSON: there is no element '" + std::string(name) + "' in object.");
|
||||||
|
|
||||||
|
return JSON(pos, ptr_end, level + 1).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool JSON::has(const char * data, size_t size) const
|
||||||
|
{
|
||||||
|
return nullptr != searchField(data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double JSON::getDouble() const
|
||||||
|
{
|
||||||
|
return readFloatText(ptr_begin, ptr_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
Int64 JSON::getInt() const
|
||||||
|
{
|
||||||
|
return readIntText(ptr_begin, ptr_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt64 JSON::getUInt() const
|
||||||
|
{
|
||||||
|
return readUIntText(ptr_begin, ptr_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JSON::getBool() const
|
||||||
|
{
|
||||||
|
if (*ptr_begin == 't')
|
||||||
|
return true;
|
||||||
|
if (*ptr_begin == 'f')
|
||||||
|
return false;
|
||||||
|
throw JSONException("JSON: cannot parse boolean.");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string JSON::getString() const
|
||||||
|
{
|
||||||
|
Pos s = ptr_begin;
|
||||||
|
if (*s != '"')
|
||||||
|
throw JSONException(std::string("JSON: expected \", got ") + *s);
|
||||||
|
++s;
|
||||||
|
checkPos(s);
|
||||||
|
|
||||||
|
std::string buf;
|
||||||
|
while (s < ptr_end)
|
||||||
|
{
|
||||||
|
switch (*s)
|
||||||
|
{
|
||||||
|
case '\\':
|
||||||
|
++s;
|
||||||
|
checkPos(s);
|
||||||
|
|
||||||
|
switch(*s)
|
||||||
|
{
|
||||||
|
case '"':
|
||||||
|
buf += '"';
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
buf += '\\';
|
||||||
|
break;
|
||||||
|
case '/':
|
||||||
|
buf += '/';
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
buf += '\b';
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
buf += '\f';
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
buf += '\n';
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
buf += '\r';
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
buf += '\t';
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
{
|
||||||
|
Poco::UTF8Encoding utf8;
|
||||||
|
|
||||||
|
++s;
|
||||||
|
checkPos(s + 4);
|
||||||
|
std::string hex(s, 4);
|
||||||
|
s += 3;
|
||||||
|
int unicode;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
unicode = Poco::NumberParser::parseHex(hex);
|
||||||
|
}
|
||||||
|
catch (const Poco::SyntaxException & e)
|
||||||
|
{
|
||||||
|
throw JSONException("JSON: incorrect syntax: incorrect HEX code.");
|
||||||
|
}
|
||||||
|
buf.resize(buf.size() + 6); /// максимальный размер UTF8 многобайтовой последовательности
|
||||||
|
int res = utf8.convert(unicode,
|
||||||
|
reinterpret_cast<unsigned char *>(const_cast<char*>(buf.data())) + buf.size() - 6, 6);
|
||||||
|
if (!res)
|
||||||
|
throw JSONException("JSON: cannot convert unicode " + Poco::NumberFormatter::format(unicode)
|
||||||
|
+ " to UTF8.");
|
||||||
|
buf.resize(buf.size() - 6 + res);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
buf += *s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++s;
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
return buf;
|
||||||
|
default:
|
||||||
|
buf += *s;
|
||||||
|
++s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw JSONException("JSON: incorrect syntax (expected end of string, found end of JSON).");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string JSON::getName() const
|
||||||
|
{
|
||||||
|
return getString();
|
||||||
|
}
|
||||||
|
|
||||||
|
StringRef JSON::getRawString() const
|
||||||
|
{
|
||||||
|
Pos s = ptr_begin;
|
||||||
|
if (*s != '"')
|
||||||
|
throw JSONException(std::string("JSON: expected \", got ") + *s);
|
||||||
|
while (++s != ptr_end && *s != '"');
|
||||||
|
if (s != ptr_end )
|
||||||
|
return StringRef(ptr_begin + 1, s - ptr_begin - 1);
|
||||||
|
throw JSONException("JSON: incorrect syntax (expected end of string, found end of JSON).");
|
||||||
|
}
|
||||||
|
|
||||||
|
StringRef JSON::getRawName() const
|
||||||
|
{
|
||||||
|
return getRawString();
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON JSON::getValue() const
|
||||||
|
{
|
||||||
|
Pos pos = skipString();
|
||||||
|
checkPos(pos);
|
||||||
|
if (*pos != ':')
|
||||||
|
throw JSONException("JSON: expected :.");
|
||||||
|
++pos;
|
||||||
|
checkPos(pos);
|
||||||
|
return JSON(pos, ptr_end, level + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double JSON::toDouble() const
|
||||||
|
{
|
||||||
|
ElementType type = getType();
|
||||||
|
|
||||||
|
if (type == TYPE_NUMBER)
|
||||||
|
return getDouble();
|
||||||
|
else if (type == TYPE_STRING)
|
||||||
|
return JSON(ptr_begin + 1, ptr_end, level + 1).getDouble();
|
||||||
|
else
|
||||||
|
throw JSONException("JSON: cannot convert value to double.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Int64 JSON::toInt() const
|
||||||
|
{
|
||||||
|
ElementType type = getType();
|
||||||
|
|
||||||
|
if (type == TYPE_NUMBER)
|
||||||
|
return getInt();
|
||||||
|
else if (type == TYPE_STRING)
|
||||||
|
return JSON(ptr_begin + 1, ptr_end, level + 1).getInt();
|
||||||
|
else
|
||||||
|
throw JSONException("JSON: cannot convert value to signed integer.");
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt64 JSON::toUInt() const
|
||||||
|
{
|
||||||
|
ElementType type = getType();
|
||||||
|
|
||||||
|
if (type == TYPE_NUMBER)
|
||||||
|
return getUInt();
|
||||||
|
else if (type == TYPE_STRING)
|
||||||
|
return JSON(ptr_begin + 1, ptr_end, level + 1).getUInt();
|
||||||
|
else
|
||||||
|
throw JSONException("JSON: cannot convert value to unsigned integer.");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string JSON::toString() const
|
||||||
|
{
|
||||||
|
ElementType type = getType();
|
||||||
|
|
||||||
|
if (type == TYPE_STRING)
|
||||||
|
return getString();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Pos pos = skipElement();
|
||||||
|
return std::string(ptr_begin, pos - ptr_begin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSON::iterator JSON::iterator::begin() const
|
||||||
|
{
|
||||||
|
ElementType type = getType();
|
||||||
|
|
||||||
|
if (type != TYPE_ARRAY && type != TYPE_OBJECT)
|
||||||
|
throw JSONException("JSON: not array or object when calling begin() method.");
|
||||||
|
|
||||||
|
//std::cerr << "begin()\t" << data() << std::endl;
|
||||||
|
|
||||||
|
Pos pos = ptr_begin + 1;
|
||||||
|
checkPos(pos);
|
||||||
|
if (*pos == '}' || *pos == ']')
|
||||||
|
return end();
|
||||||
|
|
||||||
|
return JSON(pos, ptr_end, level + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON::iterator JSON::iterator::end() const
|
||||||
|
{
|
||||||
|
return JSON(nullptr, ptr_end, level + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON::iterator & JSON::iterator::operator++()
|
||||||
|
{
|
||||||
|
Pos pos = skipElement();
|
||||||
|
checkPos(pos);
|
||||||
|
|
||||||
|
if (*pos != ',')
|
||||||
|
ptr_begin = nullptr;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++pos;
|
||||||
|
checkPos(pos);
|
||||||
|
ptr_begin = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON::iterator JSON::iterator::operator++(int)
|
||||||
|
{
|
||||||
|
iterator copy(*this);
|
||||||
|
++*this;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
double JSON::get<double>() const
|
||||||
|
{
|
||||||
|
return getDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::string JSON::get<std::string>() const
|
||||||
|
{
|
||||||
|
return getString();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
Int64 JSON::get<Int64>() const
|
||||||
|
{
|
||||||
|
return getInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
UInt64 JSON::get<UInt64>() const
|
||||||
|
{
|
||||||
|
return getUInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool JSON::get<bool>() const
|
||||||
|
{
|
||||||
|
return getBool();
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
#include <Yandex/Revision.h>
|
#include <common/Revision.h>
|
||||||
#include "revision.h"
|
#include "revision.h"
|
||||||
|
|
||||||
namespace Revision
|
namespace Revision
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include <Yandex/DateLUT.h>
|
#include <common/DateLUT.h>
|
||||||
|
|
||||||
|
|
||||||
static std::string toString(time_t Value)
|
static std::string toString(time_t Value)
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include <Poco/Exception.h>
|
#include <Poco/Exception.h>
|
||||||
|
|
||||||
#include <Yandex/DateLUT.h>
|
#include <common/DateLUT.h>
|
||||||
|
|
||||||
|
|
||||||
static std::string toString(time_t Value)
|
static std::string toString(time_t Value)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <Yandex/DateLUT.h>
|
#include <common/DateLUT.h>
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char ** argv)
|
int main(int argc, char ** argv)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include <Yandex/DateLUT.h>
|
#include <common/DateLUT.h>
|
||||||
|
|
||||||
/// Позволяет проверить время инициализации DateLUT.
|
/// Позволяет проверить время инициализации DateLUT.
|
||||||
int main(int argc, char ** argv)
|
int main(int argc, char ** argv)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <statdaemons/threadpool.hpp>
|
#include <statdaemons/threadpool.hpp>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <Yandex/MultiVersion.h>
|
#include <common/MultiVersion.h>
|
||||||
|
|
||||||
|
|
||||||
typedef std::string T;
|
typedef std::string T;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include <Poco/Util/Application.h>
|
#include <Poco/Util/Application.h>
|
||||||
|
|
||||||
#include <Yandex/singleton.h>
|
#include <common/singleton.h>
|
||||||
|
|
||||||
#include <mysqlxx/Query.h>
|
#include <mysqlxx/Query.h>
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <Yandex/DateLUT.h>
|
#include <common/DateLUT.h>
|
||||||
|
|
||||||
#include <mysqlxx/Exception.h>
|
#include <mysqlxx/Exception.h>
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <Yandex/DateLUT.h>
|
#include <common/DateLUT.h>
|
||||||
|
|
||||||
#include <mysqlxx/Date.h>
|
#include <mysqlxx/Date.h>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
#include <Poco/Exception.h>
|
#include <Poco/Exception.h>
|
||||||
#include <Poco/SharedPtr.h>
|
#include <Poco/SharedPtr.h>
|
||||||
|
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
#include <statdaemons/daemon.h>
|
#include <statdaemons/daemon.h>
|
||||||
|
|
||||||
#include <mysqlxx/Connection.h>
|
#include <mysqlxx/Connection.h>
|
||||||
|
@ -8,8 +8,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
#include <Yandex/Common.h>
|
#include <common/Common.h>
|
||||||
#include <Yandex/DateLUT.h>
|
#include <common/DateLUT.h>
|
||||||
|
|
||||||
#include <mysqlxx/Types.h>
|
#include <mysqlxx/Types.h>
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <Yandex/likely.h>
|
#include <common/likely.h>
|
||||||
|
|
||||||
#include <Poco/Ext/ThreadNumber.h>
|
#include <Poco/Ext/ThreadNumber.h>
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <zkutil/ZooKeeper.h>
|
#include <zkutil/ZooKeeper.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
|
|
||||||
namespace zkutil
|
namespace zkutil
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <zkutil/ZooKeeper.h>
|
#include <zkutil/ZooKeeper.h>
|
||||||
#include <Yandex/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
#include <DB/Core/Exception.h>
|
#include <DB/Core/Exception.h>
|
||||||
|
|
||||||
namespace zkutil
|
namespace zkutil
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user