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 <sstream>
|
||||
#include <stats/ReservoirSampler.h>
|
||||
#include <Yandex/Common.h>
|
||||
#include <common/Common.h>
|
||||
#include <DB/Common/HashTable/Hash.h>
|
||||
#include <DB/IO/ReadBuffer.h>
|
||||
#include <DB/IO/ReadHelpers.h>
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
#include <Poco/Net/StreamSocket.h>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <Poco/SharedPtr.h>
|
||||
#include <Yandex/likely.h>
|
||||
#include <common/likely.h>
|
||||
#include <DB/Common/ProfileEvents.h>
|
||||
#include <DB/Common/Allocator.h>
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <Yandex/likely.h>
|
||||
#include <common/likely.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include <DB/Core/Exception.h>
|
||||
#include <DB/IO/WriteHelpers.h>
|
||||
|
||||
#include <Yandex/Common.h>
|
||||
#include <common/Common.h>
|
||||
|
||||
#include <Poco/String.h>
|
||||
|
||||
|
@ -1,15 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <statdaemons/JSON.h>
|
||||
#include <Poco/Path.h>
|
||||
#include <common/logger_useful.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
|
||||
@ -23,146 +16,21 @@ private:
|
||||
using Map = std::map<std::string, size_t>;
|
||||
|
||||
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>;
|
||||
|
||||
void update(const Poco::File & file)
|
||||
{
|
||||
initialize();
|
||||
updateImpl(file);
|
||||
save();
|
||||
}
|
||||
|
||||
void update(const Files::iterator & begin, const Files::iterator & end)
|
||||
{
|
||||
initialize();
|
||||
for (auto it = begin; it != end; ++it)
|
||||
updateImpl(*it);
|
||||
save();
|
||||
}
|
||||
FileChecker(const std::string & file_info_path_);
|
||||
void setPath(const std::string & file_info_path_);
|
||||
void update(const Poco::File & file);
|
||||
void update(const Files::const_iterator & begin, const Files::const_iterator & end);
|
||||
|
||||
/// Проверяем файлы, параметры которых указаны в sizes.json
|
||||
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;
|
||||
}
|
||||
bool check() const;
|
||||
|
||||
private:
|
||||
void initialize()
|
||||
{
|
||||
if (initialized)
|
||||
return;
|
||||
|
||||
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();
|
||||
}
|
||||
void initialize();
|
||||
void updateImpl(const Poco::File & file);
|
||||
void save() const;
|
||||
void load(Map & map) const;
|
||||
|
||||
std::string files_info_path;
|
||||
std::string tmp_files_info_path;
|
||||
@ -173,4 +41,5 @@ private:
|
||||
|
||||
Logger * log = &Logger::get("FileChecker");
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include <Yandex/likely.h>
|
||||
#include <common/likely.h>
|
||||
|
||||
#include <stats/IntHash.h>
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Yandex/Common.h>
|
||||
#include <common/Common.h>
|
||||
|
||||
|
||||
/** Отслеживает потребление памяти.
|
||||
|
@ -8,8 +8,8 @@
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/iterator_adaptors.hpp>
|
||||
|
||||
#include <Yandex/likely.h>
|
||||
#include <Yandex/strong_typedef.h>
|
||||
#include <common/likely.h>
|
||||
#include <common/strong_typedef.h>
|
||||
|
||||
#include <DB/Common/Allocator.h>
|
||||
#include <DB/Core/Exception.h>
|
||||
|
@ -21,7 +21,7 @@ template <typename F, F* f>
|
||||
class SimpleCache
|
||||
{
|
||||
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;
|
||||
|
||||
std::map<Key, Result> cache;
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <Poco/Types.h>
|
||||
#include <Yandex/strong_typedef.h>
|
||||
#include <common/strong_typedef.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Yandex/Common.h> /// VisitID_t
|
||||
#include <common/Common.h> /// VisitID_t
|
||||
|
||||
#include <DB/Core/Field.h>
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
#include <DB/Core/Row.h>
|
||||
#include <DB/Core/ColumnNumbers.h>
|
||||
|
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <DB/DataStreams/IProfilingBlockInputStream.h>
|
||||
#include <DB/Core/SortDescription.h>
|
||||
#include <DB/Columns/ColumnsNumber.h>
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
#include <DB/DataStreams/MergingSortedBlockInputStream.h>
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include <queue>
|
||||
#include <Poco/TemporaryFile.h>
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
#include <DB/Core/SortDescription.h>
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
#include <DB/Core/Row.h>
|
||||
#include <DB/Core/SortDescription.h>
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
#include <DB/DataStreams/IProfilingBlockInputStream.h>
|
||||
#include <DB/Common/setThreadName.h>
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
#include <DB/DataStreams/IProfilingBlockInputStream.h>
|
||||
#include <DB/DataStreams/OneBlockInputStream.h>
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include <DB/DataStreams/IBlockOutputStream.h>
|
||||
|
||||
#include <DB/Client/Connection.h>
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
#include <statdaemons/NetException.h>
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
#include <DB/Core/Row.h>
|
||||
#include <DB/Core/ColumnNumbers.h>
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
#include <DB/Common/ConcurrentBoundedQueue.h>
|
||||
#include <DB/DataStreams/IProfilingBlockInputStream.h>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <Yandex/singleton.h>
|
||||
#include <common/singleton.h>
|
||||
|
||||
#include <Poco/RegularExpression.h>
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include <DB/Dictionaries/MySQLDictionarySource.h>
|
||||
#include <DB/Dictionaries/ClickHouseDictionarySource.h>
|
||||
#include <DB/DataTypes/DataTypesNumberFixed.h>
|
||||
#include <Yandex/singleton.h>
|
||||
#include <common/singleton.h>
|
||||
#include <memory>
|
||||
|
||||
namespace DB
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <DB/Functions/IFunction.h>
|
||||
#include <Yandex/singleton.h>
|
||||
#include <common/singleton.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Poco/Net/DNS.h>
|
||||
#include <Yandex/Revision.h>
|
||||
#include <common/Revision.h>
|
||||
|
||||
#include <DB/Core/Defines.h>
|
||||
#include <DB/IO/WriteBufferFromString.h>
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include <DB/IO/ReadBuffer.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_CONNECTION_TIMEOUT 1
|
||||
|
@ -6,8 +6,8 @@
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <Yandex/Common.h>
|
||||
#include <Yandex/DateLUT.h>
|
||||
#include <common/Common.h>
|
||||
#include <common/DateLUT.h>
|
||||
|
||||
#include <mysqlxx/Date.h>
|
||||
#include <mysqlxx/DateTime.h>
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include <DB/IO/WriteBufferFromOStream.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_RECEIVE_TIMEOUT 1800
|
||||
|
@ -5,8 +5,8 @@
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
|
||||
#include <Yandex/Common.h>
|
||||
#include <Yandex/DateLUT.h>
|
||||
#include <common/Common.h>
|
||||
#include <common/DateLUT.h>
|
||||
|
||||
#include <mysqlxx/Row.h>
|
||||
#include <mysqlxx/Null.h>
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <boost/type_traits.hpp>
|
||||
#include <boost/utility/enable_if.hpp>
|
||||
|
||||
#include <Yandex/likely.h>
|
||||
#include <common/likely.h>
|
||||
|
||||
#include <DB/Core/Types.h>
|
||||
#include <DB/IO/WriteBuffer.h>
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <statdaemons/threadpool.hpp>
|
||||
|
||||
#include <DB/Core/StringRef.h>
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <statdaemons/threadpool.hpp>
|
||||
|
||||
#include <DB/Core/Types.h>
|
||||
|
@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <Yandex/MultiVersion.h>
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/MultiVersion.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <statdaemons/RegionsHierarchies.h>
|
||||
#include <statdaemons/TechDataHierarchy.h>
|
||||
#include <statdaemons/RegionsNames.h>
|
||||
|
@ -4,8 +4,8 @@
|
||||
#include <DB/Core/Exception.h>
|
||||
#include <DB/Core/ErrorCodes.h>
|
||||
#include <DB/Common/setThreadName.h>
|
||||
#include <Yandex/MultiVersion.h>
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/MultiVersion.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <Poco/Event.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
#include <DB/Parsers/ASTJoin.h>
|
||||
|
||||
|
@ -29,7 +29,7 @@ public:
|
||||
/** Заменить все довольно длинные однородные OR-цепочки expr = x1 OR ... OR expr = xN
|
||||
* на выражения expr IN (x1, ..., xN).
|
||||
*/
|
||||
void optimizeDisjunctiveEqualityChains();
|
||||
void perform();
|
||||
|
||||
LogicalExpressionsOptimizer(const LogicalExpressionsOptimizer &) = delete;
|
||||
LogicalExpressionsOptimizer & operator=(const LogicalExpressionsOptimizer &) = delete;
|
||||
@ -83,7 +83,6 @@ private:
|
||||
private:
|
||||
ASTSelectQuery * select_query;
|
||||
const Settings & settings;
|
||||
bool hasOptimizedDisjunctiveEqualityChains = false;
|
||||
/// Информация про OR-цепочки внутри запроса.
|
||||
DisjunctiveEqualityChainsMap disjunctive_equality_chains_map;
|
||||
/// Количество обработанных OR-цепочек.
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
#include <Poco/Net/IPAddress.h>
|
||||
|
||||
#include <Yandex/Common.h>
|
||||
#include <common/Common.h>
|
||||
|
||||
#include <DB/Core/Types.h>
|
||||
#include <DB/Core/Exception.h>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
#include <DB/Core/ColumnNumbers.h>
|
||||
#include <DB/Common/Arena.h>
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#include <Poco/SharedPtr.h>
|
||||
|
||||
#include <Yandex/Common.h>
|
||||
#include <common/Common.h>
|
||||
|
||||
#include <DB/Core/Types.h>
|
||||
#include <DB/Core/Exception.h>
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
#include <statdaemons/Increment.h>
|
||||
#include <memory>
|
||||
#include <Yandex/Revision.h>
|
||||
#include <common/Revision.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
#include <DB/Core/Defines.h>
|
||||
#include <DB/Core/Names.h>
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include <Poco/Mutex.h>
|
||||
#include <Poco/RegularExpression.h>
|
||||
#include <Yandex/DateLUT.h>
|
||||
#include <common/DateLUT.h>
|
||||
#include <DB/Core/Types.h>
|
||||
#include <set>
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include <Poco/Mutex.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <DB/Core/Exception.h>
|
||||
#include <DB/Core/ErrorCodes.h>
|
||||
#include <DB/IO/WriteHelpers.h>
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <thread>
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Poco/Event.h>
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <DB/Core/Types.h>
|
||||
#include <thread>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <DB/Storages/IStorage.h>
|
||||
#include <Yandex/singleton.h>
|
||||
#include <common/singleton.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include <Poco/SharedPtr.h>
|
||||
#include <Poco/Util/Application.h>
|
||||
|
||||
#include <Yandex/Revision.h>
|
||||
#include <common/Revision.h>
|
||||
|
||||
#include <statdaemons/Stopwatch.h>
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include <Poco/Net/NetException.h>
|
||||
|
||||
#include <Yandex/Revision.h>
|
||||
#include <common/Revision.h>
|
||||
|
||||
#include <DB/Core/Defines.h>
|
||||
#include <DB/Core/Exception.h>
|
||||
|
@ -205,7 +205,6 @@ void ParallelReplicas::registerReplica(Connection * connection)
|
||||
if (!res.second)
|
||||
throw Exception("Invalid set of connections.", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
if (throttler)
|
||||
connection->setThrottler(throttler);
|
||||
}
|
||||
|
||||
|
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 <Yandex/logger_useful.h>
|
||||
#include <common/likely.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <DB/Core/Exception.h>
|
||||
#include <DB/Common/formatReadable.h>
|
||||
#include <DB/IO/WriteHelpers.h>
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include <string.h>
|
||||
#include <cxxabi.h>
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
#include <DB/IO/WriteHelpers.h>
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include <DB/Storages/StorageLog.h>
|
||||
|
||||
#include <DB/Interpreters/Context.h>
|
||||
#include <Yandex/Revision.h>
|
||||
#include <common/Revision.h>
|
||||
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include <boost/type_traits.hpp>
|
||||
|
||||
#include <Yandex/Common.h>
|
||||
#include <common/Common.h>
|
||||
|
||||
#include <DB/IO/ReadHelpers.h>
|
||||
#include <DB/IO/WriteHelpers.h>
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include <Poco/DirectoryIterator.h>
|
||||
#include <Yandex/Revision.h>
|
||||
#include <common/Revision.h>
|
||||
#include <statdaemons/ext/unlock_guard.hpp>
|
||||
|
||||
#include <DB/Common/SipHash.h>
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <Poco/File.h>
|
||||
#include <Poco/UUIDGenerator.h>
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
#include <DB/Common/Macros.h>
|
||||
#include <DB/Common/escapeForFileName.h>
|
||||
|
@ -114,7 +114,7 @@ void ExpressionAnalyzer::init()
|
||||
InJoinSubqueriesPreprocessor<>(select_query, context, storage).perform();
|
||||
|
||||
/// Оптимизирует логические выражения.
|
||||
LogicalExpressionsOptimizer(select_query, settings).optimizeDisjunctiveEqualityChains();
|
||||
LogicalExpressionsOptimizer(select_query, settings).perform();
|
||||
|
||||
/// Добавляет в множество известных алиасов те, которые объявлены в структуре таблицы (ALIAS-столбцы).
|
||||
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;
|
||||
|
||||
collectDisjunctiveEqualityChains();
|
||||
@ -50,8 +52,6 @@ void LogicalExpressionsOptimizer::optimizeDisjunctiveEqualityChains()
|
||||
cleanupOrExpressions();
|
||||
fixBrokenOrExpressions();
|
||||
}
|
||||
|
||||
hasOptimizedDisjunctiveEqualityChains = true;
|
||||
}
|
||||
|
||||
void LogicalExpressionsOptimizer::collectDisjunctiveEqualityChains()
|
||||
@ -62,7 +62,7 @@ void LogicalExpressionsOptimizer::collectDisjunctiveEqualityChains()
|
||||
using Edge = std::pair<IAST *, IAST *>;
|
||||
std::deque<Edge> to_visit;
|
||||
|
||||
to_visit.push_back(Edge(nullptr, select_query));
|
||||
to_visit.emplace_back(nullptr, select_query);
|
||||
while (!to_visit.empty())
|
||||
{
|
||||
auto edge = to_visit.back();
|
||||
@ -70,7 +70,6 @@ void LogicalExpressionsOptimizer::collectDisjunctiveEqualityChains()
|
||||
auto to_node = edge.second;
|
||||
|
||||
to_visit.pop_back();
|
||||
to_node->attributes |= IAST::IsVisited;
|
||||
|
||||
bool found_chain = false;
|
||||
|
||||
@ -104,6 +103,8 @@ void LogicalExpressionsOptimizer::collectDisjunctiveEqualityChains()
|
||||
}
|
||||
}
|
||||
|
||||
to_node->attributes |= IAST::IsVisited;
|
||||
|
||||
if (found_chain)
|
||||
{
|
||||
if (from_node != nullptr)
|
||||
@ -196,6 +197,14 @@ void LogicalExpressionsOptimizer::addInExpression(const DisjunctiveEqualityChain
|
||||
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
|
||||
ASTPtr equals_expr_lhs;
|
||||
{
|
||||
@ -241,7 +250,7 @@ void LogicalExpressionsOptimizer::cleanupOrExpressions()
|
||||
|
||||
const auto & or_with_expression = chain.first;
|
||||
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 <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
#include <DB/Common/SipHash.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)
|
||||
{
|
||||
Logger * log = &Logger::get("loadMetadata");
|
||||
@ -66,16 +118,15 @@ void loadMetadata(Context & context)
|
||||
/// Здесь хранятся определения таблиц
|
||||
String path = context.getPath() + "metadata";
|
||||
|
||||
struct Table
|
||||
{
|
||||
String database_name;
|
||||
String dir_name;
|
||||
String file_name;
|
||||
};
|
||||
|
||||
using Tables = std::vector<Table>;
|
||||
Tables tables;
|
||||
|
||||
/** Часть таблиц должны быть загружены раньше других, так как используются в конструкторе этих других.
|
||||
* Это таблицы, имя которых начинается на .inner.
|
||||
* NOTE Это довольно криво. Можно сделать лучше.
|
||||
*/
|
||||
Tables tables_to_load_first;
|
||||
|
||||
/// Цикл по базам данных
|
||||
Poco::DirectoryIterator dir_end;
|
||||
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());
|
||||
|
||||
for (const auto & name : file_names)
|
||||
tables.emplace_back(Table{
|
||||
{
|
||||
(0 == name.compare(0, strlen("%2Einner%2E"), "%2Einner%2E")
|
||||
? tables_to_load_first
|
||||
: tables).emplace_back(
|
||||
Table{
|
||||
.database_name = database,
|
||||
.dir_name = it.name(),
|
||||
.file_name = name});
|
||||
}
|
||||
|
||||
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();
|
||||
LOG_INFO(log, "Total " << total_tables << " tables.");
|
||||
|
||||
StopwatchWithLock watch;
|
||||
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());
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
@ -161,34 +218,7 @@ void loadMetadata(Context & context)
|
||||
watch.restart();
|
||||
}
|
||||
|
||||
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();
|
||||
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);
|
||||
}
|
||||
loadTable(context, path, table);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -208,7 +208,7 @@ TestResult check(const TestEntry & entry)
|
||||
settings.optimize_min_equality_disjunction_chain_length = entry.limit;
|
||||
|
||||
DB::LogicalExpressionsOptimizer optimizer(select_query, settings);
|
||||
optimizer.optimizeDisjunctiveEqualityChains();
|
||||
optimizer.perform();
|
||||
|
||||
/// Парсить ожидаемый результат.
|
||||
DB::ASTPtr ast_expected;
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#include <DB/IO/WriteHelpers.h>
|
||||
|
||||
#include <Yandex/DateLUT.h>
|
||||
#include <common/DateLUT.h>
|
||||
#include <strconvert/hash64.h>
|
||||
#include <statdaemons/RegionsHierarchy.h>
|
||||
#include <statdaemons/TechDataHierarchy.h>
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include <Yandex/DateLUT.h>
|
||||
#include <common/DateLUT.h>
|
||||
#include <Poco/DateTimeParser.h>
|
||||
#include <Poco/AutoPtr.h>
|
||||
|
||||
|
@ -11,8 +11,8 @@
|
||||
#include <Poco/RegularExpression.h>
|
||||
#include <Poco/AutoPtr.h>
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <Yandex/Common.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <common/Common.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
@ -5,9 +5,9 @@
|
||||
#include <Poco/Net/DNS.h>
|
||||
#include <Poco/Util/XMLConfiguration.h>
|
||||
|
||||
#include <Yandex/ApplicationServerExt.h>
|
||||
#include <Yandex/ErrorHandlers.h>
|
||||
#include <Yandex/Revision.h>
|
||||
#include <common/ApplicationServerExt.h>
|
||||
#include <common/ErrorHandlers.h>
|
||||
#include <common/Revision.h>
|
||||
|
||||
#include <statdaemons/ConfigProcessor.h>
|
||||
#include <statdaemons/ext/scope_guard.hpp>
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include <Poco/Net/TCPServerConnectionFactory.h>
|
||||
#include <Poco/Net/TCPServerConnection.h>
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <statdaemons/daemon.h>
|
||||
#include <statdaemons/HTMLForm.h>
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include <Poco/Net/NetException.h>
|
||||
|
||||
#include <Yandex/Revision.h>
|
||||
#include <common/Revision.h>
|
||||
|
||||
#include <statdaemons/Stopwatch.h>
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include <DB/Core/ErrorCodes.h>
|
||||
#include <DB/Common/setThreadName.h>
|
||||
#include <DB/IO/WriteHelpers.h>
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.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/unistr.h>
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include <Yandex/DateLUTImpl.h>
|
||||
#include <common/DateLUTImpl.h>
|
||||
#include <Poco/Exception.h>
|
||||
|
||||
#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"
|
||||
|
||||
namespace Revision
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
#include <Yandex/DateLUT.h>
|
||||
#include <common/DateLUT.h>
|
||||
|
||||
|
||||
static std::string toString(time_t Value)
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include <Poco/Exception.h>
|
||||
|
||||
#include <Yandex/DateLUT.h>
|
||||
#include <common/DateLUT.h>
|
||||
|
||||
|
||||
static std::string toString(time_t Value)
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include <iostream>
|
||||
#include <Yandex/DateLUT.h>
|
||||
#include <common/DateLUT.h>
|
||||
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include <Yandex/DateLUT.h>
|
||||
#include <common/DateLUT.h>
|
||||
|
||||
/// Позволяет проверить время инициализации DateLUT.
|
||||
int main(int argc, char ** argv)
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include <iostream>
|
||||
#include <statdaemons/threadpool.hpp>
|
||||
#include <functional>
|
||||
#include <Yandex/MultiVersion.h>
|
||||
#include <common/MultiVersion.h>
|
||||
|
||||
|
||||
typedef std::string T;
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#include <Poco/Util/Application.h>
|
||||
|
||||
#include <Yandex/singleton.h>
|
||||
#include <common/singleton.h>
|
||||
|
||||
#include <mysqlxx/Query.h>
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <Yandex/DateLUT.h>
|
||||
#include <common/DateLUT.h>
|
||||
|
||||
#include <mysqlxx/Exception.h>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <Yandex/DateLUT.h>
|
||||
#include <common/DateLUT.h>
|
||||
|
||||
#include <mysqlxx/Date.h>
|
||||
#include <iomanip>
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include <Poco/Exception.h>
|
||||
#include <Poco/SharedPtr.h>
|
||||
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <statdaemons/daemon.h>
|
||||
|
||||
#include <mysqlxx/Connection.h>
|
||||
|
@ -8,8 +8,8 @@
|
||||
#include <string>
|
||||
#include <limits>
|
||||
|
||||
#include <Yandex/Common.h>
|
||||
#include <Yandex/DateLUT.h>
|
||||
#include <common/Common.h>
|
||||
#include <common/DateLUT.h>
|
||||
|
||||
#include <mysqlxx/Types.h>
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include <pthread.h>
|
||||
#include <Yandex/likely.h>
|
||||
#include <common/likely.h>
|
||||
|
||||
#include <Poco/Ext/ThreadNumber.h>
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include <zkutil/ZooKeeper.h>
|
||||
#include <functional>
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
|
||||
namespace zkutil
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <zkutil/ZooKeeper.h>
|
||||
#include <Yandex/logger_useful.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <DB/Core/Exception.h>
|
||||
|
||||
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