mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-26 01:22:04 +00:00
Merge branch 'master' of github.com:yandex/ClickHouse
This commit is contained in:
commit
2953be89a4
@ -24,3 +24,7 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
set (COMPILER_CLANG 1)
|
||||
endif ()
|
||||
|
||||
if (OS_LINUX AND CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64")
|
||||
set (OS_LINUX_X86_64 1)
|
||||
endif ()
|
||||
|
@ -2,7 +2,9 @@ option (ENABLE_RDKAFKA "Enable kafka" ON)
|
||||
|
||||
if (ENABLE_RDKAFKA)
|
||||
|
||||
option (USE_INTERNAL_RDKAFKA_LIBRARY "Set to FALSE to use system librdkafka instead of the bundled" ${NOT_UNBUNDLED})
|
||||
if (OS_LINUX_X86_64)
|
||||
option (USE_INTERNAL_RDKAFKA_LIBRARY "Set to FALSE to use system librdkafka instead of the bundled" ${NOT_UNBUNDLED})
|
||||
endif ()
|
||||
|
||||
if (USE_INTERNAL_RDKAFKA_LIBRARY AND NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/librdkafka/CMakeLists.txt")
|
||||
message (WARNING "submodule contrib/librdkafka is missing. to fix try run: \n git submodule update --init --recursive")
|
||||
|
@ -1,4 +1,6 @@
|
||||
option (USE_INTERNAL_ZLIB_LIBRARY "Set to FALSE to use system zlib library instead of bundled" ${NOT_UNBUNDLED})
|
||||
if (NOT OS_FREEBSD)
|
||||
option (USE_INTERNAL_ZLIB_LIBRARY "Set to FALSE to use system zlib library instead of bundled" ${NOT_UNBUNDLED})
|
||||
endif ()
|
||||
|
||||
if (NOT USE_INTERNAL_ZLIB_LIBRARY)
|
||||
find_package (ZLIB)
|
||||
|
@ -13,6 +13,7 @@ option (ENABLE_CLICKHOUSE_COMPRESSOR "Enable clickhouse-compressor" ${ENABLE_CLI
|
||||
option (ENABLE_CLICKHOUSE_COPIER "Enable clickhouse-copier" ${ENABLE_CLICKHOUSE_ALL})
|
||||
option (ENABLE_CLICKHOUSE_FORMAT "Enable clickhouse-format" ${ENABLE_CLICKHOUSE_ALL})
|
||||
option (ENABLE_CLICKHOUSE_OBFUSCATOR "Enable clickhouse-obfuscator" ${ENABLE_CLICKHOUSE_ALL})
|
||||
option (ENABLE_CLICKHOUSE_ODBC_BRIDGE "Enable clickhouse-odbc-bridge" ${ENABLE_CLICKHOUSE_ALL})
|
||||
|
||||
configure_file (config_tools.h.in ${CMAKE_CURRENT_BINARY_DIR}/config_tools.h)
|
||||
|
||||
@ -27,10 +28,11 @@ add_subdirectory (copier)
|
||||
add_subdirectory (format)
|
||||
add_subdirectory (clang)
|
||||
add_subdirectory (obfuscator)
|
||||
add_subdirectory (odbc-bridge)
|
||||
|
||||
if (CLICKHOUSE_SPLIT_BINARY)
|
||||
set (CLICKHOUSE_ALL_TARGETS clickhouse-server clickhouse-client clickhouse-local clickhouse-benchmark clickhouse-performance-test
|
||||
clickhouse-extract-from-config clickhouse-format clickhouse-copier)
|
||||
clickhouse-extract-from-config clickhouse-compressor clickhouse-format clickhouse-copier clickhouse-odbc-bridge)
|
||||
|
||||
if (USE_EMBEDDED_COMPILER)
|
||||
list (APPEND CLICKHOUSE_ALL_TARGETS clickhouse-clang clickhouse-lld)
|
||||
@ -83,6 +85,9 @@ else ()
|
||||
if (USE_EMBEDDED_COMPILER)
|
||||
target_link_libraries (clickhouse clickhouse-compiler-lib)
|
||||
endif ()
|
||||
if (ENABLE_CLICKHOUSE_ODBC_BRIDGE)
|
||||
target_link_libraries (clickhouse clickhouse-odbc-bridge-lib)
|
||||
endif()
|
||||
|
||||
set (CLICKHOUSE_BUNDLE)
|
||||
if (ENABLE_CLICKHOUSE_SERVER)
|
||||
@ -135,6 +140,12 @@ else ()
|
||||
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/clickhouse-obfuscator DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT clickhouse)
|
||||
list(APPEND CLICKHOUSE_BUNDLE clickhouse-obfuscator)
|
||||
endif ()
|
||||
if (ENABLE_CLICKHOUSE_ODBC_BRIDGE)
|
||||
add_custom_target (clickhouse-odbc-bridge ALL COMMAND ${CMAKE_COMMAND} -E create_symlink clickhouse clickhouse-odbc-bridge DEPENDS clickhouse)
|
||||
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/clickhouse-odbc-bridge DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT clickhouse)
|
||||
list(APPEND CLICKHOUSE_BUNDLE clickhouse-odbc-bridge)
|
||||
endif ()
|
||||
|
||||
|
||||
# install always because depian package want this files:
|
||||
add_custom_target (clickhouse-clang ALL COMMAND ${CMAKE_COMMAND} -E create_symlink clickhouse clickhouse-clang DEPENDS clickhouse)
|
||||
|
@ -56,6 +56,10 @@ int mainEntryClickHouseClusterCopier(int argc, char ** argv);
|
||||
#if ENABLE_CLICKHOUSE_OBFUSCATOR
|
||||
int mainEntryClickHouseObfuscator(int argc, char ** argv);
|
||||
#endif
|
||||
#if ENABLE_CLICKHOUSE_ODBC_BRIDGE || !defined(ENABLE_CLICKHOUSE_ODBC_BRIDGE)
|
||||
int mainEntryClickHouseODBCBridge(int argc, char ** argv);
|
||||
#endif
|
||||
|
||||
|
||||
#if USE_EMBEDDED_COMPILER
|
||||
int mainEntryClickHouseClang(int argc, char ** argv);
|
||||
@ -101,6 +105,10 @@ std::pair<const char *, MainFunc> clickhouse_applications[] =
|
||||
#if ENABLE_CLICKHOUSE_OBFUSCATOR
|
||||
{"obfuscator", mainEntryClickHouseObfuscator},
|
||||
#endif
|
||||
#if ENABLE_CLICKHOUSE_ODBC_BRIDGE || !defined(ENABLE_CLICKHOUSE_ODBC_BRIDGE)
|
||||
{"odbc-bridge", mainEntryClickHouseODBCBridge},
|
||||
#endif
|
||||
|
||||
#if USE_EMBEDDED_COMPILER
|
||||
{"clang", mainEntryClickHouseClang},
|
||||
{"clang++", mainEntryClickHouseClang},
|
||||
|
13
dbms/programs/odbc-bridge/CMakeLists.txt
Normal file
13
dbms/programs/odbc-bridge/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
add_library (clickhouse-odbc-bridge-lib
|
||||
Handlers.cpp
|
||||
HandlerFactory.cpp
|
||||
ODBCBridge.cpp
|
||||
)
|
||||
|
||||
target_link_libraries (clickhouse-odbc-bridge-lib clickhouse_common_io daemon)
|
||||
target_include_directories (clickhouse-odbc-bridge-lib PUBLIC ${ClickHouse_SOURCE_DIR}/libs/libdaemon/include)
|
||||
|
||||
if (CLICKHOUSE_SPLIT_BINARY)
|
||||
add_executable (clickhouse-odbc-bridge odbc-bridge.cpp)
|
||||
target_link_libraries (clickhouse-odbc-bridge clickhouse-odbc-bridge-lib)
|
||||
endif ()
|
23
dbms/programs/odbc-bridge/HandlerFactory.cpp
Normal file
23
dbms/programs/odbc-bridge/HandlerFactory.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
#include "HandlerFactory.h"
|
||||
#include <Common/HTMLForm.h>
|
||||
|
||||
#include <Poco/Ext/SessionPoolHelpers.h>
|
||||
#include <Poco/Net/HTTPServerRequest.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
Poco::Net::HTTPRequestHandler * HandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest & request)
|
||||
{
|
||||
const auto & uri = request.getURI();
|
||||
LOG_TRACE(log, "Request URI: " + uri);
|
||||
|
||||
if (uri == "/ping" && request.getMethod() == Poco::Net::HTTPRequest::HTTP_GET)
|
||||
return new PingHandler(keep_alive_timeout);
|
||||
|
||||
if (request.getMethod() == Poco::Net::HTTPRequest::HTTP_POST)
|
||||
return new ODBCHandler(pool_map, keep_alive_timeout, context);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
37
dbms/programs/odbc-bridge/HandlerFactory.h
Normal file
37
dbms/programs/odbc-bridge/HandlerFactory.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Poco/Logger.h>
|
||||
#include <Poco/Net/HTTPRequestHandler.h>
|
||||
#include <Poco/Net/HTTPRequestHandlerFactory.h>
|
||||
#include "Handlers.h"
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#include <Poco/Data/SessionPool.h>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
/** Factory for '/ping' and '/' handlers.
|
||||
* Also stores Session pools for ODBC connections
|
||||
*/
|
||||
class HandlerFactory : public Poco::Net::HTTPRequestHandlerFactory
|
||||
{
|
||||
public:
|
||||
HandlerFactory(const std::string & name_, size_t keep_alive_timeout_, std::shared_ptr<Context> context_)
|
||||
: log(&Poco::Logger::get(name_)), name(name_), keep_alive_timeout(keep_alive_timeout_), context(context_)
|
||||
{
|
||||
pool_map = std::make_shared<ODBCHandler::PoolMap>();
|
||||
}
|
||||
|
||||
Poco::Net::HTTPRequestHandler * createRequestHandler(const Poco::Net::HTTPServerRequest & request) override;
|
||||
|
||||
private:
|
||||
Poco::Logger * log;
|
||||
std::string name;
|
||||
size_t keep_alive_timeout;
|
||||
std::shared_ptr<Context> context;
|
||||
std::shared_ptr<ODBCHandler::PoolMap> pool_map;
|
||||
};
|
||||
}
|
146
dbms/programs/odbc-bridge/Handlers.cpp
Normal file
146
dbms/programs/odbc-bridge/Handlers.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
#include "Handlers.h"
|
||||
#include <Common/HTMLForm.h>
|
||||
|
||||
#include <memory>
|
||||
#include <DataStreams/IBlockOutputStream.h>
|
||||
#include <DataStreams/copyData.h>
|
||||
#include <DataTypes/DataTypeFactory.h>
|
||||
#include <Dictionaries/ODBCBlockInputStream.h>
|
||||
#include <Formats/BinaryRowInputStream.h>
|
||||
#include <Formats/FormatFactory.h>
|
||||
#include <IO/ReadBufferFromIStream.h>
|
||||
#include <IO/WriteBufferFromHTTPServerResponse.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
#include <Interpreters/Context.h>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/tokenizer.hpp>
|
||||
#include <Poco/Ext/SessionPoolHelpers.h>
|
||||
#include <Poco/Net/HTTPServerRequest.h>
|
||||
#include <Poco/Net/HTTPServerResponse.h>
|
||||
#include <common/logger_useful.h>
|
||||
namespace DB
|
||||
{
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int BAD_REQUEST_PARAMETER;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
std::unique_ptr<Block> parseColumns(std::string && column_string)
|
||||
{
|
||||
std::unique_ptr<Block> sample_block = std::make_unique<Block>();
|
||||
auto names_and_types = NamesAndTypesList::parse(column_string);
|
||||
for (const NameAndTypePair & column_data : names_and_types)
|
||||
sample_block->insert({column_data.type, column_data.name});
|
||||
return sample_block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ODBCHandler::PoolPtr ODBCHandler::getPool(const std::string & connection_str)
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
if (!pool_map->count(connection_str))
|
||||
{
|
||||
pool_map->emplace(connection_str, createAndCheckResizePocoSessionPool([connection_str] {
|
||||
return std::make_shared<Poco::Data::SessionPool>("ODBC", connection_str);
|
||||
}));
|
||||
}
|
||||
return pool_map->at(connection_str);
|
||||
}
|
||||
|
||||
void ODBCHandler::handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response)
|
||||
{
|
||||
Poco::Net::HTMLForm params(request, request.stream());
|
||||
LOG_TRACE(log, "Request URI: " + request.getURI());
|
||||
|
||||
auto process_error = [&response, this](const std::string & message) {
|
||||
response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR);
|
||||
if (!response.sent())
|
||||
response.send() << message << std::endl;
|
||||
LOG_WARNING(log, message);
|
||||
};
|
||||
|
||||
if (!params.has("query"))
|
||||
{
|
||||
process_error("No 'query' in request body");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!params.has("columns"))
|
||||
{
|
||||
process_error("No 'columns' in request URL");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!params.has("connection_string"))
|
||||
{
|
||||
process_error("No 'connection_string' in request URL");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t max_block_size = DEFAULT_BLOCK_SIZE;
|
||||
if (params.has("max_block_size"))
|
||||
{
|
||||
std::string max_block_size_str = params.get("max_block_size", "");
|
||||
if (max_block_size_str.empty())
|
||||
{
|
||||
process_error("Empty max_block_size specified");
|
||||
return;
|
||||
}
|
||||
max_block_size = parse<size_t>(max_block_size_str);
|
||||
}
|
||||
|
||||
std::string columns = params.get("columns");
|
||||
std::unique_ptr<Block> sample_block;
|
||||
try
|
||||
{
|
||||
sample_block = parseColumns(std::move(columns));
|
||||
}
|
||||
catch (const Exception & ex)
|
||||
{
|
||||
process_error("Invalid 'columns' parameter in request body '" + ex.message() + "'");
|
||||
LOG_WARNING(log, ex.getStackTrace().toString());
|
||||
return;
|
||||
}
|
||||
|
||||
std::string format = params.get("format", "RowBinary");
|
||||
std::string query = params.get("query");
|
||||
LOG_TRACE(log, "Query: " << query);
|
||||
|
||||
std::string connection_string = params.get("connection_string");
|
||||
LOG_TRACE(log, "Connection string: '" << connection_string << "'");
|
||||
|
||||
WriteBufferFromHTTPServerResponse out(request, response, keep_alive_timeout);
|
||||
try
|
||||
{
|
||||
BlockOutputStreamPtr writer = FormatFactory::instance().getOutput(format, out, *sample_block, *context);
|
||||
auto pool = getPool(connection_string);
|
||||
ODBCBlockInputStream inp(pool->get(), query, *sample_block, max_block_size);
|
||||
copyData(inp, *writer);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
auto message = getCurrentExceptionMessage(true);
|
||||
response.setStatusAndReason(
|
||||
Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR); // can't call process_error, bacause of too soon response sending
|
||||
writeStringBinary(message, out);
|
||||
tryLogCurrentException(log);
|
||||
}
|
||||
}
|
||||
|
||||
void PingHandler::handleRequest(Poco::Net::HTTPServerRequest & /*request*/, Poco::Net::HTTPServerResponse & response)
|
||||
{
|
||||
try
|
||||
{
|
||||
setResponseDefaultHeaders(response, keep_alive_timeout);
|
||||
const char * data = "Ok.\n";
|
||||
response.sendBuffer(data, strlen(data));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
tryLogCurrentException("PingHandler");
|
||||
}
|
||||
}
|
||||
}
|
60
dbms/programs/odbc-bridge/Handlers.h
Normal file
60
dbms/programs/odbc-bridge/Handlers.h
Normal file
@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Poco/Logger.h>
|
||||
#include <Poco/Net/HTTPRequestHandler.h>
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#include <Poco/Data/SessionPool.h>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
namespace DB
|
||||
{
|
||||
/** Main handler for requests to ODBC driver
|
||||
* requires connection_string and columns in request params
|
||||
* and also query in request body
|
||||
* response in RowBinary format
|
||||
*/
|
||||
class ODBCHandler : public Poco::Net::HTTPRequestHandler
|
||||
{
|
||||
public:
|
||||
using PoolPtr = std::shared_ptr<Poco::Data::SessionPool>;
|
||||
using PoolMap = std::unordered_map<std::string, PoolPtr>;
|
||||
|
||||
ODBCHandler(std::shared_ptr<PoolMap> pool_map_,
|
||||
size_t keep_alive_timeout_,
|
||||
std::shared_ptr<Context> context_)
|
||||
: log(&Poco::Logger::get("ODBCHandler"))
|
||||
, pool_map(pool_map_)
|
||||
, keep_alive_timeout(keep_alive_timeout_)
|
||||
, context(context_)
|
||||
{
|
||||
}
|
||||
|
||||
void handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response) override;
|
||||
|
||||
private:
|
||||
Poco::Logger * log;
|
||||
|
||||
std::shared_ptr<PoolMap> pool_map;
|
||||
size_t keep_alive_timeout;
|
||||
std::shared_ptr<Context> context;
|
||||
|
||||
static inline std::mutex mutex;
|
||||
|
||||
PoolPtr getPool(const std::string & connection_str);
|
||||
};
|
||||
|
||||
/** Simple ping handler, answers "Ok." to GET request
|
||||
*/
|
||||
class PingHandler : public Poco::Net::HTTPRequestHandler
|
||||
{
|
||||
public:
|
||||
PingHandler(size_t keep_alive_timeout_) : keep_alive_timeout(keep_alive_timeout_) {}
|
||||
void handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response) override;
|
||||
|
||||
private:
|
||||
size_t keep_alive_timeout;
|
||||
};
|
||||
}
|
205
dbms/programs/odbc-bridge/ODBCBridge.cpp
Normal file
205
dbms/programs/odbc-bridge/ODBCBridge.cpp
Normal file
@ -0,0 +1,205 @@
|
||||
#include "ODBCBridge.h"
|
||||
#include "HandlerFactory.h"
|
||||
|
||||
#include <string>
|
||||
#include <errno.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
#include <boost/program_options.hpp>
|
||||
#include <Poco/Net/HTTPServer.h>
|
||||
#include <Poco/Net/NetException.h>
|
||||
#include <Poco/String.h>
|
||||
#include <Poco/Util/HelpFormatter.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <Common/StringUtils/StringUtils.h>
|
||||
#include <Common/config.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <ext/scope_guard.h>
|
||||
#include <ext/range.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int ARGUMENT_OUT_OF_BOUND;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
Poco::Net::SocketAddress makeSocketAddress(const std::string & host, UInt16 port, Poco::Logger * log)
|
||||
{
|
||||
Poco::Net::SocketAddress socket_address;
|
||||
try
|
||||
{
|
||||
socket_address = Poco::Net::SocketAddress(host, port);
|
||||
}
|
||||
catch (const Poco::Net::DNSException & e)
|
||||
{
|
||||
const auto code = e.code();
|
||||
if (code == EAI_FAMILY
|
||||
#if defined(EAI_ADDRFAMILY)
|
||||
|| code == EAI_ADDRFAMILY
|
||||
#endif
|
||||
)
|
||||
{
|
||||
LOG_ERROR(log,
|
||||
"Cannot resolve listen_host (" << host << "), error " << e.code() << ": " << e.message()
|
||||
<< ". "
|
||||
"If it is an IPv6 address and your host has disabled IPv6, then consider to "
|
||||
"specify IPv4 address to listen in <listen_host> element of configuration "
|
||||
"file. Example: <listen_host>0.0.0.0</listen_host>");
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
return socket_address;
|
||||
}
|
||||
|
||||
Poco::Net::SocketAddress socketBindListen(Poco::Net::ServerSocket & socket, const std::string & host, UInt16 port, Poco::Logger * log)
|
||||
{
|
||||
auto address = makeSocketAddress(host, port, log);
|
||||
#if POCO_VERSION < 0x01080000
|
||||
socket.bind(address, /* reuseAddress = */ true);
|
||||
#else
|
||||
socket.bind(address, /* reuseAddress = */ true, /* reusePort = */ false);
|
||||
#endif
|
||||
|
||||
socket.listen(/* backlog = */ 64);
|
||||
|
||||
return address;
|
||||
};
|
||||
}
|
||||
|
||||
void ODBCBridge::handleHelp(const std::string &, const std::string &)
|
||||
{
|
||||
Poco::Util::HelpFormatter helpFormatter(options());
|
||||
helpFormatter.setCommand(commandName());
|
||||
helpFormatter.setHeader("HTTP-proxy for odbc requests");
|
||||
helpFormatter.setUsage("--http-port <port>");
|
||||
helpFormatter.format(std::cerr);
|
||||
|
||||
stopOptionsProcessing();
|
||||
}
|
||||
|
||||
|
||||
void ODBCBridge::defineOptions(Poco::Util::OptionSet & options)
|
||||
{
|
||||
options.addOption(Poco::Util::Option("http-port", "", "port to listen").argument("http-port", true).binding("http-port"));
|
||||
options.addOption(
|
||||
Poco::Util::Option("listen-host", "", "hostname to listen, default localhost").argument("listen-host").binding("listen-host"));
|
||||
options.addOption(
|
||||
Poco::Util::Option("http-timeout", "", "http timout for socket, default 1800").argument("http-timeout").binding("http-timeout"));
|
||||
|
||||
options.addOption(Poco::Util::Option("max-server-connections", "", "max connections to server, default 1024")
|
||||
.argument("max-server-connections")
|
||||
.binding("max-server-connections"));
|
||||
options.addOption(Poco::Util::Option("keep-alive-timeout", "", "keepalive timeout, default 10")
|
||||
.argument("keep-alive-timeout")
|
||||
.binding("keep-alive-timeout"));
|
||||
|
||||
options.addOption(Poco::Util::Option("log-level", "", "sets log level, default info").argument("log-level").binding("logger.level"));
|
||||
|
||||
options.addOption(
|
||||
Poco::Util::Option("log-path", "", "log path for all logs, default console").argument("log-path").binding("logger.log"));
|
||||
|
||||
options.addOption(Poco::Util::Option("err-log-path", "", "err log path for all logs, default no")
|
||||
.argument("err-log-path")
|
||||
.binding("logger.errorlog"));
|
||||
|
||||
using Me = std::decay_t<decltype(*this)>;
|
||||
options.addOption(Poco::Util::Option("help", "", "produce this help message")
|
||||
.binding("help")
|
||||
.callback(Poco::Util::OptionCallback<Me>(this, &Me::handleHelp)));
|
||||
|
||||
ServerApplication::defineOptions(options); /// Don't need complex BaseDaemon's .xml config
|
||||
}
|
||||
|
||||
void ODBCBridge::initialize(Application & self)
|
||||
{
|
||||
BaseDaemon::closeFDs();
|
||||
is_help = config().has("help");
|
||||
|
||||
if (is_help)
|
||||
return;
|
||||
|
||||
if (!config().has("logger.log"))
|
||||
config().setBool("logger.console", true);
|
||||
|
||||
config().setString("logger", "ODBCBridge");
|
||||
|
||||
buildLoggers(config());
|
||||
log = &logger();
|
||||
hostname = config().getString("listen-host", "localhost");
|
||||
port = config().getUInt("http-port");
|
||||
if (port > 0xFFFF)
|
||||
throw Exception("Out of range 'http-port': " + std::to_string(port), ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||
|
||||
http_timeout = config().getUInt("http-timeout", DEFAULT_HTTP_READ_BUFFER_TIMEOUT);
|
||||
max_server_connections = config().getUInt("max-server-connections", 1024);
|
||||
keep_alive_timeout = config().getUInt("keep-alive-timeout", 10);
|
||||
|
||||
initializeTerminationAndSignalProcessing();
|
||||
|
||||
ServerApplication::initialize(self);
|
||||
}
|
||||
|
||||
void ODBCBridge::uninitialize()
|
||||
{
|
||||
BaseDaemon::uninitialize();
|
||||
}
|
||||
|
||||
int ODBCBridge::main(const std::vector<std::string> & /*args*/)
|
||||
{
|
||||
if (is_help)
|
||||
return Application::EXIT_OK;
|
||||
|
||||
LOG_INFO(log, "Starting up");
|
||||
Poco::Net::ServerSocket socket;
|
||||
auto address = socketBindListen(socket, hostname, port, log);
|
||||
socket.setReceiveTimeout(http_timeout);
|
||||
socket.setSendTimeout(http_timeout);
|
||||
Poco::ThreadPool server_pool(3, max_server_connections);
|
||||
Poco::Net::HTTPServerParams::Ptr http_params = new Poco::Net::HTTPServerParams;
|
||||
http_params->setTimeout(http_timeout);
|
||||
http_params->setKeepAliveTimeout(keep_alive_timeout);
|
||||
|
||||
context = std::make_shared<Context>(Context::createGlobal());
|
||||
context->setGlobalContext(*context);
|
||||
|
||||
auto server = Poco::Net::HTTPServer(
|
||||
new HandlerFactory("ODBCRequestHandlerFactory-factory", keep_alive_timeout, context), server_pool, socket, http_params);
|
||||
server.start();
|
||||
|
||||
LOG_INFO(log, "Listening http://" + address.toString());
|
||||
|
||||
SCOPE_EXIT({
|
||||
LOG_DEBUG(log, "Received termination signal.");
|
||||
LOG_DEBUG(log, "Waiting for current connections to close.");
|
||||
server.stop();
|
||||
for (size_t count : ext::range(1, 6))
|
||||
{
|
||||
if (server.currentConnections() == 0)
|
||||
break;
|
||||
LOG_DEBUG(log, "Waiting for " << server.currentConnections() << " connections, try " << count);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
}
|
||||
});
|
||||
|
||||
waitForTerminationRequest();
|
||||
return Application::EXIT_OK;
|
||||
}
|
||||
}
|
||||
|
||||
int mainEntryClickHouseODBCBridge(int argc, char ** argv)
|
||||
{
|
||||
DB::ODBCBridge app;
|
||||
try
|
||||
{
|
||||
return app.run(argc, argv);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cerr << DB::getCurrentExceptionMessage(true) << "\n";
|
||||
auto code = DB::getCurrentExceptionCode();
|
||||
return code ? code : 1;
|
||||
}
|
||||
}
|
41
dbms/programs/odbc-bridge/ODBCBridge.h
Normal file
41
dbms/programs/odbc-bridge/ODBCBridge.h
Normal file
@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Poco/Logger.h>
|
||||
#include <daemon/BaseDaemon.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
/** Class represents clickhouse-odbc-bridge server, which listen
|
||||
* incoming HTTP POST and GET requests on specified port and host.
|
||||
* Has two handlers '/' for all incoming POST requests to ODBC driver
|
||||
* and /ping for GET request about service status
|
||||
*/
|
||||
class ODBCBridge : public BaseDaemon
|
||||
{
|
||||
public:
|
||||
void defineOptions(Poco::Util::OptionSet & options) override;
|
||||
|
||||
protected:
|
||||
void initialize(Application & self) override;
|
||||
|
||||
void uninitialize() override;
|
||||
|
||||
int main(const std::vector<std::string> & args) override;
|
||||
|
||||
private:
|
||||
void handleHelp(const std::string &, const std::string &);
|
||||
|
||||
bool is_help;
|
||||
std::string hostname;
|
||||
size_t port;
|
||||
size_t http_timeout;
|
||||
std::string log_level;
|
||||
size_t max_server_connections;
|
||||
size_t keep_alive_timeout;
|
||||
|
||||
Poco::Logger * log;
|
||||
|
||||
std::shared_ptr<Context> context; /// need for settings only
|
||||
};
|
||||
}
|
38
dbms/programs/odbc-bridge/README.md
Normal file
38
dbms/programs/odbc-bridge/README.md
Normal file
@ -0,0 +1,38 @@
|
||||
# clickhouse-odbc-bridge
|
||||
|
||||
Simple HTTP-server which works like a proxy for ODBC driver. The main motivation
|
||||
was possible segfaults or another faults in ODBC implementations, which can
|
||||
crash whole clickhouse-server process.
|
||||
|
||||
This tool works via HTTP, not via pipes, shared memory, or TCP because:
|
||||
- It's simplier to implement
|
||||
- It's simplier to debug
|
||||
- jdbc-bridge can be implemented in the same way
|
||||
|
||||
## Usage
|
||||
|
||||
`clickhouse-server` use this tool inside odbc table function and StorageODBC.
|
||||
However it can be used as standalone tool from command line with the following
|
||||
parameters in POST-request URL:
|
||||
- `connection_string` -- ODBC connection string.
|
||||
- `columns` -- columns in ClickHouse NamesAndTypesList format, name in backticks,
|
||||
type as string. Name and type are space separated, rows separated with
|
||||
newline.
|
||||
- `max_block_size` -- optional parameter, sets maximum size of single block.
|
||||
Query is send in post body. Response is returned in RowBinary format.
|
||||
|
||||
## Example:
|
||||
|
||||
```bash
|
||||
$ clickhouse-odbc-bridge --http-port 9018 --daemon
|
||||
|
||||
$ curl -d "query=SELECT PageID, ImpID, AdType FROM Keys ORDER BY PageID, ImpID" --data-urlencode "connection_string=DSN=ClickHouse;DATABASE=stat" --data-urlencode "columns=columns format version: 1
|
||||
3 columns:
|
||||
\`PageID\` String
|
||||
\`ImpID\` String
|
||||
\`AdType\` String
|
||||
" "http://localhost:9018/" > result.txt
|
||||
|
||||
$ cat result.txt
|
||||
12246623837185725195925621517
|
||||
```
|
2
dbms/programs/odbc-bridge/odbc-bridge.cpp
Normal file
2
dbms/programs/odbc-bridge/odbc-bridge.cpp
Normal file
@ -0,0 +1,2 @@
|
||||
int mainEntryClickHouseODBCBridge(int argc, char ** argv);
|
||||
int main(int argc_, char ** argv_) { return mainEntryClickHouseODBCBridge(argc_, argv_); }
|
@ -327,6 +327,11 @@ template class ColumnVector<Int8>;
|
||||
template class ColumnVector<Int16>;
|
||||
template class ColumnVector<Int32>;
|
||||
template class ColumnVector<Int64>;
|
||||
template class ColumnVector<Int128>;
|
||||
template class ColumnVector<Float32>;
|
||||
template class ColumnVector<Float64>;
|
||||
|
||||
template class ColumnVector<Dec32>;
|
||||
template class ColumnVector<Dec64>;
|
||||
template class ColumnVector<Dec128>;
|
||||
}
|
||||
|
@ -8,6 +8,12 @@
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int LOGICAL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/** Stuff for comparing numbers.
|
||||
* Integer values are compared as usual.
|
||||
* Floating-point numbers are compared this way that NaNs always end up at the end
|
||||
@ -117,22 +123,69 @@ template <> inline UInt64 unionCastToUInt64(Float32 x)
|
||||
}
|
||||
|
||||
|
||||
/// PaddedPODArray extended by Decimal scale
|
||||
template <typename T, size_t INITIAL_SIZE = 4096>
|
||||
class DecPaddedPODArray : public PODArray<T, INITIAL_SIZE, Allocator<false>, sizeof(T)-1>
|
||||
{
|
||||
public:
|
||||
using Base = PODArray<T, INITIAL_SIZE, Allocator<false>, sizeof(T)-1>;
|
||||
using Base::operator[];
|
||||
|
||||
DecPaddedPODArray()
|
||||
{}
|
||||
|
||||
DecPaddedPODArray(size_t n)
|
||||
: Base(n)
|
||||
{}
|
||||
|
||||
DecPaddedPODArray(size_t n, const T & x)
|
||||
: Base(n, x)
|
||||
{}
|
||||
|
||||
DecPaddedPODArray(typename Base::const_iterator from_begin, typename Base::const_iterator from_end)
|
||||
: Base(from_begin, from_end)
|
||||
{}
|
||||
|
||||
DecPaddedPODArray(std::initializer_list<T> il)
|
||||
: DecPaddedPODArray(std::begin(il), std::end(il))
|
||||
{}
|
||||
|
||||
DecPaddedPODArray(DecPaddedPODArray && other)
|
||||
{
|
||||
this->swap(other);
|
||||
std::swap(scale, other.scale);
|
||||
}
|
||||
|
||||
DecPaddedPODArray & operator= (DecPaddedPODArray && other)
|
||||
{
|
||||
this->swap(other);
|
||||
std::swap(scale, other.scale);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void setScale(UInt32 s) { scale = s; }
|
||||
UInt32 getScale() const { return scale; }
|
||||
|
||||
private:
|
||||
UInt32 scale = std::numeric_limits<UInt32>::max();
|
||||
};
|
||||
|
||||
|
||||
/** A template for columns that use a simple array to store.
|
||||
*/
|
||||
*/
|
||||
template <typename T>
|
||||
class ColumnVector final : public COWPtrHelper<IColumn, ColumnVector<T>>
|
||||
{
|
||||
private:
|
||||
friend class COWPtrHelper<IColumn, ColumnVector<T>>;
|
||||
|
||||
using Self = ColumnVector<T>;
|
||||
friend class COWPtrHelper<IColumn, Self>;
|
||||
|
||||
struct less;
|
||||
struct greater;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
using Container = PaddedPODArray<value_type>;
|
||||
using Container = std::conditional_t<decTrait<T>(), DecPaddedPODArray<value_type>, PaddedPODArray<value_type>>;
|
||||
|
||||
private:
|
||||
ColumnVector() {}
|
||||
@ -216,12 +269,20 @@ public:
|
||||
|
||||
Field operator[](size_t n) const override
|
||||
{
|
||||
return typename NearestFieldType<T>::Type(data[n]);
|
||||
if constexpr (decTrait<T>())
|
||||
{
|
||||
UInt32 scale = data.getScale();
|
||||
if (scale == std::numeric_limits<UInt32>::max())
|
||||
throw Exception("Extracting Decimal field with unknown scale. Scale is lost.", ErrorCodes::LOGICAL_ERROR);
|
||||
return DecField(data[n], scale);
|
||||
}
|
||||
else
|
||||
return typename NearestFieldType<T>::Type(data[n]);
|
||||
}
|
||||
|
||||
void get(size_t n, Field & res) const override
|
||||
{
|
||||
res = typename NearestFieldType<T>::Type(data[n]);
|
||||
res = (*this)[n];
|
||||
}
|
||||
|
||||
UInt64 get64(size_t n) const override;
|
||||
|
@ -381,6 +381,10 @@ namespace ErrorCodes
|
||||
extern const int BAD_ODBC_CONNECTION_STRING = 404;
|
||||
extern const int PARTITION_SIZE_EXCEEDS_MAX_DROP_SIZE_LIMIT = 405;
|
||||
extern const int TOP_AND_LIMIT_TOGETHER = 406;
|
||||
extern const int DECIMAL_OVERFLOW = 407;
|
||||
extern const int BAD_REQUEST_PARAMETER = 408;
|
||||
extern const int EXTERNAL_EXECUTABLE_NOT_FOUND = 409;
|
||||
extern const int EXTERNAL_SERVER_IS_NOT_RESPONDING = 410;
|
||||
|
||||
extern const int KEEPER_EXCEPTION = 999;
|
||||
extern const int POCO_EXCEPTION = 1000;
|
||||
|
@ -47,3 +47,11 @@ std::enable_if_t<std::is_class_v<T>, T> NaNOrZero()
|
||||
{
|
||||
return T{};
|
||||
}
|
||||
|
||||
#if 1 /// __int128
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_same_v<T, __int128>, __int128> NaNOrZero()
|
||||
{
|
||||
return __int128(0);
|
||||
}
|
||||
#endif
|
||||
|
106
dbms/src/Common/ODBCBridgeHelper.cpp
Normal file
106
dbms/src/Common/ODBCBridgeHelper.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
#include <Common/ODBCBridgeHelper.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <Common/validateODBCConnectionString.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
#include <IO/ReadWriteBufferFromHTTP.h>
|
||||
#include <Poco/Path.h>
|
||||
#include <Poco/Util/AbstractConfiguration.h>
|
||||
#include <Common/ShellCommand.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <ext/range.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int EXTERNAL_SERVER_IS_NOT_RESPONDING;
|
||||
}
|
||||
ODBCBridgeHelper::ODBCBridgeHelper(const Context & context_global_, const std::string & connection_string_)
|
||||
: context_global(context_global_), connection_string(validateODBCConnectionString(connection_string_))
|
||||
{
|
||||
const auto & config = context_global.getConfigRef();
|
||||
size_t bridge_port = config.getUInt("odbc_bridge.port", DEFAULT_PORT);
|
||||
std::string bridge_host = config.getString("odbc_bridge.host", DEFAULT_HOST);
|
||||
|
||||
ping_url.setHost(bridge_host);
|
||||
ping_url.setPort(bridge_port);
|
||||
ping_url.setScheme("http");
|
||||
ping_url.setPath(PING_HANDLER);
|
||||
}
|
||||
void ODBCBridgeHelper::startODBCBridge() const
|
||||
{
|
||||
const auto & config = context_global.getConfigRef();
|
||||
const auto & settings = context_global.getSettingsRef();
|
||||
Poco::Path path{config.getString("application.dir", "")};
|
||||
path.setFileName("clickhouse-odbc-bridge");
|
||||
|
||||
if (!path.isFile())
|
||||
throw Exception("clickhouse-odbc-bridge is not found", ErrorCodes::EXTERNAL_EXECUTABLE_NOT_FOUND);
|
||||
|
||||
std::stringstream command;
|
||||
command << path.toString() << ' ';
|
||||
command << "--http-port " << config.getUInt("odbc_bridge.port", DEFAULT_PORT) << ' ';
|
||||
command << "--listen-host " << config.getString("odbc_bridge.listen_host", DEFAULT_HOST) << ' ';
|
||||
command << "--http-timeout " << settings.http_receive_timeout.value.totalSeconds() << ' ';
|
||||
if (config.has("logger.odbc_bridge_log"))
|
||||
command << "--log-path " << config.getString("logger.odbc_bridge_log") << ' ';
|
||||
if (config.has("logger.odbc_bridge_errlog"))
|
||||
command << "--err-log-path " << config.getString("logger.odbc_bridge_errlog") << ' ';
|
||||
if (config.has("logger.odbc_bridge_level"))
|
||||
command << "--log-level " << config.getString("logger.odbc_bridge_level") << ' ';
|
||||
command << "&"; /// we don't want to wait this process
|
||||
|
||||
auto command_str = command.str();
|
||||
LOG_TRACE(log, "Starting clickhouse-odbc-bridge with command: " << command_str);
|
||||
|
||||
auto cmd = ShellCommand::execute(command_str);
|
||||
cmd->wait();
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> ODBCBridgeHelper::getURLParams(const NamesAndTypesList & cols, size_t max_block_size) const
|
||||
{
|
||||
std::vector<std::pair<std::string, std::string>> result;
|
||||
|
||||
result.emplace_back("connection_string", connection_string); /// already validated
|
||||
result.emplace_back("columns", cols.toString());
|
||||
result.emplace_back("max_block_size", std::to_string(max_block_size));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ODBCBridgeHelper::checkODBCBridgeIsRunning() const
|
||||
{
|
||||
try
|
||||
{
|
||||
ReadWriteBufferFromHTTP buf(ping_url, ODBCBridgeHelper::PING_METHOD, nullptr);
|
||||
return checkString(ODBCBridgeHelper::PING_OK_ANSWER, buf);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ODBCBridgeHelper::startODBCBridgeSync() const
|
||||
{
|
||||
if (!checkODBCBridgeIsRunning())
|
||||
{
|
||||
LOG_TRACE(log, "clickhouse-odbc-bridge is not running, will try to start it");
|
||||
startODBCBridge();
|
||||
bool started = false;
|
||||
for (size_t counter : ext::range(1, 20))
|
||||
{
|
||||
LOG_TRACE(log, "Checking clickhouse-odbc-bridge is running, try " << counter);
|
||||
if (checkODBCBridgeIsRunning())
|
||||
{
|
||||
started = true;
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
if (!started)
|
||||
throw Exception("ODBCBridgeHelper: clickhouse-odbc-bridge is not responding", ErrorCodes::EXTERNAL_SERVER_IS_NOT_RESPONDING);
|
||||
}
|
||||
}
|
||||
}
|
47
dbms/src/Common/ODBCBridgeHelper.h
Normal file
47
dbms/src/Common/ODBCBridgeHelper.h
Normal file
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Poco/Logger.h>
|
||||
#include <Poco/Net/HTTPRequest.h>
|
||||
#include <Poco/URI.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int EXTERNAL_EXECUTABLE_NOT_FOUND;
|
||||
}
|
||||
/** Helper for odbc-bridge, provide utility methods, not main request
|
||||
*/
|
||||
class ODBCBridgeHelper
|
||||
{
|
||||
private:
|
||||
const Context & context_global;
|
||||
|
||||
std::string connection_string;
|
||||
|
||||
Poco::URI ping_url;
|
||||
|
||||
Poco::Logger * log = &Poco::Logger::get("ODBCBridgeHelper");
|
||||
|
||||
public:
|
||||
static constexpr inline size_t DEFAULT_PORT = 9018;
|
||||
|
||||
static constexpr inline auto DEFAULT_HOST = "localhost";
|
||||
static constexpr inline auto DEFAULT_FORMAT = "RowBinary";
|
||||
static constexpr inline auto PING_HANDLER = "/ping";
|
||||
static constexpr inline auto MAIN_HANDLER = "/";
|
||||
static constexpr inline auto PING_OK_ANSWER = "Ok.";
|
||||
|
||||
static const inline std::string PING_METHOD = Poco::Net::HTTPRequest::HTTP_GET;
|
||||
static const inline std::string MAIN_METHOD = Poco::Net::HTTPRequest::HTTP_POST;
|
||||
|
||||
ODBCBridgeHelper(const Context & context_global_, const std::string & connection_string_);
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> getURLParams(const NamesAndTypesList & cols, size_t max_block_size) const;
|
||||
bool checkODBCBridgeIsRunning() const;
|
||||
|
||||
void startODBCBridge() const;
|
||||
void startODBCBridgeSync() const;
|
||||
};
|
||||
}
|
@ -65,6 +65,7 @@ template <typename T> bool inline operator< (T a, const UInt128 b) { return UIn
|
||||
|
||||
template <> constexpr bool IsNumber<UInt128> = true;
|
||||
template <> struct TypeName<UInt128> { static const char * get() { return "UInt128"; } };
|
||||
template <> struct TypeNumber<UInt128> { static constexpr const size_t value = 5; };
|
||||
|
||||
struct UInt128Hash
|
||||
{
|
||||
|
@ -71,3 +71,6 @@ target_link_libraries (cow_columns clickhouse_common_io)
|
||||
|
||||
add_executable (stopwatch stopwatch.cpp)
|
||||
target_link_libraries (stopwatch clickhouse_common_io)
|
||||
|
||||
add_executable (validate-odbc-connection-string validate-odbc-connection-string.cpp)
|
||||
target_link_libraries (validate-odbc-connection-string dbms)
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include <iostream>
|
||||
#include <Common/Exception.h>
|
||||
#include <Dictionaries/validateODBCConnectionString.h>
|
||||
#include <Common/validateODBCConnectionString.h>
|
||||
|
||||
|
||||
using namespace DB;
|
@ -5,7 +5,7 @@
|
||||
#include <common/find_first_symbols.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <Common/StringUtils/StringUtils.h>
|
||||
#include <Dictionaries/validateODBCConnectionString.h>
|
||||
#include <Common/validateODBCConnectionString.h>
|
||||
|
||||
|
||||
namespace DB
|
@ -139,7 +139,6 @@ inline bool_if_double_can_be_used<TAInt, TAFloat> equalsOpTmpl(TAFloat a, TAInt
|
||||
return static_cast<double>(a) == static_cast<double>(b);
|
||||
}
|
||||
|
||||
|
||||
/* Final realiztions */
|
||||
|
||||
|
||||
@ -333,6 +332,15 @@ inline bool equalsOp<DB::Float32, DB::UInt128>(DB::Float32 f, DB::UInt128 u)
|
||||
return equalsOp(static_cast<DB::Float64>(f), u);
|
||||
}
|
||||
|
||||
inline bool greaterOp(DB::Int128 i, DB::Float64 f) { return static_cast<DB::Int128>(f) < i; }
|
||||
inline bool greaterOp(DB::Int128 i, DB::Float32 f) { return static_cast<DB::Int128>(f) < i; }
|
||||
inline bool greaterOp(DB::Float64 f, DB::Int128 i) { return static_cast<DB::Int128>(f) > i; }
|
||||
inline bool greaterOp(DB::Float32 f, DB::Int128 i) { return static_cast<DB::Int128>(f) > i; }
|
||||
|
||||
inline bool equalsOp(DB::Int128 i, DB::Float64 f) { return i == static_cast<DB::Int128>(f) && static_cast<DB::Float64>(i) == f; }
|
||||
inline bool equalsOp(DB::Int128 i, DB::Float32 f) { return i == static_cast<DB::Int128>(f) && static_cast<DB::Float32>(i) == f; }
|
||||
inline bool equalsOp(DB::Float64 f, DB::Int128 i) { return equalsOp(i, f); }
|
||||
inline bool equalsOp(DB::Float32 f, DB::Int128 i) { return equalsOp(i, f); }
|
||||
|
||||
template <typename A, typename B>
|
||||
inline bool_if_not_safe_conversion<A, B> notEqualsOp(A a, B b)
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <Core/Field.h>
|
||||
#include <Common/FieldVisitors.h>
|
||||
#include <Functions/FunctionsComparison.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -270,4 +271,23 @@ namespace DB
|
||||
DB::String res = applyVisitor(DB::FieldVisitorToString(), DB::Field(x));
|
||||
buf.write(res.data(), res.size());
|
||||
}
|
||||
|
||||
|
||||
bool DecField::operator < (const DecField & r) const
|
||||
{
|
||||
using Comparator = DecimalComparison<Dec128, Dec128, LessOp>;
|
||||
return Comparator::compare(Dec128(dec), Dec128(r.dec), scale, r.scale);
|
||||
}
|
||||
|
||||
bool DecField::operator <= (const DecField & r) const
|
||||
{
|
||||
using Comparator = DecimalComparison<Dec128, Dec128, LessOrEqualsOp>;
|
||||
return Comparator::compare(Dec128(dec), Dec128(r.dec), scale, r.scale);
|
||||
}
|
||||
|
||||
bool DecField::operator == (const DecField & r) const
|
||||
{
|
||||
using Comparator = DecimalComparison<Dec128, Dec128, EqualsOp>;
|
||||
return Comparator::compare(Dec128(dec), Dec128(r.dec), scale, r.scale);
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,35 @@ using TupleBackend = std::vector<Field>;
|
||||
STRONG_TYPEDEF(TupleBackend, Tuple) /// Array and Tuple are different types with equal representation inside Field.
|
||||
|
||||
|
||||
class DecField
|
||||
{
|
||||
public:
|
||||
static constexpr UInt32 wrongScale() { return std::numeric_limits<UInt32>::max(); }
|
||||
|
||||
DecField(Int128 value, UInt32 scale_ = wrongScale())
|
||||
: dec(value),
|
||||
scale(scale_)
|
||||
{}
|
||||
|
||||
operator Dec32() const { return dec; }
|
||||
operator Dec64() const { return dec; }
|
||||
operator Dec128() const { return dec; }
|
||||
|
||||
UInt32 getScale() const { return scale; }
|
||||
|
||||
bool operator < (const DecField & r) const;
|
||||
bool operator <= (const DecField & r) const;
|
||||
bool operator == (const DecField & r) const;
|
||||
|
||||
bool operator > (const DecField & r) const { return r < *this; }
|
||||
bool operator >= (const DecField & r) const { return r <= * this; }
|
||||
bool operator != (const DecField & r) const { return !(*this == r); }
|
||||
|
||||
private:
|
||||
Int128 dec;
|
||||
UInt32 scale;
|
||||
};
|
||||
|
||||
/** 32 is enough. Round number is used for alignment and for better arithmetic inside std::vector.
|
||||
* NOTE: Actually, sizeof(std::string) is 32 when using libc++, so Field is 40 bytes.
|
||||
*/
|
||||
@ -55,12 +84,14 @@ public:
|
||||
Int64 = 2,
|
||||
Float64 = 3,
|
||||
UInt128 = 4,
|
||||
Int128 = 5,
|
||||
|
||||
/// Non-POD types.
|
||||
|
||||
String = 16,
|
||||
Array = 17,
|
||||
Tuple = 18,
|
||||
Decimal = 19,
|
||||
};
|
||||
|
||||
static const int MIN_NON_POD = 16;
|
||||
@ -73,14 +104,15 @@ public:
|
||||
case UInt64: return "UInt64";
|
||||
case UInt128: return "UInt128";
|
||||
case Int64: return "Int64";
|
||||
case Int128: return "Int128";
|
||||
case Float64: return "Float64";
|
||||
case String: return "String";
|
||||
case Array: return "Array";
|
||||
case Tuple: return "Tuple";
|
||||
|
||||
default:
|
||||
throw Exception("Bad type of Field", ErrorCodes::BAD_TYPE_OF_FIELD);
|
||||
case Decimal: return "Decimal";
|
||||
}
|
||||
|
||||
throw Exception("Bad type of Field", ErrorCodes::BAD_TYPE_OF_FIELD);
|
||||
}
|
||||
};
|
||||
|
||||
@ -257,14 +289,15 @@ public:
|
||||
case Types::UInt64: return get<UInt64>() < rhs.get<UInt64>();
|
||||
case Types::UInt128: return get<UInt128>() < rhs.get<UInt128>();
|
||||
case Types::Int64: return get<Int64>() < rhs.get<Int64>();
|
||||
case Types::Int128: return get<Int128>() < rhs.get<Int128>();
|
||||
case Types::Float64: return get<Float64>() < rhs.get<Float64>();
|
||||
case Types::String: return get<String>() < rhs.get<String>();
|
||||
case Types::Array: return get<Array>() < rhs.get<Array>();
|
||||
case Types::Tuple: return get<Tuple>() < rhs.get<Tuple>();
|
||||
|
||||
default:
|
||||
throw Exception("Bad type of Field", ErrorCodes::BAD_TYPE_OF_FIELD);
|
||||
case Types::Decimal: return get<DecField>() < rhs.get<DecField>();
|
||||
}
|
||||
|
||||
throw Exception("Bad type of Field", ErrorCodes::BAD_TYPE_OF_FIELD);
|
||||
}
|
||||
|
||||
bool operator> (const Field & rhs) const
|
||||
@ -285,15 +318,15 @@ public:
|
||||
case Types::UInt64: return get<UInt64>() <= rhs.get<UInt64>();
|
||||
case Types::UInt128: return get<UInt128>() <= rhs.get<UInt128>();
|
||||
case Types::Int64: return get<Int64>() <= rhs.get<Int64>();
|
||||
case Types::Int128: return get<Int128>() <= rhs.get<Int128>();
|
||||
case Types::Float64: return get<Float64>() <= rhs.get<Float64>();
|
||||
case Types::String: return get<String>() <= rhs.get<String>();
|
||||
case Types::Array: return get<Array>() <= rhs.get<Array>();
|
||||
case Types::Tuple: return get<Tuple>() <= rhs.get<Tuple>();
|
||||
|
||||
|
||||
default:
|
||||
throw Exception("Bad type of Field", ErrorCodes::BAD_TYPE_OF_FIELD);
|
||||
case Types::Decimal: return get<DecField>() <= rhs.get<DecField>();
|
||||
}
|
||||
|
||||
throw Exception("Bad type of Field", ErrorCodes::BAD_TYPE_OF_FIELD);
|
||||
}
|
||||
|
||||
bool operator>= (const Field & rhs) const
|
||||
@ -316,10 +349,11 @@ public:
|
||||
case Types::Array: return get<Array>() == rhs.get<Array>();
|
||||
case Types::Tuple: return get<Tuple>() == rhs.get<Tuple>();
|
||||
case Types::UInt128: return get<UInt128>() == rhs.get<UInt128>();
|
||||
|
||||
default:
|
||||
throw Exception("Bad type of Field", ErrorCodes::BAD_TYPE_OF_FIELD);
|
||||
case Types::Int128: return get<Int128>() == rhs.get<Int128>();
|
||||
case Types::Decimal: return get<DecField>() == rhs.get<DecField>();
|
||||
}
|
||||
|
||||
throw Exception("Bad type of Field", ErrorCodes::BAD_TYPE_OF_FIELD);
|
||||
}
|
||||
|
||||
bool operator!= (const Field & rhs) const
|
||||
@ -329,7 +363,7 @@ public:
|
||||
|
||||
private:
|
||||
std::aligned_union_t<DBMS_MIN_FIELD_SIZE - sizeof(Types::Which),
|
||||
Null, UInt64, UInt128, Int64, Float64, String, Array, Tuple
|
||||
Null, UInt64, UInt128, Int64, Int128, Float64, String, Array, Tuple, DecField
|
||||
> storage;
|
||||
|
||||
Types::Which which;
|
||||
@ -370,6 +404,7 @@ private:
|
||||
case Types::UInt64: f(field.template get<UInt64>()); return;
|
||||
case Types::UInt128: f(field.template get<UInt128>()); return;
|
||||
case Types::Int64: f(field.template get<Int64>()); return;
|
||||
case Types::Int128: f(field.template get<Int128>()); return;
|
||||
case Types::Float64: f(field.template get<Float64>()); return;
|
||||
#if !__clang__
|
||||
#pragma GCC diagnostic pop
|
||||
@ -377,6 +412,7 @@ private:
|
||||
case Types::String: f(field.template get<String>()); return;
|
||||
case Types::Array: f(field.template get<Array>()); return;
|
||||
case Types::Tuple: f(field.template get<Tuple>()); return;
|
||||
case Types::Decimal: f(field.template get<DecField>()); return;
|
||||
|
||||
default:
|
||||
throw Exception("Bad type of Field", ErrorCodes::BAD_TYPE_OF_FIELD);
|
||||
@ -455,19 +491,23 @@ template <> struct Field::TypeToEnum<Null> { static const Types::Which value
|
||||
template <> struct Field::TypeToEnum<UInt64> { static const Types::Which value = Types::UInt64; };
|
||||
template <> struct Field::TypeToEnum<UInt128> { static const Types::Which value = Types::UInt128; };
|
||||
template <> struct Field::TypeToEnum<Int64> { static const Types::Which value = Types::Int64; };
|
||||
template <> struct Field::TypeToEnum<Int128> { static const Types::Which value = Types::Int128; };
|
||||
template <> struct Field::TypeToEnum<Float64> { static const Types::Which value = Types::Float64; };
|
||||
template <> struct Field::TypeToEnum<String> { static const Types::Which value = Types::String; };
|
||||
template <> struct Field::TypeToEnum<Array> { static const Types::Which value = Types::Array; };
|
||||
template <> struct Field::TypeToEnum<Tuple> { static const Types::Which value = Types::Tuple; };
|
||||
template <> struct Field::TypeToEnum<DecField>{ static const Types::Which value = Types::Decimal; };
|
||||
|
||||
template <> struct Field::EnumToType<Field::Types::Null> { using Type = Null; };
|
||||
template <> struct Field::EnumToType<Field::Types::UInt64> { using Type = UInt64; };
|
||||
template <> struct Field::EnumToType<Field::Types::UInt128> { using Type = UInt128; };
|
||||
template <> struct Field::EnumToType<Field::Types::Int64> { using Type = Int64; };
|
||||
template <> struct Field::EnumToType<Field::Types::Int128> { using Type = Int128; };
|
||||
template <> struct Field::EnumToType<Field::Types::Float64> { using Type = Float64; };
|
||||
template <> struct Field::EnumToType<Field::Types::String> { using Type = String; };
|
||||
template <> struct Field::EnumToType<Field::Types::Array> { using Type = Array; };
|
||||
template <> struct Field::EnumToType<Field::Types::Tuple> { using Type = Tuple; };
|
||||
template <> struct Field::EnumToType<Field::Types::Decimal> { using Type = DecField; };
|
||||
|
||||
|
||||
template <typename T>
|
||||
@ -510,6 +550,10 @@ template <> struct NearestFieldType<Int8> { using Type = Int64; };
|
||||
template <> struct NearestFieldType<Int16> { using Type = Int64; };
|
||||
template <> struct NearestFieldType<Int32> { using Type = Int64; };
|
||||
template <> struct NearestFieldType<Int64> { using Type = Int64; };
|
||||
template <> struct NearestFieldType<Int128> { using Type = Int128; };
|
||||
template <> struct NearestFieldType<Dec32> { using Type = DecField; };
|
||||
template <> struct NearestFieldType<Dec64> { using Type = DecField; };
|
||||
template <> struct NearestFieldType<Dec128> { using Type = DecField; };
|
||||
template <> struct NearestFieldType<Float32> { using Type = Float64; };
|
||||
template <> struct NearestFieldType<Float64> { using Type = Float64; };
|
||||
template <> struct NearestFieldType<String> { using Type = String; };
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <Poco/Types.h>
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -12,15 +12,15 @@ namespace DB
|
||||
|
||||
struct Null {};
|
||||
|
||||
using UInt8 = Poco::UInt8;
|
||||
using UInt16 = Poco::UInt16;
|
||||
using UInt32 = Poco::UInt32;
|
||||
using UInt64 = Poco::UInt64;
|
||||
using UInt8 = uint8_t;
|
||||
using UInt16 = uint16_t;
|
||||
using UInt32 = uint32_t;
|
||||
using UInt64 = uint64_t;
|
||||
|
||||
using Int8 = Poco::Int8;
|
||||
using Int16 = Poco::Int16;
|
||||
using Int32 = Poco::Int32;
|
||||
using Int64 = Poco::Int64;
|
||||
using Int8 = int8_t;
|
||||
using Int16 = int16_t;
|
||||
using Int32 = int32_t;
|
||||
using Int64 = int64_t;
|
||||
|
||||
using Float32 = float;
|
||||
using Float64 = double;
|
||||
@ -57,8 +57,123 @@ template <> struct TypeName<Float32> { static const char * get() { return "Float
|
||||
template <> struct TypeName<Float64> { static const char * get() { return "Float64"; } };
|
||||
template <> struct TypeName<String> { static const char * get() { return "String"; } };
|
||||
|
||||
template <typename T> struct TypeNumber;
|
||||
|
||||
/// 0 reserved for types without number
|
||||
template <> struct TypeNumber<UInt8> { static constexpr const size_t value = 1; };
|
||||
template <> struct TypeNumber<UInt16> { static constexpr const size_t value = 2; };
|
||||
template <> struct TypeNumber<UInt32> { static constexpr const size_t value = 3; };
|
||||
template <> struct TypeNumber<UInt64> { static constexpr const size_t value = 4; };
|
||||
/// 5 reserved for TypeNumber<UInt128>
|
||||
template <> struct TypeNumber<Float32> { static constexpr const size_t value = 7; };
|
||||
template <> struct TypeNumber<Float64> { static constexpr const size_t value = 8; };
|
||||
template <> struct TypeNumber<Int8> { static constexpr const size_t value = 9; };
|
||||
template <> struct TypeNumber<Int16> { static constexpr const size_t value = 10; };
|
||||
template <> struct TypeNumber<Int32> { static constexpr const size_t value = 11; };
|
||||
template <> struct TypeNumber<Int64> { static constexpr const size_t value = 12; };
|
||||
/// 13 reserved for TypeNumber<Int128>
|
||||
|
||||
/// Not a data type in database, defined just for convenience.
|
||||
using Strings = std::vector<String>;
|
||||
|
||||
}
|
||||
|
||||
#if 1 /// __int128
|
||||
namespace DB
|
||||
{
|
||||
|
||||
using Int128 = __int128;
|
||||
template <> constexpr bool IsNumber<Int128> = true;
|
||||
template <> struct TypeName<Int128> { static const char * get() { return "Int128"; } };
|
||||
template <> struct TypeNumber<Int128> { static constexpr const size_t value = 13; };
|
||||
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <> struct is_signed<__int128>
|
||||
{
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
template <> struct is_unsigned<__int128>
|
||||
{
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
|
||||
template <> struct is_integral<__int128>
|
||||
{
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
template <> struct is_arithmetic<__int128>
|
||||
{
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
/// Own FieldType for Decimal
|
||||
template <typename T>
|
||||
class Dec
|
||||
{
|
||||
public:
|
||||
using NativeType = T;
|
||||
|
||||
Dec() = default;
|
||||
Dec(Dec<T> &&) = default;
|
||||
Dec(const Dec<T> &) = default;
|
||||
|
||||
Dec(const T & value_)
|
||||
: value(value_)
|
||||
{}
|
||||
|
||||
template <typename U>
|
||||
Dec(const Dec<U> & x)
|
||||
: value(x)
|
||||
{}
|
||||
|
||||
constexpr Dec<T> & operator = (Dec<T> &&) = default;
|
||||
constexpr Dec<T> & operator = (const Dec<T> &) = default;
|
||||
|
||||
operator T () const { return value; }
|
||||
|
||||
const Dec<T> & operator += (const T & x) { value += x; return *this; }
|
||||
const Dec<T> & operator -= (const T & x) { value -= x; return *this; }
|
||||
const Dec<T> & operator *= (const T & x) { value *= x; return *this; }
|
||||
const Dec<T> & operator /= (const T & x) { value /= x; return *this; }
|
||||
const Dec<T> & operator %= (const T & x) { value %= x; return *this; }
|
||||
|
||||
private:
|
||||
T value;
|
||||
};
|
||||
|
||||
using Dec32 = Dec<Int32>;
|
||||
using Dec64 = Dec<Int64>;
|
||||
using Dec128 = Dec<Int128>;
|
||||
|
||||
template <> struct TypeName<Dec32> { static const char * get() { return "Dec32"; } };
|
||||
template <> struct TypeName<Dec64> { static const char * get() { return "Dec64"; } };
|
||||
template <> struct TypeName<Dec128> { static const char * get() { return "Dec128"; } };
|
||||
|
||||
template <> struct TypeNumber<Dec32> { static constexpr const size_t value = 16; };
|
||||
template <> struct TypeNumber<Dec64> { static constexpr const size_t value = 17; };
|
||||
template <> struct TypeNumber<Dec128> { static constexpr const size_t value = 18; };
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool decTrait() { return false; }
|
||||
template <> constexpr bool decTrait<Dec32>() { return true; }
|
||||
template <> constexpr bool decTrait<Dec64>() { return true; }
|
||||
template <> constexpr bool decTrait<Dec128>() { return true; }
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool decBaseTrait() { return false; }
|
||||
template <> constexpr bool decBaseTrait<Int32>() { return true; }
|
||||
template <> constexpr bool decBaseTrait<Int64>() { return true; }
|
||||
template <> constexpr bool decBaseTrait<Int128>() { return true; }
|
||||
}
|
||||
|
@ -129,6 +129,7 @@ void DataTypeFactory::registerSimpleDataType(const String & name, SimpleCreator
|
||||
}
|
||||
|
||||
void registerDataTypeNumbers(DataTypeFactory & factory);
|
||||
void registerDataTypeDecimal(DataTypeFactory & factory);
|
||||
void registerDataTypeDate(DataTypeFactory & factory);
|
||||
void registerDataTypeDateTime(DataTypeFactory & factory);
|
||||
void registerDataTypeString(DataTypeFactory & factory);
|
||||
@ -148,6 +149,7 @@ void registerDataTypeWithDictionary(DataTypeFactory & factory);
|
||||
DataTypeFactory::DataTypeFactory()
|
||||
{
|
||||
registerDataTypeNumbers(*this);
|
||||
registerDataTypeDecimal(*this);
|
||||
registerDataTypeDate(*this);
|
||||
registerDataTypeDateTime(*this);
|
||||
registerDataTypeString(*this);
|
||||
|
@ -16,6 +16,7 @@ public:
|
||||
using FieldType = T;
|
||||
|
||||
const char * getFamilyName() const override { return TypeName<T>::get(); }
|
||||
size_t getTypeNumber() const override { return TypeNumber<T>::value; }
|
||||
|
||||
void serializeText(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings &) const override;
|
||||
void serializeTextEscaped(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings &) const override;
|
||||
|
220
dbms/src/DataTypes/DataTypesDecimal.cpp
Normal file
220
dbms/src/DataTypes/DataTypesDecimal.cpp
Normal file
@ -0,0 +1,220 @@
|
||||
#include <type_traits>
|
||||
#include <common/intExp.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
#include <DataTypes/DataTypesDecimal.h>
|
||||
#include <DataTypes/DataTypeFactory.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
#include <IO/readFloatText.h>
|
||||
#include <Parsers/IAST.h>
|
||||
#include <Parsers/ASTLiteral.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
|
||||
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
||||
extern const int ARGUMENT_OUT_OF_BOUND;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
|
||||
template <typename T>
|
||||
std::string DataTypeDecimal<T>::getName() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Decimal(" << precision << ", " << scale << ")";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool DataTypeDecimal<T>::equals(const IDataType & rhs) const
|
||||
{
|
||||
if (auto * ptype = typeid_cast<const DataTypeDecimal<T> *>(&rhs))
|
||||
return scale == ptype->getScale();
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DataTypeDecimal<T>::serializeText(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings &) const
|
||||
{
|
||||
T value = static_cast<const ColumnType &>(column).getData()[row_num];
|
||||
if (value < T(0))
|
||||
{
|
||||
value *= T(-1);
|
||||
writeChar('-', ostr); /// avoid crop leading minus when whole part is zero
|
||||
}
|
||||
|
||||
writeIntText(static_cast<typename T::NativeType>(wholePart(value)), ostr);
|
||||
if (scale)
|
||||
{
|
||||
writeChar('.', ostr);
|
||||
String str_fractional(scale, '0');
|
||||
for (Int32 pos = scale - 1; pos >= 0; --pos, value /= T(10))
|
||||
str_fractional[pos] += value % T(10);
|
||||
ostr.write(str_fractional.data(), scale);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void DataTypeDecimal<T>::deserializeText(IColumn & column, ReadBuffer & istr, const FormatSettings &) const
|
||||
{
|
||||
T x;
|
||||
UInt32 unread_scale = scale;
|
||||
readDecimalText(istr, x, precision, unread_scale);
|
||||
x *= getScaleMultiplier(unread_scale);
|
||||
static_cast<ColumnType &>(column).getData().push_back(x);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
T DataTypeDecimal<T>::parseFromString(const String & str) const
|
||||
{
|
||||
ReadBufferFromMemory buf(str.data(), str.size());
|
||||
T x;
|
||||
UInt32 unread_scale = scale;
|
||||
readDecimalText(buf, x, precision, unread_scale, true);
|
||||
x *= getScaleMultiplier(unread_scale);
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void DataTypeDecimal<T>::serializeBinary(const Field & field, WriteBuffer & ostr) const
|
||||
{
|
||||
FieldType x = get<DecField>(field);
|
||||
writeBinary(x, ostr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DataTypeDecimal<T>::serializeBinary(const IColumn & column, size_t row_num, WriteBuffer & ostr) const
|
||||
{
|
||||
const FieldType & x = static_cast<const ColumnType &>(column).getData()[row_num];
|
||||
writeBinary(x, ostr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DataTypeDecimal<T>::serializeBinaryBulk(const IColumn & column, WriteBuffer & ostr, size_t offset, size_t limit) const
|
||||
{
|
||||
const typename ColumnType::Container & x = typeid_cast<const ColumnType &>(column).getData();
|
||||
|
||||
size_t size = x.size();
|
||||
|
||||
if (limit == 0 || offset + limit > size)
|
||||
limit = size - offset;
|
||||
|
||||
ostr.write(reinterpret_cast<const char *>(&x[offset]), sizeof(FieldType) * limit);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void DataTypeDecimal<T>::deserializeBinary(Field & field, ReadBuffer & istr) const
|
||||
{
|
||||
typename FieldType::NativeType x;
|
||||
readBinary(x, istr);
|
||||
field = DecField(T(x), scale);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DataTypeDecimal<T>::deserializeBinary(IColumn & column, ReadBuffer & istr) const
|
||||
{
|
||||
typename FieldType::NativeType x;
|
||||
readBinary(x, istr);
|
||||
static_cast<ColumnType &>(column).getData().push_back(FieldType(x));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DataTypeDecimal<T>::deserializeBinaryBulk(IColumn & column, ReadBuffer & istr, size_t limit, double ) const
|
||||
{
|
||||
typename ColumnType::Container & x = typeid_cast<ColumnType &>(column).getData();
|
||||
size_t initial_size = x.size();
|
||||
x.resize(initial_size + limit);
|
||||
size_t size = istr.readBig(reinterpret_cast<char*>(&x[initial_size]), sizeof(FieldType) * limit);
|
||||
x.resize(initial_size + size / sizeof(FieldType));
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
Field DataTypeDecimal<T>::getDefault() const
|
||||
{
|
||||
return DecField(T(0), scale);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
MutableColumnPtr DataTypeDecimal<T>::createColumn() const
|
||||
{
|
||||
auto column = ColumnType::create();
|
||||
column->getData().setScale(scale);
|
||||
return column;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
|
||||
static DataTypePtr create(const ASTPtr & arguments)
|
||||
{
|
||||
if (!arguments || arguments->children.size() != 2)
|
||||
throw Exception("Decimal data type family must have exactly two arguments: precision and scale",
|
||||
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
|
||||
const ASTLiteral * precision = typeid_cast<const ASTLiteral *>(arguments->children[0].get());
|
||||
const ASTLiteral * scale = typeid_cast<const ASTLiteral *>(arguments->children[1].get());
|
||||
|
||||
if (!precision || precision->value.getType() != Field::Types::UInt64 ||
|
||||
!scale || !(scale->value.getType() == Field::Types::Int64 || scale->value.getType() == Field::Types::UInt64))
|
||||
throw Exception("Decimal data type family must have a two numbers as its arguments", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||
|
||||
UInt64 precision_value = precision->value.get<UInt64>();
|
||||
Int64 scale_value = scale->value.get<Int64>();
|
||||
|
||||
if (precision_value < minDecimalPrecision() || precision_value > maxDecimalPrecision<Dec128>())
|
||||
throw Exception("Wrong precision", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||
|
||||
if (scale_value < 0 || static_cast<UInt64>(scale_value) > precision_value)
|
||||
throw Exception("Negative scales and scales larger than presicion are not supported", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||
|
||||
if (precision_value <= maxDecimalPrecision<Dec32>())
|
||||
return std::make_shared<DataTypeDecimal<Dec32>>(precision_value, scale_value);
|
||||
else if (precision_value <= maxDecimalPrecision<Dec64>())
|
||||
return std::make_shared<DataTypeDecimal<Dec64>>(precision_value, scale_value);
|
||||
return std::make_shared<DataTypeDecimal<Dec128>>(precision_value, scale_value);
|
||||
}
|
||||
|
||||
|
||||
void registerDataTypeDecimal(DataTypeFactory & factory)
|
||||
{
|
||||
factory.registerDataType("Decimal", create, DataTypeFactory::CaseInsensitive);
|
||||
factory.registerAlias("DEC", "Decimal", DataTypeFactory::CaseInsensitive);
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
Dec32 DataTypeDecimal<Dec32>::getScaleMultiplier(UInt32 scale_)
|
||||
{
|
||||
return common::exp10_i32(scale_);
|
||||
}
|
||||
|
||||
template <>
|
||||
Dec64 DataTypeDecimal<Dec64>::getScaleMultiplier(UInt32 scale_)
|
||||
{
|
||||
return common::exp10_i64(scale_);
|
||||
}
|
||||
|
||||
template <>
|
||||
Dec128 DataTypeDecimal<Dec128>::getScaleMultiplier(UInt32 scale_)
|
||||
{
|
||||
return common::exp10_i128(scale_);
|
||||
}
|
||||
|
||||
|
||||
/// Explicit template instantiations.
|
||||
template class DataTypeDecimal<Dec32>;
|
||||
template class DataTypeDecimal<Dec64>;
|
||||
template class DataTypeDecimal<Dec128>;
|
||||
|
||||
}
|
271
dbms/src/DataTypes/DataTypesDecimal.h
Normal file
271
dbms/src/DataTypes/DataTypesDecimal.h
Normal file
@ -0,0 +1,271 @@
|
||||
#pragma once
|
||||
#include <common/likely.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
#include <Columns/ColumnVector.h>
|
||||
#include <DataTypes/IDataType.h>
|
||||
#include <DataTypes/DataTypesNumber.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int ARGUMENT_OUT_OF_BOUND;
|
||||
}
|
||||
|
||||
///
|
||||
class DataTypeSimpleSerialization : public IDataType
|
||||
{
|
||||
void serializeTextEscaped(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const override
|
||||
{
|
||||
serializeText(column, row_num, ostr, settings);
|
||||
}
|
||||
|
||||
void serializeTextQuoted(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const override
|
||||
{
|
||||
serializeText(column, row_num, ostr, settings);
|
||||
}
|
||||
|
||||
void serializeTextJSON(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const override
|
||||
{
|
||||
serializeText(column, row_num, ostr, settings);
|
||||
}
|
||||
|
||||
void serializeTextCSV(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const override
|
||||
{
|
||||
serializeText(column, row_num, ostr, settings);
|
||||
}
|
||||
|
||||
void deserializeTextEscaped(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const override
|
||||
{
|
||||
deserializeText(column, istr, settings);
|
||||
}
|
||||
|
||||
void deserializeTextQuoted(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const override
|
||||
{
|
||||
deserializeText(column, istr, settings);
|
||||
}
|
||||
|
||||
void deserializeTextJSON(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const override
|
||||
{
|
||||
deserializeText(column, istr, settings);
|
||||
}
|
||||
|
||||
void deserializeTextCSV(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const override
|
||||
{
|
||||
deserializeText(column, istr, settings);
|
||||
}
|
||||
|
||||
virtual void deserializeText(IColumn & column, ReadBuffer & istr, const FormatSettings &) const = 0;
|
||||
};
|
||||
|
||||
|
||||
static constexpr size_t minDecimalPrecision() { return 1; }
|
||||
template <typename T> static constexpr size_t maxDecimalPrecision() { return 0; }
|
||||
template <> constexpr size_t maxDecimalPrecision<Dec32>() { return 9; }
|
||||
template <> constexpr size_t maxDecimalPrecision<Dec64>() { return 18; }
|
||||
template <> constexpr size_t maxDecimalPrecision<Dec128>() { return 38; }
|
||||
|
||||
|
||||
/// Implements Decimal(P, S), where P is precision, S is scale.
|
||||
/// Maximum precisions for underlying types are:
|
||||
/// Int32 9
|
||||
/// Int64 18
|
||||
/// Int128 38
|
||||
/// Operation between two decimals leads to Decimal(P, S), where
|
||||
/// P is one of (9, 18, 38); equals to the maximum precision for the biggest underlying type of operands.
|
||||
/// S is maximum scale of operands.
|
||||
///
|
||||
/// NOTE: It's possible to set scale as a template parameter then most of functions become static.
|
||||
template <typename T>
|
||||
class DataTypeDecimal final : public DataTypeSimpleSerialization
|
||||
{
|
||||
public:
|
||||
using FieldType = T;
|
||||
using ColumnType = ColumnVector<T>;
|
||||
|
||||
static constexpr bool is_parametric = true;
|
||||
|
||||
DataTypeDecimal(UInt32 precision_, UInt32 scale_)
|
||||
: precision(precision_),
|
||||
scale(scale_)
|
||||
{
|
||||
if (unlikely(precision < 1 || precision > maxDecimalPrecision<T>()))
|
||||
throw Exception("Precision is out of bounds", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||
if (unlikely(scale < 0 || static_cast<UInt32>(scale) > maxDecimalPrecision<T>()))
|
||||
throw Exception("Scale is out of bounds", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||
}
|
||||
|
||||
const char * getFamilyName() const override { return "Decimal"; }
|
||||
std::string getName() const override;
|
||||
size_t getTypeNumber() const override { return TypeNumber<T>::value; }
|
||||
|
||||
void serializeText(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings &) const override;
|
||||
void deserializeText(IColumn & column, ReadBuffer & istr, const FormatSettings &) const override;
|
||||
|
||||
void serializeBinary(const Field & field, WriteBuffer & ostr) const override;
|
||||
void serializeBinary(const IColumn & column, size_t row_num, WriteBuffer & ostr) const override;
|
||||
void serializeBinaryBulk(const IColumn & column, WriteBuffer & ostr, size_t offset, size_t limit) const override;
|
||||
|
||||
void deserializeBinary(Field & field, ReadBuffer & istr) const override;
|
||||
void deserializeBinary(IColumn & column, ReadBuffer & istr) const override;
|
||||
void deserializeBinaryBulk(IColumn & column, ReadBuffer & istr, size_t limit, double avg_value_size_hint) const override;
|
||||
|
||||
Field getDefault() const override;
|
||||
MutableColumnPtr createColumn() const override;
|
||||
bool equals(const IDataType & rhs) const override;
|
||||
|
||||
bool isParametric() const override { return true; }
|
||||
bool haveSubtypes() const override { return false; }
|
||||
bool shouldAlignRightInPrettyFormats() const override { return true; }
|
||||
bool textCanContainOnlyValidUTF8() const override { return true; }
|
||||
bool isComparable() const override { return true; }
|
||||
bool isValueRepresentedByNumber() const override { return true; }
|
||||
bool isValueRepresentedByInteger() const override { return true; }
|
||||
bool isValueRepresentedByUnsignedInteger() const override { return false; }
|
||||
bool isValueUnambiguouslyRepresentedInContiguousMemoryRegion() const override { return true; }
|
||||
bool haveMaximumSizeOfValue() const override { return true; }
|
||||
size_t getSizeOfValueInMemory() const override { return sizeof(T); }
|
||||
bool isCategorial() const override { return isValueRepresentedByInteger(); }
|
||||
|
||||
bool canBeUsedAsVersion() const override { return false; }
|
||||
bool isSummable() const override { return true; }
|
||||
bool canBeUsedInBitOperations() const override { return false; }
|
||||
bool isUnsignedInteger() const override { return false; }
|
||||
bool canBeUsedInBooleanContext() const override { return true; }
|
||||
bool isNumber() const override { return true; }
|
||||
bool isInteger() const override { return false; }
|
||||
bool canBeInsideNullable() const override { return true; }
|
||||
|
||||
/// Decimal specific
|
||||
|
||||
UInt32 getPrecision() const { return precision; }
|
||||
UInt32 getScale() const { return scale; }
|
||||
T getScaleMultiplier() const { return getScaleMultiplier(scale); }
|
||||
|
||||
T wholePart(T x) const
|
||||
{
|
||||
if (scale == 0)
|
||||
return x;
|
||||
return x / getScaleMultiplier();
|
||||
}
|
||||
|
||||
T fractionalPart(T x) const
|
||||
{
|
||||
if (scale == 0)
|
||||
return 0;
|
||||
if (x < T(0))
|
||||
x *= T(-1);
|
||||
return x % getScaleMultiplier();
|
||||
}
|
||||
|
||||
T maxWholeValue() const { return getScaleMultiplier(maxDecimalPrecision<T>() - scale) - T(1); }
|
||||
|
||||
bool canStoreWhole(T x) const
|
||||
{
|
||||
T max = maxWholeValue();
|
||||
if (x > max || x < -max)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @returns multiplier for U to become T with correct scale
|
||||
template <typename U>
|
||||
T scaleFactorFor(const DataTypeDecimal<U> & x, bool ) const
|
||||
{
|
||||
if (getScale() < x.getScale())
|
||||
throw Exception("Decimal result's scale is less then argiment's one", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||
UInt32 scale_delta = getScale() - x.getScale(); /// scale_delta >= 0
|
||||
return getScaleMultiplier(scale_delta);
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
T scaleFactorFor(const DataTypeNumber<U> & , bool is_multiply_or_divisor) const
|
||||
{
|
||||
if (is_multiply_or_divisor)
|
||||
return 1;
|
||||
return getScaleMultiplier();
|
||||
}
|
||||
|
||||
T parseFromString(const String & str) const;
|
||||
|
||||
static T getScaleMultiplier(UInt32 scale);
|
||||
|
||||
private:
|
||||
const UInt32 precision;
|
||||
const UInt32 scale; /// TODO: should we support scales out of [0, precision]?
|
||||
};
|
||||
|
||||
|
||||
template <typename T, typename U>
|
||||
typename std::enable_if_t<(sizeof(T) >= sizeof(U)), const DataTypeDecimal<T>>
|
||||
decimalResultType(const DataTypeDecimal<T> & tx, const DataTypeDecimal<U> & ty, bool is_multiply, bool is_divide)
|
||||
{
|
||||
UInt32 scale = (tx.getScale() > ty.getScale() ? tx.getScale() : ty.getScale());
|
||||
if (is_multiply)
|
||||
scale = tx.getScale() + ty.getScale();
|
||||
else if (is_divide)
|
||||
scale = tx.getScale();
|
||||
return DataTypeDecimal<T>(maxDecimalPrecision<T>(), scale);
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
typename std::enable_if_t<(sizeof(T) < sizeof(U)), const DataTypeDecimal<U>>
|
||||
decimalResultType(const DataTypeDecimal<T> & tx, const DataTypeDecimal<U> & ty, bool is_multiply, bool is_divide)
|
||||
{
|
||||
UInt32 scale = (tx.getScale() > ty.getScale() ? tx.getScale() : ty.getScale());
|
||||
if (is_multiply)
|
||||
scale = tx.getScale() * ty.getScale();
|
||||
else if (is_divide)
|
||||
scale = tx.getScale();
|
||||
return DataTypeDecimal<U>(maxDecimalPrecision<U>(), scale);
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
const DataTypeDecimal<T> decimalResultType(const DataTypeDecimal<T> & tx, const DataTypeNumber<U> &, bool, bool)
|
||||
{
|
||||
return DataTypeDecimal<T>(maxDecimalPrecision<T>(), tx.getScale());
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
const DataTypeDecimal<U> decimalResultType(const DataTypeNumber<T> &, const DataTypeDecimal<U> & ty, bool, bool)
|
||||
{
|
||||
return DataTypeDecimal<U>(maxDecimalPrecision<U>(), ty.getScale());
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline const DataTypeDecimal<T> * checkDecimal(const IDataType & data_type)
|
||||
{
|
||||
return typeid_cast<const DataTypeDecimal<T> *>(&data_type);
|
||||
}
|
||||
|
||||
inline bool isDecimal(const IDataType & data_type)
|
||||
{
|
||||
if (typeid_cast<const DataTypeDecimal<Dec32> *>(&data_type))
|
||||
return true;
|
||||
if (typeid_cast<const DataTypeDecimal<Dec64> *>(&data_type))
|
||||
return true;
|
||||
if (typeid_cast<const DataTypeDecimal<Dec128> *>(&data_type))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
///
|
||||
inline bool notDecimalButComparableToDecimal(const IDataType & data_type)
|
||||
{
|
||||
if (data_type.isInteger())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
///
|
||||
inline bool comparableToDecimal(const IDataType & data_type)
|
||||
{
|
||||
if (data_type.isInteger())
|
||||
return true;
|
||||
return isDecimal(data_type);
|
||||
}
|
||||
|
||||
}
|
@ -45,6 +45,9 @@ public:
|
||||
/// Name of data type family (example: FixedString, Array).
|
||||
virtual const char * getFamilyName() const = 0;
|
||||
|
||||
/// Unique type number or zero
|
||||
virtual size_t getTypeNumber() const { return 0; }
|
||||
|
||||
/** Binary serialization for range of values in column - for writing to disk/network, etc.
|
||||
*
|
||||
* Some data types are represented in multiple streams while being serialized.
|
||||
|
@ -1,3 +0,0 @@
|
||||
if (ENABLE_TESTS)
|
||||
add_subdirectory (tests)
|
||||
endif ()
|
@ -1,20 +1,58 @@
|
||||
#include <Dictionaries/ODBCDictionarySource.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <common/LocalDateTime.h>
|
||||
#include <Poco/Ext/SessionPoolHelpers.h>
|
||||
#include <Poco/Util/AbstractConfiguration.h>
|
||||
#include <DataTypes/DataTypeString.h>
|
||||
#include <Columns/ColumnString.h>
|
||||
#include <Dictionaries/ODBCDictionarySource.h>
|
||||
#include <Dictionaries/ODBCBlockInputStream.h>
|
||||
#include <Dictionaries/validateODBCConnectionString.h>
|
||||
#include <DataStreams/IProfilingBlockInputStream.h>
|
||||
#include <Dictionaries/readInvalidateQuery.h>
|
||||
#include <Interpreters/Context.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
#include <IO/ReadWriteBufferFromHTTP.h>
|
||||
#include <Formats/FormatFactory.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
class ODBCBridgeBlockInputStream : public IProfilingBlockInputStream
|
||||
{
|
||||
public:
|
||||
ODBCBridgeBlockInputStream(const Poco::URI & uri,
|
||||
std::function<void(std::ostream &)> callback,
|
||||
const Block & sample_block,
|
||||
const Context & context,
|
||||
size_t max_block_size,
|
||||
const ConnectionTimeouts & timeouts)
|
||||
{
|
||||
read_buf = std::make_unique<ReadWriteBufferFromHTTP>(uri, ODBCBridgeHelper::MAIN_METHOD, callback, timeouts);
|
||||
reader = FormatFactory::instance().getInput(ODBCBridgeHelper::DEFAULT_FORMAT, *read_buf, sample_block, context, max_block_size);
|
||||
}
|
||||
|
||||
Block getHeader() const override
|
||||
{
|
||||
return reader->getHeader();
|
||||
}
|
||||
|
||||
String getName() const override
|
||||
{
|
||||
return "ODBCBridgeBlockInputStream";
|
||||
}
|
||||
|
||||
private:
|
||||
Block readImpl() override
|
||||
{
|
||||
return reader->read();
|
||||
}
|
||||
|
||||
std::unique_ptr<ReadWriteBufferFromHTTP> read_buf;
|
||||
BlockInputStreamPtr reader;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
static const size_t max_block_size = 8192;
|
||||
|
||||
@ -32,20 +70,22 @@ ODBCDictionarySource::ODBCDictionarySource(const DictionaryStructure & dict_stru
|
||||
sample_block{sample_block},
|
||||
query_builder{dict_struct, db, table, where, IdentifierQuotingStyle::None}, /// NOTE Better to obtain quoting style via ODBC interface.
|
||||
load_all_query{query_builder.composeLoadAllQuery()},
|
||||
invalidate_query{config.getString(config_prefix + ".invalidate_query", "")}
|
||||
invalidate_query{config.getString(config_prefix + ".invalidate_query", "")},
|
||||
odbc_bridge_helper{context, config.getString(config_prefix + ".connection_string")},
|
||||
timeouts{ConnectionTimeouts::getHTTPTimeouts(context.getSettingsRef())},
|
||||
global_context(context)
|
||||
{
|
||||
std::size_t field_size = context.getSettingsRef().odbc_max_field_size;
|
||||
const auto & global_config = context.getConfigRef();
|
||||
size_t bridge_port = global_config.getUInt("odbc_bridge.port", ODBCBridgeHelper::DEFAULT_PORT);
|
||||
std::string bridge_host = global_config.getString("odbc_bridge.host", ODBCBridgeHelper::DEFAULT_HOST);
|
||||
|
||||
pool = createAndCheckResizePocoSessionPool([&]
|
||||
{
|
||||
auto session = std::make_shared<Poco::Data::SessionPool>(
|
||||
config.getString(config_prefix + ".connector", "ODBC"),
|
||||
validateODBCConnectionString(config.getString(config_prefix + ".connection_string")));
|
||||
bridge_url.setHost(bridge_host);
|
||||
bridge_url.setPort(bridge_port);
|
||||
bridge_url.setScheme("http");
|
||||
|
||||
/// Default POCO value is 1024. Set property manually to make possible reading of longer strings.
|
||||
session->setProperty("maxFieldSize", Poco::Any(field_size));
|
||||
return session;
|
||||
});
|
||||
auto url_params = odbc_bridge_helper.getURLParams(sample_block.getNamesAndTypesList(), max_block_size);
|
||||
for (const auto & [name, value] : url_params)
|
||||
bridge_url.addQueryParameter(name, value);
|
||||
}
|
||||
|
||||
/// copy-constructor is provided in order to support cloneability
|
||||
@ -58,11 +98,14 @@ ODBCDictionarySource::ODBCDictionarySource(const ODBCDictionarySource & other)
|
||||
where{other.where},
|
||||
update_field{other.update_field},
|
||||
sample_block{other.sample_block},
|
||||
pool{other.pool},
|
||||
query_builder{dict_struct, db, table, where, IdentifierQuotingStyle::None},
|
||||
load_all_query{other.load_all_query},
|
||||
invalidate_query{other.invalidate_query}, invalidate_query_response{other.invalidate_query_response}
|
||||
invalidate_query{other.invalidate_query},
|
||||
invalidate_query_response{other.invalidate_query_response},
|
||||
odbc_bridge_helper{other.odbc_bridge_helper},
|
||||
global_context{other.global_context}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string ODBCDictionarySource::getUpdateFieldAndDate()
|
||||
@ -86,7 +129,7 @@ std::string ODBCDictionarySource::getUpdateFieldAndDate()
|
||||
BlockInputStreamPtr ODBCDictionarySource::loadAll()
|
||||
{
|
||||
LOG_TRACE(log, load_all_query);
|
||||
return std::make_shared<ODBCBlockInputStream>(pool->get(), load_all_query, sample_block, max_block_size);
|
||||
return loadBase(load_all_query);
|
||||
}
|
||||
|
||||
BlockInputStreamPtr ODBCDictionarySource::loadUpdatedAll()
|
||||
@ -94,20 +137,20 @@ BlockInputStreamPtr ODBCDictionarySource::loadUpdatedAll()
|
||||
std::string load_query_update = getUpdateFieldAndDate();
|
||||
|
||||
LOG_TRACE(log, load_query_update);
|
||||
return std::make_shared<ODBCBlockInputStream>(pool->get(), load_query_update, sample_block, max_block_size);
|
||||
return loadBase(load_query_update);
|
||||
}
|
||||
|
||||
BlockInputStreamPtr ODBCDictionarySource::loadIds(const std::vector<UInt64> & ids)
|
||||
{
|
||||
const auto query = query_builder.composeLoadIdsQuery(ids);
|
||||
return std::make_shared<ODBCBlockInputStream>(pool->get(), query, sample_block, max_block_size);
|
||||
return loadBase(query);
|
||||
}
|
||||
|
||||
BlockInputStreamPtr ODBCDictionarySource::loadKeys(
|
||||
const Columns & key_columns, const std::vector<size_t> & requested_rows)
|
||||
{
|
||||
const auto query = query_builder.composeLoadKeysQuery(key_columns, requested_rows, ExternalQueryBuilder::AND_OR_CHAIN);
|
||||
return std::make_shared<ODBCBlockInputStream>(pool->get(), query, sample_block, max_block_size);
|
||||
return loadBase(query);
|
||||
}
|
||||
|
||||
bool ODBCDictionarySource::supportsSelectiveLoad() const
|
||||
@ -148,8 +191,28 @@ std::string ODBCDictionarySource::doInvalidateQuery(const std::string & request)
|
||||
Block invalidate_sample_block;
|
||||
ColumnPtr column(ColumnString::create());
|
||||
invalidate_sample_block.insert(ColumnWithTypeAndName(column, std::make_shared<DataTypeString>(), "Sample Block"));
|
||||
ODBCBlockInputStream block_input_stream(pool->get(), request, invalidate_sample_block, 1);
|
||||
return readInvalidateQuery(block_input_stream);
|
||||
odbc_bridge_helper.startODBCBridgeSync();
|
||||
|
||||
ODBCBridgeBlockInputStream stream(
|
||||
bridge_url,
|
||||
[request](std::ostream & os) { os << "query=" << request; },
|
||||
invalidate_sample_block,
|
||||
global_context,
|
||||
max_block_size,
|
||||
timeouts);
|
||||
|
||||
return readInvalidateQuery(stream);
|
||||
}
|
||||
|
||||
BlockInputStreamPtr ODBCDictionarySource::loadBase(const std::string & query) const
|
||||
{
|
||||
odbc_bridge_helper.startODBCBridgeSync();
|
||||
return std::make_shared<ODBCBridgeBlockInputStream>(bridge_url,
|
||||
[query](std::ostream & os) { os << "query=" << query; },
|
||||
sample_block,
|
||||
global_context,
|
||||
max_block_size,
|
||||
timeouts);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,11 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <Poco/Data/SessionPool.h>
|
||||
#include <Poco/URI.h>
|
||||
|
||||
#include <Common/ODBCBridgeHelper.h>
|
||||
|
||||
#include <Dictionaries/IDictionarySource.h>
|
||||
#include <Dictionaries/ExternalQueryBuilder.h>
|
||||
#include <Dictionaries/DictionaryStructure.h>
|
||||
|
||||
#include <IO/ConnectionTimeouts.h>
|
||||
|
||||
|
||||
namespace Poco
|
||||
{
|
||||
@ -58,6 +63,8 @@ private:
|
||||
// execute invalidate_query. expects single cell in result
|
||||
std::string doInvalidateQuery(const std::string & request) const;
|
||||
|
||||
BlockInputStreamPtr loadBase(const std::string & query) const;
|
||||
|
||||
Poco::Logger * log;
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> update_time;
|
||||
@ -67,11 +74,16 @@ private:
|
||||
const std::string where;
|
||||
const std::string update_field;
|
||||
Block sample_block;
|
||||
std::shared_ptr<Poco::Data::SessionPool> pool = nullptr;
|
||||
ExternalQueryBuilder query_builder;
|
||||
const std::string load_all_query;
|
||||
std::string invalidate_query;
|
||||
mutable std::string invalidate_query_response;
|
||||
|
||||
ODBCBridgeHelper odbc_bridge_helper;
|
||||
Poco::URI bridge_url;
|
||||
ConnectionTimeouts timeouts;
|
||||
const Context & global_context;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -7,6 +7,83 @@
|
||||
#include <Core/Block.h>
|
||||
#include <Core/ColumnNumbers.h>
|
||||
|
||||
namespace common
|
||||
{
|
||||
template <typename T>
|
||||
inline bool addOverflow(T x, T y, T & res)
|
||||
{
|
||||
return __builtin_add_overflow(x, y, &res);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool addOverflow(Int32 x, Int32 y, Int32 & res)
|
||||
{
|
||||
return __builtin_sadd_overflow(x, y, &res);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool addOverflow(Int64 x, Int64 y, Int64 & res)
|
||||
{
|
||||
return __builtin_saddl_overflow(x, y, &res);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool addOverflow(__int128 x, __int128 y, __int128 & res)
|
||||
{
|
||||
res = x + y;
|
||||
return (res - y) != x;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool subOverflow(T x, T y, T & res)
|
||||
{
|
||||
return __builtin_sub_overflow(x, y, &res);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool subOverflow(Int32 x, Int32 y, Int32 & res)
|
||||
{
|
||||
return __builtin_ssub_overflow(x, y, &res);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool subOverflow(Int64 x, Int64 y, Int64 & res)
|
||||
{
|
||||
return __builtin_ssubl_overflow(x, y, &res);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool subOverflow(__int128 x, __int128 y, __int128 & res)
|
||||
{
|
||||
res = x - y;
|
||||
return (res + y) != x;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool mulOverflow(T x, T y, T & res)
|
||||
{
|
||||
return __builtin_mul_overflow(x, y, &res);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool mulOverflow(Int32 x, Int32 y, Int32 & res)
|
||||
{
|
||||
return __builtin_smul_overflow(x, y, &res);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool mulOverflow(Int64 x, Int64 y, Int64 & res)
|
||||
{
|
||||
return __builtin_smull_overflow(x, y, &res);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool mulOverflow(__int128 x, __int128 y, __int128 & res)
|
||||
{
|
||||
res = x * y;
|
||||
return (res / y) != x;
|
||||
}
|
||||
}
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -94,5 +171,62 @@ Block createBlockWithNestedColumns(const Block & block, const ColumnNumbers & ar
|
||||
/// Similar function as above. Additionally transform the result type if needed.
|
||||
Block createBlockWithNestedColumns(const Block & block, const ColumnNumbers & args, size_t result);
|
||||
|
||||
template <typename T, template <typename, typename, template <typename, typename> typename> typename Apply,
|
||||
template <typename, typename> typename Op, typename... Args>
|
||||
void callByTypeAndNumber(UInt8 number, bool & done, Args &... args)
|
||||
{
|
||||
done = true;
|
||||
switch (number)
|
||||
{
|
||||
case TypeNumber<UInt8>::value: Apply<T, UInt8, Op>(args...); break;
|
||||
case TypeNumber<UInt16>::value: Apply<T, UInt16, Op>(args...); break;
|
||||
case TypeNumber<UInt32>::value: Apply<T, UInt32, Op>(args...); break;
|
||||
case TypeNumber<UInt64>::value: Apply<T, UInt64, Op>(args...); break;
|
||||
//case TypeNumber<UInt128>::value: Apply<T, UInt128, Op>(args...); break;
|
||||
|
||||
case TypeNumber<Int8>::value: Apply<T, Int8, Op>(args...); break;
|
||||
case TypeNumber<Int16>::value: Apply<T, Int16, Op>(args...); break;
|
||||
case TypeNumber<Int32>::value: Apply<T, Int32, Op>(args...); break;
|
||||
case TypeNumber<Int64>::value: Apply<T, Int64, Op>(args...); break;
|
||||
case TypeNumber<Int128>::value: Apply<T, Int128, Op>(args...); break;
|
||||
|
||||
case TypeNumber<Dec32>::value: Apply<T, Dec32, Op>(args...); break;
|
||||
case TypeNumber<Dec64>::value: Apply<T, Dec64, Op>(args...); break;
|
||||
case TypeNumber<Dec128>::value: Apply<T, Dec128, Op>(args...); break;
|
||||
default:
|
||||
done = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Unroll template using TypeNumber<T>
|
||||
template <template <typename, typename, template <typename, typename> typename> typename Apply,
|
||||
template <typename, typename> typename Op, typename... Args>
|
||||
inline bool callByNumbers(UInt8 type_num1, UInt8 type_num2, Args &... args)
|
||||
{
|
||||
bool done = false;
|
||||
switch (type_num1)
|
||||
{
|
||||
case TypeNumber<UInt8>::value: callByTypeAndNumber<UInt8, Apply, Op, Args...>(type_num2, done, args...); break;
|
||||
case TypeNumber<UInt16>::value: callByTypeAndNumber<UInt16, Apply, Op, Args...>(type_num2, done, args...); break;
|
||||
case TypeNumber<UInt32>::value: callByTypeAndNumber<UInt32, Apply, Op, Args...>(type_num2, done, args...); break;
|
||||
case TypeNumber<UInt64>::value: callByTypeAndNumber<UInt64, Apply, Op, Args...>(type_num2, done, args...); break;
|
||||
//case TypeNumber<UInt128>::value: callByTypeAndNumber<UInt128, Apply, Op, Args...>(type_num2, done, args...); break;
|
||||
|
||||
case TypeNumber<Int8>::value: callByTypeAndNumber<Int8, Apply, Op, Args...>(type_num2, done, args...); break;
|
||||
case TypeNumber<Int16>::value: callByTypeAndNumber<Int16, Apply, Op, Args...>(type_num2, done, args...); break;
|
||||
case TypeNumber<Int32>::value: callByTypeAndNumber<Int32, Apply, Op, Args...>(type_num2, done, args...); break;
|
||||
case TypeNumber<Int64>::value: callByTypeAndNumber<Int64, Apply, Op, Args...>(type_num2, done, args...); break;
|
||||
case TypeNumber<Int128>::value: callByTypeAndNumber<Int128, Apply, Op, Args...>(type_num2, done, args...); break;
|
||||
|
||||
case TypeNumber<Dec32>::value: callByTypeAndNumber<Dec32, Apply, Op, Args...>(type_num2, done, args...); break;
|
||||
case TypeNumber<Dec64>::value: callByTypeAndNumber<Dec64, Apply, Op, Args...>(type_num2, done, args...); break;
|
||||
case TypeNumber<Dec128>::value: callByTypeAndNumber<Dec128, Apply, Op, Args...>(type_num2, done, args...); break;
|
||||
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <DataTypes/DataTypesNumber.h>
|
||||
#include <DataTypes/DataTypesDecimal.h>
|
||||
#include <DataTypes/DataTypeDate.h>
|
||||
#include <DataTypes/DataTypeDateTime.h>
|
||||
#include <DataTypes/DataTypeInterval.h>
|
||||
@ -37,6 +38,7 @@ namespace ErrorCodes
|
||||
extern const int ILLEGAL_COLUMN;
|
||||
extern const int LOGICAL_ERROR;
|
||||
extern const int TOO_LESS_ARGUMENTS_FOR_FUNCTION;
|
||||
extern const int DECIMAL_OVERFLOW;
|
||||
}
|
||||
|
||||
|
||||
@ -88,8 +90,10 @@ template <typename A, typename Op>
|
||||
struct UnaryOperationImpl
|
||||
{
|
||||
using ResultType = typename Op::ResultType;
|
||||
using ArrayA = typename ColumnVector<A>::Container;
|
||||
using ArrayC = typename ColumnVector<ResultType>::Container;
|
||||
|
||||
static void NO_INLINE vector(const PaddedPODArray<A> & a, PaddedPODArray<ResultType> & c)
|
||||
static void NO_INLINE vector(const ArrayA & a, ArrayC & c)
|
||||
{
|
||||
size_t size = a.size();
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
@ -107,6 +111,7 @@ template <typename A, typename B>
|
||||
struct PlusImpl
|
||||
{
|
||||
using ResultType = typename NumberTraits::ResultOfAdditionMultiplication<A, B>::Type;
|
||||
static const constexpr bool allow_decimal = true;
|
||||
|
||||
template <typename Result = ResultType>
|
||||
static inline Result apply(A a, B b)
|
||||
@ -115,6 +120,12 @@ struct PlusImpl
|
||||
return static_cast<Result>(a) + b;
|
||||
}
|
||||
|
||||
template <typename Result = ResultType>
|
||||
static inline bool apply(A a, B b, Result & c)
|
||||
{
|
||||
return common::addOverflow(static_cast<Result>(a), b, c);
|
||||
}
|
||||
|
||||
#if USE_EMBEDDED_COMPILER
|
||||
static constexpr bool compilable = true;
|
||||
|
||||
@ -130,6 +141,7 @@ template <typename A, typename B>
|
||||
struct MultiplyImpl
|
||||
{
|
||||
using ResultType = typename NumberTraits::ResultOfAdditionMultiplication<A, B>::Type;
|
||||
static const constexpr bool allow_decimal = true;
|
||||
|
||||
template <typename Result = ResultType>
|
||||
static inline Result apply(A a, B b)
|
||||
@ -137,6 +149,12 @@ struct MultiplyImpl
|
||||
return static_cast<Result>(a) * b;
|
||||
}
|
||||
|
||||
template <typename Result = ResultType>
|
||||
static inline bool apply(A a, B b, Result & c)
|
||||
{
|
||||
return common::mulOverflow(static_cast<Result>(a), b, c);
|
||||
}
|
||||
|
||||
#if USE_EMBEDDED_COMPILER
|
||||
static constexpr bool compilable = true;
|
||||
|
||||
@ -151,6 +169,7 @@ template <typename A, typename B>
|
||||
struct MinusImpl
|
||||
{
|
||||
using ResultType = typename NumberTraits::ResultOfSubtraction<A, B>::Type;
|
||||
static const constexpr bool allow_decimal = true;
|
||||
|
||||
template <typename Result = ResultType>
|
||||
static inline Result apply(A a, B b)
|
||||
@ -158,6 +177,12 @@ struct MinusImpl
|
||||
return static_cast<Result>(a) - b;
|
||||
}
|
||||
|
||||
template <typename Result = ResultType>
|
||||
static inline bool apply(A a, B b, Result & c)
|
||||
{
|
||||
return common::subOverflow(static_cast<Result>(a), b, c);
|
||||
}
|
||||
|
||||
#if USE_EMBEDDED_COMPILER
|
||||
static constexpr bool compilable = true;
|
||||
|
||||
@ -172,6 +197,7 @@ template <typename A, typename B>
|
||||
struct DivideFloatingImpl
|
||||
{
|
||||
using ResultType = typename NumberTraits::ResultOfFloatingPointDivision<A, B>::Type;
|
||||
static const constexpr bool allow_decimal = true;
|
||||
|
||||
template <typename Result = ResultType>
|
||||
static inline Result apply(A a, B b)
|
||||
@ -551,7 +577,7 @@ using GreatestImpl = std::conditional_t<!NumberTraits::LeastGreatestSpecialCase<
|
||||
template <typename A>
|
||||
struct NegateImpl
|
||||
{
|
||||
using ResultType = typename NumberTraits::ResultOfNegate<A>::Type;
|
||||
using ResultType = std::conditional_t<decTrait<A>(), A, typename NumberTraits::ResultOfNegate<A>::Type>;
|
||||
|
||||
static inline ResultType apply(A a)
|
||||
{
|
||||
@ -593,11 +619,13 @@ struct BitNotImpl
|
||||
template <typename A>
|
||||
struct AbsImpl
|
||||
{
|
||||
using ResultType = typename NumberTraits::ResultOfAbs<A>::Type;
|
||||
using ResultType = std::conditional_t<decTrait<A>(), A, typename NumberTraits::ResultOfAbs<A>::Type>;
|
||||
|
||||
static inline ResultType apply(A a)
|
||||
{
|
||||
if constexpr (std::is_integral_v<A> && std::is_signed_v<A>)
|
||||
if constexpr (decTrait<A>())
|
||||
return a < 0 ? A(-a) : a;
|
||||
else if constexpr (std::is_integral_v<A> && std::is_signed_v<A>)
|
||||
return a < 0 ? static_cast<ResultType>(~a) + 1 : a;
|
||||
else if constexpr (std::is_integral_v<A> && std::is_unsigned_v<A>)
|
||||
return static_cast<ResultType>(a);
|
||||
@ -687,6 +715,199 @@ struct IntExp10Impl
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
template <typename T> struct NativeType { using Type = T; };
|
||||
template <> struct NativeType<Dec32> { using Type = Int32; };
|
||||
template <> struct NativeType<Dec64> { using Type = Int64; };
|
||||
template <> struct NativeType<Dec128> { using Type = Int128; };
|
||||
|
||||
/// Binary operations for Decimals need scale args
|
||||
/// +|- scale one of args (which scale factor is not 1). ScaleR = oneof(Scale1, Scale2);
|
||||
/// * no agrs scale. ScaleR = Scale1 + Scale2;
|
||||
/// / first arg scale. ScaleR = Scale1 (scale_a = DecimalType<B>::getScale()).
|
||||
template <typename A, typename B, template <typename, typename> typename Operation, typename ResultType_>
|
||||
struct DecimalBinaryOperation
|
||||
{
|
||||
using ResultType = ResultType_;
|
||||
using NativeResultType = typename NativeType<ResultType>::Type;
|
||||
using Op = Operation<NativeResultType, NativeResultType>;
|
||||
using ArrayA = typename ColumnVector<A>::Container;
|
||||
using ArrayB = typename ColumnVector<B>::Container;
|
||||
using ArrayC = typename ColumnVector<ResultType>::Container;
|
||||
|
||||
static constexpr bool is_plus_minus = std::is_same_v<Operation<Int32, Int32>, PlusImpl<Int32, Int32>> ||
|
||||
std::is_same_v<Operation<Int32, Int32>, MinusImpl<Int32, Int32>>;
|
||||
static constexpr bool is_multiply = std::is_same_v<Operation<Int32, Int32>, MultiplyImpl<Int32, Int32>>;
|
||||
static constexpr bool is_division = std::is_same_v<Operation<Int32, Int32>, DivideFloatingImpl<Int32, Int32>>;
|
||||
static constexpr bool is_compare = std::is_same_v<Operation<Int32, Int32>, LeastBaseImpl<Int32, Int32>> ||
|
||||
std::is_same_v<Operation<Int32, Int32>, GreatestBaseImpl<Int32, Int32>>;
|
||||
static constexpr bool is_plus_minus_compare = is_plus_minus || is_compare;
|
||||
static constexpr bool can_overflow = is_plus_minus || is_multiply;
|
||||
|
||||
static void NO_INLINE vector_vector(const ArrayA & a, const ArrayB & b, ArrayC & c,
|
||||
ResultType scale_a [[maybe_unused]], ResultType scale_b [[maybe_unused]])
|
||||
{
|
||||
size_t size = a.size();
|
||||
if constexpr (is_plus_minus_compare)
|
||||
{
|
||||
if (scale_a != 1)
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
c[i] = applyScaled<true>(a[i], b[i], scale_a);
|
||||
return;
|
||||
}
|
||||
else if (scale_b != 1)
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
c[i] = applyScaled<false>(a[i], b[i], scale_b);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if constexpr (is_division && decTrait<B>())
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
c[i] = applyScaledDiv(a[i], b[i], scale_a);
|
||||
return;
|
||||
}
|
||||
|
||||
/// default: use it if no return before
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
c[i] = apply(a[i], b[i]);
|
||||
}
|
||||
|
||||
static void NO_INLINE vector_constant(const ArrayA & a, B b, ArrayC & c,
|
||||
ResultType scale_a [[maybe_unused]], ResultType scale_b [[maybe_unused]])
|
||||
{
|
||||
size_t size = a.size();
|
||||
if constexpr (is_plus_minus_compare)
|
||||
{
|
||||
if (scale_a != 1)
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
c[i] = applyScaled<true>(a[i], b, scale_a);
|
||||
return;
|
||||
}
|
||||
else if (scale_b != 1)
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
c[i] = applyScaled<false>(a[i], b, scale_b);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if constexpr (is_division && decTrait<B>())
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
c[i] = applyScaledDiv(a[i], b, scale_a);
|
||||
return;
|
||||
}
|
||||
|
||||
/// default: use it if no return before
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
c[i] = apply(a[i], b);
|
||||
}
|
||||
|
||||
static void NO_INLINE constant_vector(A a, const ArrayB & b, ArrayC & c,
|
||||
ResultType scale_a [[maybe_unused]], ResultType scale_b [[maybe_unused]])
|
||||
{
|
||||
size_t size = b.size();
|
||||
if constexpr (is_plus_minus_compare)
|
||||
{
|
||||
if (scale_a != 1)
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
c[i] = applyScaled<true>(a, b[i], scale_a);
|
||||
return;
|
||||
}
|
||||
else if (scale_b != 1)
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
c[i] = applyScaled<false>(a, b[i], scale_b);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if constexpr (is_division && decTrait<B>())
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
c[i] = applyScaledDiv(a, b[i], scale_a);
|
||||
return;
|
||||
}
|
||||
|
||||
/// default: use it if no return before
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
c[i] = apply(a, b[i]);
|
||||
}
|
||||
|
||||
static ResultType constant_constant(A a, B b, ResultType scale_a [[maybe_unused]], ResultType scale_b [[maybe_unused]])
|
||||
{
|
||||
if constexpr (is_plus_minus_compare)
|
||||
{
|
||||
if (scale_a != 1)
|
||||
return applyScaled<true>(a, b, scale_a);
|
||||
else if (scale_b != 1)
|
||||
return applyScaled<false>(a, b, scale_b);
|
||||
}
|
||||
else if constexpr (is_division && decTrait<B>())
|
||||
return applyScaledDiv(a, b, scale_a);
|
||||
return apply(a, b);
|
||||
}
|
||||
|
||||
private:
|
||||
/// there's implicit type convertion here
|
||||
static NativeResultType apply(NativeResultType a, NativeResultType b)
|
||||
{
|
||||
if constexpr (can_overflow)
|
||||
{
|
||||
NativeResultType res;
|
||||
if (Op::template apply<NativeResultType>(a, b, res))
|
||||
throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW);
|
||||
return res;
|
||||
}
|
||||
else
|
||||
return Op::template apply<NativeResultType>(a, b);
|
||||
}
|
||||
|
||||
template <bool scale_left>
|
||||
static NativeResultType applyScaled(NativeResultType a, NativeResultType b, NativeResultType scale)
|
||||
{
|
||||
if constexpr (is_plus_minus_compare)
|
||||
{
|
||||
NativeResultType res;
|
||||
|
||||
bool overflow = false;
|
||||
if constexpr (scale_left)
|
||||
overflow |= common::mulOverflow(a, scale, a);
|
||||
else
|
||||
overflow |= common::mulOverflow(b, scale, b);
|
||||
|
||||
if constexpr (can_overflow)
|
||||
overflow |= Op::template apply<NativeResultType>(a, b, res);
|
||||
else
|
||||
res = Op::template apply<NativeResultType>(a, b);
|
||||
|
||||
if (overflow)
|
||||
throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW);
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
static NativeResultType applyScaledDiv(NativeResultType a, NativeResultType b, NativeResultType scale)
|
||||
{
|
||||
if constexpr (is_division)
|
||||
{
|
||||
bool overflow = false;
|
||||
if constexpr (!decTrait<A>())
|
||||
overflow |= common::mulOverflow(scale, scale, scale);
|
||||
overflow |= common::mulOverflow(a, scale, a);
|
||||
if (overflow)
|
||||
throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW);
|
||||
|
||||
return Op::template apply<NativeResultType>(a, b);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// Used to indicate undefined operation
|
||||
struct InvalidType;
|
||||
|
||||
@ -709,6 +930,16 @@ template <typename DataType> constexpr bool IsDateOrDateTime = false;
|
||||
template <> constexpr bool IsDateOrDateTime<DataTypeDate> = true;
|
||||
template <> constexpr bool IsDateOrDateTime<DataTypeDateTime> = true;
|
||||
|
||||
template <typename DataType> constexpr bool IsDecimal = false;
|
||||
template <> constexpr bool IsDecimal<DataTypeDecimal<Dec32>> = true;
|
||||
template <> constexpr bool IsDecimal<DataTypeDecimal<Dec64>> = true;
|
||||
template <> constexpr bool IsDecimal<DataTypeDecimal<Dec128>> = true;
|
||||
|
||||
template <typename T0, typename T1> constexpr bool UseLeftDecimal = false;
|
||||
template <> constexpr bool UseLeftDecimal<DataTypeDecimal<Dec128>, DataTypeDecimal<Dec32>> = true;
|
||||
template <> constexpr bool UseLeftDecimal<DataTypeDecimal<Dec128>, DataTypeDecimal<Dec64>> = true;
|
||||
template <> constexpr bool UseLeftDecimal<DataTypeDecimal<Dec64>, DataTypeDecimal<Dec32>> = true;
|
||||
|
||||
template <typename T> using DataTypeFromFieldType = std::conditional_t<std::is_same_v<T, NumberTraits::Error>, InvalidType, DataTypeNumber<T>>;
|
||||
|
||||
template <template <typename, typename> class Operation, typename LeftDataType, typename RightDataType>
|
||||
@ -716,11 +947,30 @@ struct BinaryOperationTraits
|
||||
{
|
||||
using T0 = typename LeftDataType::FieldType;
|
||||
using T1 = typename RightDataType::FieldType;
|
||||
private: /// it's not correct for Decimal
|
||||
using Op = Operation<T0, T1>;
|
||||
public:
|
||||
|
||||
static constexpr bool allow_decimal =
|
||||
std::is_same_v<Operation<T0, T0>, PlusImpl<T0, T0>> ||
|
||||
std::is_same_v<Operation<T0, T0>, MinusImpl<T0, T0>> ||
|
||||
std::is_same_v<Operation<T0, T0>, MultiplyImpl<T0, T0>> ||
|
||||
std::is_same_v<Operation<T0, T0>, DivideFloatingImpl<T0, T0>> ||
|
||||
std::is_same_v<Operation<T0, T0>, LeastBaseImpl<T0, T0>> ||
|
||||
std::is_same_v<Operation<T0, T0>, GreatestBaseImpl<T0, T0>>;
|
||||
|
||||
/// Appropriate result type for binary operator on numeric types. "Date" can also mean
|
||||
/// DateTime, but if both operands are Dates, their type must be the same (e.g. Date - DateTime is invalid).
|
||||
using ResultDataType = Switch<
|
||||
/// Decimal cases
|
||||
Case<!allow_decimal && (IsDecimal<LeftDataType> || IsDecimal<RightDataType>), InvalidType>,
|
||||
Case<IsDecimal<LeftDataType> && IsDecimal<RightDataType> && UseLeftDecimal<LeftDataType, RightDataType>, LeftDataType>,
|
||||
Case<IsDecimal<LeftDataType> && IsDecimal<RightDataType>, RightDataType>,
|
||||
Case<IsDecimal<LeftDataType> && !IsDecimal<RightDataType> && IsIntegral<RightDataType>, LeftDataType>,
|
||||
Case<!IsDecimal<LeftDataType> && IsDecimal<RightDataType> && IsIntegral<LeftDataType>, RightDataType>,
|
||||
/// Decimal <op> Real is not supported (traditional DBs convert Decimal <op> Real to Real)
|
||||
Case<IsDecimal<LeftDataType> && !IsDecimal<RightDataType> && !IsIntegral<RightDataType>, InvalidType>,
|
||||
Case<!IsDecimal<LeftDataType> && IsDecimal<RightDataType> && !IsIntegral<LeftDataType>, InvalidType>,
|
||||
/// number <op> number -> see corresponding impl
|
||||
Case<!IsDateOrDateTime<LeftDataType> && !IsDateOrDateTime<RightDataType>,
|
||||
DataTypeFromFieldType<typename Op::ResultType>>,
|
||||
@ -769,7 +1019,10 @@ class FunctionBinaryArithmetic : public IFunction
|
||||
DataTypeFloat32,
|
||||
DataTypeFloat64,
|
||||
DataTypeDate,
|
||||
DataTypeDateTime
|
||||
DataTypeDateTime,
|
||||
DataTypeDecimal<Dec32>,
|
||||
DataTypeDecimal<Dec64>,
|
||||
DataTypeDecimal<Dec128>
|
||||
>(type, std::forward<F>(f));
|
||||
}
|
||||
|
||||
@ -862,7 +1115,19 @@ public:
|
||||
using ResultDataType = typename BinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType;
|
||||
if constexpr (!std::is_same_v<ResultDataType, InvalidType>)
|
||||
{
|
||||
type_res = std::make_shared<ResultDataType>();
|
||||
if constexpr (IsDecimal<LeftDataType> && IsDecimal<RightDataType>)
|
||||
{
|
||||
constexpr bool is_multiply = std::is_same_v<Op<UInt8, UInt8>, MultiplyImpl<UInt8, UInt8>>;
|
||||
constexpr bool is_division = std::is_same_v<Op<UInt8, UInt8>, DivideFloatingImpl<UInt8, UInt8>>;
|
||||
ResultDataType result_type = decimalResultType(left, right, is_multiply, is_division);
|
||||
type_res = std::make_shared<ResultDataType>(result_type.getPrecision(), result_type.getScale());
|
||||
}
|
||||
else if constexpr (IsDecimal<LeftDataType>)
|
||||
type_res = std::make_shared<LeftDataType>(left.getPrecision(), left.getScale());
|
||||
else if constexpr (IsDecimal<RightDataType>)
|
||||
type_res = std::make_shared<RightDataType>(right.getPrecision(), right.getScale());
|
||||
else
|
||||
type_res = std::make_shared<ResultDataType>();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -907,42 +1172,101 @@ public:
|
||||
using ResultDataType = typename BinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType;
|
||||
if constexpr (!std::is_same_v<ResultDataType, InvalidType>)
|
||||
{
|
||||
constexpr bool result_is_decimal = IsDecimal<LeftDataType> || IsDecimal<RightDataType>;
|
||||
constexpr bool is_multiply = std::is_same_v<Op<UInt8, UInt8>, MultiplyImpl<UInt8, UInt8>>;
|
||||
constexpr bool is_division = std::is_same_v<Op<UInt8, UInt8>, DivideFloatingImpl<UInt8, UInt8>>;
|
||||
|
||||
using T0 = typename LeftDataType::FieldType;
|
||||
using T1 = typename RightDataType::FieldType;
|
||||
using ResultType = typename ResultDataType::FieldType;
|
||||
using OpImpl = BinaryOperationImpl<T0, T1, Op<T0, T1>, ResultType>;
|
||||
using ColVecT0 = ColumnVector<T0>;
|
||||
using ColVecT1 = ColumnVector<T1>;
|
||||
using ColVecResult = ColumnVector<ResultType>;
|
||||
|
||||
/// Decimal operations need scale. Operations are on result type.
|
||||
using OpImpl = std::conditional_t<IsDecimal<ResultDataType>,
|
||||
DecimalBinaryOperation<T0, T1, Op, ResultType>,
|
||||
BinaryOperationImpl<T0, T1, Op<T0, T1>, ResultType>>;
|
||||
|
||||
auto col_left_raw = block.getByPosition(arguments[0]).column.get();
|
||||
auto col_right_raw = block.getByPosition(arguments[1]).column.get();
|
||||
if (auto col_left = checkAndGetColumnConst<ColumnVector<T0>>(col_left_raw))
|
||||
if (auto col_left = checkAndGetColumnConst<ColVecT0>(col_left_raw))
|
||||
{
|
||||
if (auto col_right = checkAndGetColumnConst<ColumnVector<T1>>(col_right_raw))
|
||||
if (auto col_right = checkAndGetColumnConst<ColVecT1>(col_right_raw))
|
||||
{
|
||||
/// the only case with a non-vector result
|
||||
auto res = OpImpl::constant_constant(col_left->template getValue<T0>(), col_right->template getValue<T1>());
|
||||
block.getByPosition(result).column = ResultDataType().createColumnConst(col_left->size(), toField(res));
|
||||
if constexpr (result_is_decimal)
|
||||
{
|
||||
ResultDataType type = decimalResultType(left, right, is_multiply, is_division);
|
||||
typename ResultDataType::FieldType scale_a = type.scaleFactorFor(left, is_multiply);
|
||||
typename ResultDataType::FieldType scale_b = type.scaleFactorFor(right, is_multiply || is_division);
|
||||
if constexpr (IsDecimal<RightDataType> && is_division)
|
||||
scale_a = right.getScaleMultiplier();
|
||||
auto res = OpImpl::constant_constant(col_left->template getValue<T0>(), col_right->template getValue<T1>(),
|
||||
scale_a, scale_b);
|
||||
block.getByPosition(result).column =
|
||||
ResultDataType(type.getPrecision(), type.getScale()).createColumnConst(col_left->size(), toField(res));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto res = OpImpl::constant_constant(col_left->template getValue<T0>(), col_right->template getValue<T1>());
|
||||
block.getByPosition(result).column = ResultDataType().createColumnConst(col_left->size(), toField(res));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
auto col_res = ColumnVector<ResultType>::create();
|
||||
auto col_res = ColVecResult::create();
|
||||
auto & vec_res = col_res->getData();
|
||||
vec_res.resize(block.rows());
|
||||
if (auto col_left = checkAndGetColumnConst<ColumnVector<T0>>(col_left_raw))
|
||||
if (auto col_left = checkAndGetColumnConst<ColVecT0>(col_left_raw))
|
||||
{
|
||||
if (auto col_right = checkAndGetColumn<ColumnVector<T1>>(col_right_raw))
|
||||
OpImpl::constant_vector(col_left->template getValue<T0>(), col_right->getData(), vec_res);
|
||||
if (auto col_right = checkAndGetColumn<ColVecT1>(col_right_raw))
|
||||
{
|
||||
if constexpr (result_is_decimal)
|
||||
{
|
||||
ResultDataType type = decimalResultType(left, right, is_multiply, is_division);
|
||||
vec_res.setScale(type.getScale());
|
||||
|
||||
typename ResultDataType::FieldType scale_a = type.scaleFactorFor(left, is_multiply);
|
||||
typename ResultDataType::FieldType scale_b = type.scaleFactorFor(right, is_multiply || is_division);
|
||||
if constexpr (IsDecimal<RightDataType> && is_division)
|
||||
scale_a = right.getScaleMultiplier();
|
||||
OpImpl::constant_vector(col_left->template getValue<T0>(), col_right->getData(), vec_res, scale_a, scale_b);
|
||||
}
|
||||
else
|
||||
OpImpl::constant_vector(col_left->template getValue<T0>(), col_right->getData(), vec_res);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else if (auto col_left = checkAndGetColumn<ColumnVector<T0>>(col_left_raw))
|
||||
else if (auto col_left = checkAndGetColumn<ColVecT0>(col_left_raw))
|
||||
{
|
||||
if (auto col_right = checkAndGetColumn<ColumnVector<T1>>(col_right_raw))
|
||||
OpImpl::vector_vector(col_left->getData(), col_right->getData(), vec_res);
|
||||
else if (auto col_right = checkAndGetColumnConst<ColumnVector<T1>>(col_right_raw))
|
||||
OpImpl::vector_constant(col_left->getData(), col_right->template getValue<T1>(), vec_res);
|
||||
if constexpr (result_is_decimal)
|
||||
{
|
||||
ResultDataType type = decimalResultType(left, right, is_multiply, is_division);
|
||||
vec_res.setScale(type.getScale());
|
||||
|
||||
typename ResultDataType::FieldType scale_a = type.scaleFactorFor(left, is_multiply);
|
||||
typename ResultDataType::FieldType scale_b = type.scaleFactorFor(right, is_multiply || is_division);
|
||||
if constexpr (IsDecimal<RightDataType> && is_division)
|
||||
scale_a = right.getScaleMultiplier();
|
||||
if (auto col_right = checkAndGetColumn<ColVecT1>(col_right_raw))
|
||||
OpImpl::vector_vector(col_left->getData(), col_right->getData(), vec_res, scale_a, scale_b);
|
||||
else if (auto col_right = checkAndGetColumnConst<ColVecT1>(col_right_raw))
|
||||
OpImpl::vector_constant(col_left->getData(), col_right->template getValue<T1>(), vec_res, scale_a, scale_b);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
{
|
||||
if (auto col_right = checkAndGetColumn<ColVecT1>(col_right_raw))
|
||||
OpImpl::vector_vector(col_left->getData(), col_right->getData(), vec_res);
|
||||
else if (auto col_right = checkAndGetColumnConst<ColVecT1>(col_right_raw))
|
||||
OpImpl::vector_constant(col_left->getData(), col_right->template getValue<T1>(), vec_res);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1005,6 +1329,8 @@ struct FunctionUnaryArithmeticMonotonicity;
|
||||
template <template <typename> class Op, typename Name, bool is_injective>
|
||||
class FunctionUnaryArithmetic : public IFunction
|
||||
{
|
||||
static constexpr bool allow_decimal = std::is_same_v<Op<Int8>, NegateImpl<Int8>> || std::is_same_v<Op<Int8>, AbsImpl<Int8>>;
|
||||
|
||||
template <typename F>
|
||||
static bool castType(const IDataType * type, F && f)
|
||||
{
|
||||
@ -1018,7 +1344,10 @@ class FunctionUnaryArithmetic : public IFunction
|
||||
DataTypeInt32,
|
||||
DataTypeInt64,
|
||||
DataTypeFloat32,
|
||||
DataTypeFloat64
|
||||
DataTypeFloat64,
|
||||
DataTypeDecimal<Dec32>,
|
||||
DataTypeDecimal<Dec64>,
|
||||
DataTypeDecimal<Dec128>
|
||||
>(type, std::forward<F>(f));
|
||||
}
|
||||
|
||||
@ -1041,8 +1370,17 @@ public:
|
||||
DataTypePtr result;
|
||||
bool valid = castType(arguments[0].get(), [&](const auto & type)
|
||||
{
|
||||
using T0 = typename std::decay_t<decltype(type)>::FieldType;
|
||||
result = std::make_shared<DataTypeNumber<typename Op<T0>::ResultType>>();
|
||||
using DataType = std::decay_t<decltype(type)>;
|
||||
using T0 = typename DataType::FieldType;
|
||||
|
||||
if constexpr (IsDecimal<DataType>)
|
||||
{
|
||||
if constexpr (!allow_decimal)
|
||||
return false;
|
||||
result = std::make_shared<DataType>(type.getPrecision(), type.getScale());
|
||||
}
|
||||
else
|
||||
result = std::make_shared<DataTypeNumber<typename Op<T0>::ResultType>>();
|
||||
return true;
|
||||
});
|
||||
if (!valid)
|
||||
@ -1055,16 +1393,37 @@ public:
|
||||
{
|
||||
bool valid = castType(block.getByPosition(arguments[0]).type.get(), [&](const auto & type)
|
||||
{
|
||||
using T0 = typename std::decay_t<decltype(type)>::FieldType;
|
||||
if (auto col = checkAndGetColumn<ColumnVector<T0>>(block.getByPosition(arguments[0]).column.get()))
|
||||
using DataType = std::decay_t<decltype(type)>;
|
||||
using T0 = typename DataType::FieldType;
|
||||
|
||||
if constexpr (IsDecimal<DataType>)
|
||||
{
|
||||
auto col_res = ColumnVector<typename Op<T0>::ResultType>::create();
|
||||
auto & vec_res = col_res->getData();
|
||||
vec_res.resize(col->getData().size());
|
||||
UnaryOperationImpl<T0, Op<T0>>::vector(col->getData(), vec_res);
|
||||
block.getByPosition(result).column = std::move(col_res);
|
||||
return true;
|
||||
if constexpr (allow_decimal)
|
||||
{
|
||||
if (auto col = checkAndGetColumn<ColumnVector<T0>>(block.getByPosition(arguments[0]).column.get()))
|
||||
{
|
||||
auto col_res = ColumnVector<typename Op<T0>::ResultType>::create();
|
||||
auto & vec_res = col_res->getData();
|
||||
vec_res.resize(col->getData().size());
|
||||
UnaryOperationImpl<T0, Op<T0>>::vector(col->getData(), vec_res);
|
||||
block.getByPosition(result).column = std::move(col_res);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto col = checkAndGetColumn<ColumnVector<T0>>(block.getByPosition(arguments[0]).column.get()))
|
||||
{
|
||||
auto col_res = ColumnVector<typename Op<T0>::ResultType>::create();
|
||||
auto & vec_res = col_res->getData();
|
||||
vec_res.resize(col->getData().size());
|
||||
UnaryOperationImpl<T0, Op<T0>>::vector(col->getData(), vec_res);
|
||||
block.getByPosition(result).column = std::move(col_res);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
if (!valid)
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <Columns/ColumnArray.h>
|
||||
|
||||
#include <DataTypes/DataTypesNumber.h>
|
||||
#include <DataTypes/DataTypesDecimal.h>
|
||||
#include <DataTypes/DataTypeDateTime.h>
|
||||
#include <DataTypes/DataTypeDate.h>
|
||||
#include <DataTypes/DataTypeString.h>
|
||||
@ -35,11 +36,17 @@
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int DECIMAL_OVERFLOW;
|
||||
}
|
||||
|
||||
|
||||
/** Comparison functions: ==, !=, <, >, <=, >=.
|
||||
* The comparison functions always return 0 or 1 (UInt8).
|
||||
*
|
||||
* You can compare the following types:
|
||||
* - numbers;
|
||||
* - numbers and decimals;
|
||||
* - strings and fixed strings;
|
||||
* - dates;
|
||||
* - datetimes;
|
||||
@ -188,6 +195,287 @@ struct NumComparisonImpl
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
inline bool allowDecimalComparison(const IDataType & left_type, const IDataType & right_type)
|
||||
{
|
||||
if (isDecimal(left_type))
|
||||
{
|
||||
if (isDecimal(right_type) || notDecimalButComparableToDecimal(right_type))
|
||||
return true;
|
||||
}
|
||||
else if (notDecimalButComparableToDecimal(left_type) && isDecimal(right_type))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <size_t > struct ConstructDecInt { using Type = Int32; };
|
||||
template <> struct ConstructDecInt<8> { using Type = Int64; };
|
||||
template <> struct ConstructDecInt<16> { using Type = Int128; };
|
||||
|
||||
template <typename T, typename U>
|
||||
struct DecCompareInt
|
||||
{
|
||||
using Type = typename ConstructDecInt<(!decTrait<U>() || sizeof(T) > sizeof(U)) ? sizeof(T) : sizeof(U)>::Type;
|
||||
};
|
||||
|
||||
///
|
||||
template <typename A, typename B, template <typename, typename> typename Operation, bool _actual = decTrait<A>() || decTrait<B>()>
|
||||
class DecimalComparison
|
||||
{
|
||||
public:
|
||||
using CompareInt = typename DecCompareInt<A, B>::Type;
|
||||
using Op = Operation<CompareInt, CompareInt>;
|
||||
using ColVecA = ColumnVector<A>;
|
||||
using ColVecB = ColumnVector<B>;
|
||||
using ArrayA = typename ColVecA::Container;
|
||||
using ArrayB = typename ColVecB::Container;
|
||||
|
||||
DecimalComparison(Block & block, size_t result, const ColumnWithTypeAndName & col_left, const ColumnWithTypeAndName & col_right)
|
||||
{
|
||||
if (!apply(block, result, col_left, col_right))
|
||||
throw Exception("Wrong decimal comparison with " + col_left.type->getName() + " and " + col_right.type->getName(),
|
||||
ErrorCodes::LOGICAL_ERROR);
|
||||
}
|
||||
|
||||
static bool apply(Block & block, size_t result [[maybe_unused]],
|
||||
const ColumnWithTypeAndName & col_left, const ColumnWithTypeAndName & col_right)
|
||||
{
|
||||
if constexpr (_actual)
|
||||
{
|
||||
ColumnPtr c_res;
|
||||
Shift shift = getScales<A, B>(col_left.type, col_right.type);
|
||||
|
||||
c_res = applyWithScale(col_left.column, col_right.column, shift);
|
||||
if (c_res)
|
||||
block.getByPosition(result).column = std::move(c_res);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool compare(A a, B b, UInt32 scale_a, UInt32 scale_b)
|
||||
{
|
||||
static const UInt32 max_scale = maxDecimalPrecision<Dec128>();
|
||||
if (scale_a > max_scale || scale_b > max_scale)
|
||||
throw Exception("Bad scale of decimal field", ErrorCodes::DECIMAL_OVERFLOW);
|
||||
|
||||
Shift shift;
|
||||
if (scale_a < scale_b)
|
||||
shift.a = DataTypeDecimal<B>(maxDecimalPrecision<B>(), scale_b).getScaleMultiplier(scale_b - scale_a);
|
||||
if (scale_a > scale_b)
|
||||
shift.b = DataTypeDecimal<A>(maxDecimalPrecision<A>(), scale_a).getScaleMultiplier(scale_a - scale_b);
|
||||
|
||||
return applyWithScale(a, b, shift);
|
||||
}
|
||||
|
||||
private:
|
||||
struct Shift
|
||||
{
|
||||
CompareInt a = 1;
|
||||
CompareInt b = 1;
|
||||
|
||||
bool none() const { return a == 1 && b == 1; }
|
||||
bool left() const { return a != 1; }
|
||||
bool right() const { return b != 1; }
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
static auto applyWithScale(T a, U b, const Shift & shift)
|
||||
{
|
||||
if (shift.left())
|
||||
return apply<true, false>(a, b, shift.a);
|
||||
else if (shift.right())
|
||||
return apply<false, true>(a, b, shift.b);
|
||||
return apply<false, false>(a, b, 1);
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
static std::enable_if_t<decTrait<T>() && decTrait<U>(), Shift>
|
||||
getScales(const DataTypePtr & left_type, const DataTypePtr & right_type)
|
||||
{
|
||||
const DataTypeDecimal<T> * decimal0 = checkDecimal<T>(*left_type);
|
||||
const DataTypeDecimal<U> * decimal1 = checkDecimal<U>(*right_type);
|
||||
|
||||
Shift shift;
|
||||
if (decimal0 && decimal1)
|
||||
{
|
||||
auto result_type = decimalResultType(*decimal0, *decimal1, false, false);
|
||||
shift.a = result_type.scaleFactorFor(*decimal0, false);
|
||||
shift.b = result_type.scaleFactorFor(*decimal1, false);
|
||||
}
|
||||
else if (decimal0)
|
||||
shift.b = decimal0->getScaleMultiplier();
|
||||
else if (decimal1)
|
||||
shift.a = decimal1->getScaleMultiplier();
|
||||
|
||||
return shift;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
static std::enable_if_t<decTrait<T>() && !decTrait<U>(), Shift>
|
||||
getScales(const DataTypePtr & left_type, const DataTypePtr &)
|
||||
{
|
||||
Shift shift;
|
||||
const DataTypeDecimal<T> * decimal0 = checkDecimal<T>(*left_type);
|
||||
if (decimal0)
|
||||
shift.b = decimal0->getScaleMultiplier();
|
||||
return shift;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
static std::enable_if_t<!decTrait<T>() && decTrait<U>(), Shift>
|
||||
getScales(const DataTypePtr &, const DataTypePtr & right_type)
|
||||
{
|
||||
Shift shift;
|
||||
const DataTypeDecimal<U> * decimal1 = checkDecimal<U>(*right_type);
|
||||
if (decimal1)
|
||||
shift.a = decimal1->getScaleMultiplier();
|
||||
return shift;
|
||||
}
|
||||
|
||||
template <bool scale_left, bool scale_right>
|
||||
static ColumnPtr apply(const ColumnPtr & c0, const ColumnPtr & c1, CompareInt scale)
|
||||
{
|
||||
auto c_res = ColumnUInt8::create();
|
||||
|
||||
if constexpr (_actual)
|
||||
{
|
||||
bool c0_const = c0->isColumnConst();
|
||||
bool c1_const = c1->isColumnConst();
|
||||
|
||||
if (c0_const && c1_const)
|
||||
{
|
||||
const ColumnConst * c0_const = checkAndGetColumnConst<ColVecA>(c0.get());
|
||||
const ColumnConst * c1_const = checkAndGetColumnConst<ColVecB>(c1.get());
|
||||
|
||||
A a = c0_const->template getValue<A>();
|
||||
B b = c1_const->template getValue<B>();
|
||||
UInt8 res = apply<scale_left, scale_right>(a, b, scale);
|
||||
return DataTypeUInt8().createColumnConst(c0->size(), toField(res));
|
||||
}
|
||||
|
||||
ColumnUInt8::Container & vec_res = c_res->getData();
|
||||
vec_res.resize(c0->size());
|
||||
|
||||
if (c0_const)
|
||||
{
|
||||
const ColumnConst * c0_const = checkAndGetColumnConst<ColVecA>(c0.get());
|
||||
A a = c0_const->template getValue<A>();
|
||||
if (const ColVecB * c1_vec = checkAndGetColumn<ColVecB>(c1.get()))
|
||||
constant_vector<scale_left, scale_right>(a, c1_vec->getData(), vec_res, scale);
|
||||
else if (const ColVecB * c1_vec = checkAndGetColumn<ColVecB>(c1.get()))
|
||||
constant_vector<scale_left, scale_right>(a, c1_vec->getData(), vec_res, scale);
|
||||
}
|
||||
else if (c1_const)
|
||||
{
|
||||
const ColumnConst * c1_const = checkAndGetColumnConst<ColVecB>(c1.get());
|
||||
B b = c1_const->template getValue<B>();
|
||||
if (const ColVecA * c0_vec = checkAndGetColumn<ColVecA>(c0.get()))
|
||||
vector_constant<scale_left, scale_right>(c0_vec->getData(), b, vec_res, scale);
|
||||
else if (const ColVecA * c0_vec = checkAndGetColumn<ColVecA>(c0.get()))
|
||||
vector_constant<scale_left, scale_right>(c0_vec->getData(), b, vec_res, scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (const ColVecA * c0_vec = checkAndGetColumn<ColVecA>(c0.get()))
|
||||
{
|
||||
if (const ColVecB * c1_vec = checkAndGetColumn<ColVecB>(c1.get()))
|
||||
vector_vector<scale_left, scale_right>(c0_vec->getData(), c1_vec->getData(), vec_res, scale);
|
||||
else if (const ColVecB * c1_vec = checkAndGetColumn<ColVecB>(c1.get()))
|
||||
vector_vector<scale_left, scale_right>(c0_vec->getData(), c1_vec->getData(), vec_res, scale);
|
||||
}
|
||||
else if (const ColVecA * c0_vec = checkAndGetColumn<ColVecA>(c0.get()))
|
||||
{
|
||||
if (const ColVecB * c1_vec = checkAndGetColumn<ColVecB>(c1.get()))
|
||||
vector_vector<scale_left, scale_right>(c0_vec->getData(), c1_vec->getData(), vec_res, scale);
|
||||
else if (const ColVecB * c1_vec = checkAndGetColumn<ColVecB>(c1.get()))
|
||||
vector_vector<scale_left, scale_right>(c0_vec->getData(), c1_vec->getData(), vec_res, scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return c_res;
|
||||
}
|
||||
|
||||
template <bool scale_left, bool scale_right>
|
||||
static NO_INLINE UInt8 apply(A a, B b, CompareInt scale [[maybe_unused]])
|
||||
{
|
||||
CompareInt x = a;
|
||||
CompareInt y = b;
|
||||
bool overflow = false;
|
||||
|
||||
if constexpr (sizeof(A) > sizeof(CompareInt))
|
||||
overflow |= (A(x) != a);
|
||||
if constexpr (sizeof(B) > sizeof(CompareInt))
|
||||
overflow |= (B(y) != b);
|
||||
if constexpr (std::is_unsigned_v<A>)
|
||||
overflow |= (x < 0);
|
||||
if constexpr (std::is_unsigned_v<B>)
|
||||
overflow |= (y < 0);
|
||||
|
||||
if constexpr (scale_left)
|
||||
overflow |= common::mulOverflow(x, scale, x);
|
||||
if constexpr (scale_right)
|
||||
overflow |= common::mulOverflow(y, scale, y);
|
||||
|
||||
if (overflow)
|
||||
throw Exception("Can't compare", ErrorCodes::DECIMAL_OVERFLOW);
|
||||
|
||||
return Op::apply(x, y);
|
||||
}
|
||||
|
||||
template <bool scale_left, bool scale_right>
|
||||
static void NO_INLINE vector_vector(const ArrayA & a, const ArrayB & b, PaddedPODArray<UInt8> & c,
|
||||
CompareInt scale)
|
||||
{
|
||||
size_t size = a.size();
|
||||
const A * a_pos = &a[0];
|
||||
const B * b_pos = &b[0];
|
||||
UInt8 * c_pos = &c[0];
|
||||
const A * a_end = a_pos + size;
|
||||
|
||||
while (a_pos < a_end)
|
||||
{
|
||||
*c_pos = apply<scale_left, scale_right>(*a_pos, *b_pos, scale);
|
||||
++a_pos;
|
||||
++b_pos;
|
||||
++c_pos;
|
||||
}
|
||||
}
|
||||
|
||||
template <bool scale_left, bool scale_right>
|
||||
static void NO_INLINE vector_constant(const ArrayA & a, B b, PaddedPODArray<UInt8> & c, CompareInt scale)
|
||||
{
|
||||
size_t size = a.size();
|
||||
const A * a_pos = &a[0];
|
||||
UInt8 * c_pos = &c[0];
|
||||
const A * a_end = a_pos + size;
|
||||
|
||||
while (a_pos < a_end)
|
||||
{
|
||||
*c_pos = apply<scale_left, scale_right>(*a_pos, b, scale);
|
||||
++a_pos;
|
||||
++c_pos;
|
||||
}
|
||||
}
|
||||
|
||||
template <bool scale_left, bool scale_right>
|
||||
static void NO_INLINE constant_vector(A a, const ArrayB & b, PaddedPODArray<UInt8> & c, CompareInt scale)
|
||||
{
|
||||
size_t size = b.size();
|
||||
const B * b_pos = &b[0];
|
||||
UInt8 * c_pos = &c[0];
|
||||
const B * b_end = b_pos + size;
|
||||
|
||||
while (b_pos < b_end)
|
||||
{
|
||||
*c_pos = apply<scale_left, scale_right>(a, *b_pos, scale);
|
||||
++b_pos;
|
||||
++c_pos;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline int memcmp16(const void * a, const void * b)
|
||||
{
|
||||
@ -696,6 +984,7 @@ private:
|
||||
|| executeNumRightType<T0, Int16>(block, result, col_left, col_right_untyped)
|
||||
|| executeNumRightType<T0, Int32>(block, result, col_left, col_right_untyped)
|
||||
|| executeNumRightType<T0, Int64>(block, result, col_left, col_right_untyped)
|
||||
|| executeNumRightType<T0, Int128>(block, result, col_left, col_right_untyped)
|
||||
|| executeNumRightType<T0, Float32>(block, result, col_left, col_right_untyped)
|
||||
|| executeNumRightType<T0, Float64>(block, result, col_left, col_right_untyped))
|
||||
return true;
|
||||
@ -715,6 +1004,7 @@ private:
|
||||
|| executeNumConstRightType<T0, Int16>(block, result, col_left, col_right_untyped)
|
||||
|| executeNumConstRightType<T0, Int32>(block, result, col_left, col_right_untyped)
|
||||
|| executeNumConstRightType<T0, Int64>(block, result, col_left, col_right_untyped)
|
||||
|| executeNumConstRightType<T0, Int128>(block, result, col_left, col_right_untyped)
|
||||
|| executeNumConstRightType<T0, Float32>(block, result, col_left, col_right_untyped)
|
||||
|| executeNumConstRightType<T0, Float64>(block, result, col_left, col_right_untyped))
|
||||
return true;
|
||||
@ -727,6 +1017,16 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
void executeDecimal(Block & block, size_t result, const ColumnWithTypeAndName & col_left, const ColumnWithTypeAndName & col_right)
|
||||
{
|
||||
size_t left_number = col_left.type->getTypeNumber();
|
||||
size_t right_number = col_right.type->getTypeNumber();
|
||||
|
||||
if (!callByNumbers<DecimalComparison, Op>(left_number, right_number, block, result, col_left, col_right))
|
||||
throw Exception("Wrong call for " + getName() + " with " + col_left.type->getName() + " and " + col_right.type->getName(),
|
||||
ErrorCodes::LOGICAL_ERROR);
|
||||
}
|
||||
|
||||
bool executeString(Block & block, size_t result, const IColumn * c0, const IColumn * c1)
|
||||
{
|
||||
const ColumnString * c0_string = checkAndGetColumn<ColumnString>(c0);
|
||||
@ -1172,6 +1472,8 @@ public:
|
||||
const auto & col_with_type_and_name_right = block.getByPosition(arguments[1]);
|
||||
const IColumn * col_left_untyped = col_with_type_and_name_left.column.get();
|
||||
const IColumn * col_right_untyped = col_with_type_and_name_right.column.get();
|
||||
const DataTypePtr & left_type = col_with_type_and_name_left.type;
|
||||
const DataTypePtr & right_type = col_with_type_and_name_right.type;
|
||||
|
||||
const bool left_is_num = col_left_untyped->isNumeric();
|
||||
const bool right_is_num = col_right_untyped->isNumeric();
|
||||
@ -1187,26 +1489,35 @@ public:
|
||||
|| executeNumLeftType<Int16>(block, result, col_left_untyped, col_right_untyped)
|
||||
|| executeNumLeftType<Int32>(block, result, col_left_untyped, col_right_untyped)
|
||||
|| executeNumLeftType<Int64>(block, result, col_left_untyped, col_right_untyped)
|
||||
|| executeNumLeftType<Int128>(block, result, col_left_untyped, col_right_untyped)
|
||||
|| executeNumLeftType<Float32>(block, result, col_left_untyped, col_right_untyped)
|
||||
|| executeNumLeftType<Float64>(block, result, col_left_untyped, col_right_untyped)))
|
||||
throw Exception("Illegal column " + col_left_untyped->getName()
|
||||
+ " of first argument of function " + getName(),
|
||||
ErrorCodes::ILLEGAL_COLUMN);
|
||||
}
|
||||
else if (checkAndGetDataType<DataTypeTuple>(col_with_type_and_name_left.type.get()))
|
||||
else if (checkAndGetDataType<DataTypeTuple>(left_type.get()))
|
||||
{
|
||||
executeTuple(block, result, col_with_type_and_name_left, col_with_type_and_name_right, input_rows_count);
|
||||
}
|
||||
else if (isDecimal(*left_type) || isDecimal(*right_type))
|
||||
{
|
||||
if (!allowDecimalComparison(*left_type, *right_type))
|
||||
throw Exception("No operation " + getName() + " between " + left_type->getName() + " and " + right_type->getName(),
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||
|
||||
executeDecimal(block, result, col_with_type_and_name_left, col_with_type_and_name_right);
|
||||
}
|
||||
else if (!left_is_num && !right_is_num && executeString(block, result, col_left_untyped, col_right_untyped))
|
||||
{
|
||||
}
|
||||
else if (col_with_type_and_name_left.type->equals(*col_with_type_and_name_right.type))
|
||||
else if (left_type->equals(*right_type))
|
||||
{
|
||||
executeGenericIdenticalTypes(block, result, col_left_untyped, col_right_untyped);
|
||||
}
|
||||
else if (executeDateOrDateTimeOrEnumOrUUIDWithConstString(
|
||||
block, result, col_left_untyped, col_right_untyped,
|
||||
col_with_type_and_name_left.type, col_with_type_and_name_right.type,
|
||||
left_type, right_type,
|
||||
left_is_num, input_rows_count))
|
||||
{
|
||||
}
|
||||
|
@ -637,6 +637,7 @@ inline std::enable_if_t<std::is_arithmetic_v<T>, void>
|
||||
readBinary(T & x, ReadBuffer & buf) { readPODBinary(x, buf); }
|
||||
|
||||
inline void readBinary(String & x, ReadBuffer & buf) { readStringBinary(x, buf); }
|
||||
inline void readBinary(Int128 & x, ReadBuffer & buf) { readPODBinary(x, buf); }
|
||||
inline void readBinary(UInt128 & x, ReadBuffer & buf) { readPODBinary(x, buf); }
|
||||
inline void readBinary(UInt256 & x, ReadBuffer & buf) { readPODBinary(x, buf); }
|
||||
inline void readBinary(LocalDate & x, ReadBuffer & buf) { readPODBinary(x, buf); }
|
||||
|
@ -674,6 +674,7 @@ writeBinary(const T & x, WriteBuffer & buf) { writePODBinary(x, buf); }
|
||||
|
||||
inline void writeBinary(const String & x, WriteBuffer & buf) { writeStringBinary(x, buf); }
|
||||
inline void writeBinary(const StringRef & x, WriteBuffer & buf) { writeStringBinary(x, buf); }
|
||||
inline void writeBinary(const Int128 & x, WriteBuffer & buf) { writePODBinary(x, buf); }
|
||||
inline void writeBinary(const UInt128 & x, WriteBuffer & buf) { writePODBinary(x, buf); }
|
||||
inline void writeBinary(const UInt256 & x, WriteBuffer & buf) { writePODBinary(x, buf); }
|
||||
inline void writeBinary(const LocalDate & x, WriteBuffer & buf) { writePODBinary(x, buf); }
|
||||
|
@ -150,6 +150,13 @@ namespace detail
|
||||
}
|
||||
|
||||
|
||||
inline void writeLeadingMinus(WriteBuffer & buf)
|
||||
{
|
||||
buf.nextIfAtEnd();
|
||||
*buf.position() = '-';
|
||||
++buf.position();
|
||||
}
|
||||
|
||||
/** Wrapper for signed numbers.
|
||||
*/
|
||||
template <typename T>
|
||||
@ -172,14 +179,30 @@ namespace detail
|
||||
if (x < 0)
|
||||
{
|
||||
x = -x;
|
||||
buf.nextIfAtEnd();
|
||||
*buf.position() = '-';
|
||||
++buf.position();
|
||||
writeLeadingMinus(buf);
|
||||
}
|
||||
|
||||
writeUIntText(static_cast<std::make_unsigned_t<T>>(x), buf);
|
||||
}
|
||||
|
||||
#if 1
|
||||
inline void writeSIntText(__int128 x, WriteBuffer & buf)
|
||||
{
|
||||
if (unlikely(-x < 0))
|
||||
{
|
||||
buf.write("-170141183460469231731687303715884105728", 40);
|
||||
return;
|
||||
}
|
||||
|
||||
if (x < 0)
|
||||
{
|
||||
x = -x;
|
||||
writeLeadingMinus(buf);
|
||||
}
|
||||
|
||||
writeUIntText(static_cast<unsigned __int128>(x), buf);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -187,8 +210,10 @@ template <typename T>
|
||||
std::enable_if_t<std::is_signed_v<T>, void> writeIntText(T x, WriteBuffer & buf)
|
||||
{
|
||||
detail::writeSIntText(x, buf);
|
||||
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_unsigned_v<T>, void> writeIntText(T x, WriteBuffer & buf)
|
||||
{
|
||||
|
@ -96,6 +96,7 @@ namespace DB
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int CANNOT_PARSE_NUMBER;
|
||||
extern const int ARGUMENT_OUT_OF_BOUND;
|
||||
}
|
||||
|
||||
|
||||
@ -552,6 +553,85 @@ ReturnType readFloatTextSimpleImpl(T & x, ReadBuffer & buf)
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline void readDecimalText(ReadBuffer & buf, T & x, unsigned int precision, unsigned int & scale, bool digits_only = false)
|
||||
{
|
||||
x = 0;
|
||||
typename T::NativeType sign = 1;
|
||||
bool leading_zeores = true;
|
||||
bool trailing_zeores = false;
|
||||
bool after_point = false;
|
||||
|
||||
if (buf.eof())
|
||||
throwReadAfterEOF();
|
||||
|
||||
if (!buf.eof())
|
||||
{
|
||||
switch (*buf.position())
|
||||
{
|
||||
case '-':
|
||||
sign = -1;
|
||||
[[fallthrough]];
|
||||
case '+':
|
||||
++buf.position();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (!buf.eof())
|
||||
{
|
||||
const char & byte = *buf.position();
|
||||
switch (byte)
|
||||
{
|
||||
case '.':
|
||||
after_point = true;
|
||||
if (scale == 0)
|
||||
trailing_zeores = true;
|
||||
break;
|
||||
case '1': [[fallthrough]];
|
||||
case '2': [[fallthrough]];
|
||||
case '3': [[fallthrough]];
|
||||
case '4': [[fallthrough]];
|
||||
case '5': [[fallthrough]];
|
||||
case '6': [[fallthrough]];
|
||||
case '7': [[fallthrough]];
|
||||
case '8': [[fallthrough]];
|
||||
case '9':
|
||||
leading_zeores = false;
|
||||
if (trailing_zeores || precision == 0)
|
||||
throw Exception("Cannot read decimal value", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||
[[fallthrough]];
|
||||
case '0':
|
||||
{
|
||||
/// ignore leading and trailing zeroes
|
||||
if (likely(!leading_zeores && !trailing_zeores))
|
||||
{
|
||||
if (precision == 0 || precision < scale || ((precision == scale) && !after_point))
|
||||
throw Exception("Cannot read decimal value", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||
--precision;
|
||||
x = x * 10 + (byte - '0');
|
||||
}
|
||||
if (after_point && scale)
|
||||
{
|
||||
--scale;
|
||||
if (!scale)
|
||||
trailing_zeores = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
if (digits_only)
|
||||
throw Exception("Unexpected symbol while reading decimal", ErrorCodes::CANNOT_PARSE_NUMBER);
|
||||
x *= sign;
|
||||
return;
|
||||
}
|
||||
++buf.position();
|
||||
}
|
||||
x *= sign;
|
||||
}
|
||||
|
||||
|
||||
template <typename T> void readFloatTextPrecise(T & x, ReadBuffer & in) { readFloatTextPreciseImpl<T, void>(x, in); }
|
||||
template <typename T> bool tryReadFloatTextPrecise(T & x, ReadBuffer & in) { return readFloatTextPreciseImpl<T, bool>(x, in); }
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <DataTypes/DataTypeFactory.h>
|
||||
#include <DataTypes/NestedUtils.h>
|
||||
#include <DataTypes/DataTypesNumber.h>
|
||||
#include <DataTypes/DataTypesDecimal.h>
|
||||
|
||||
#include <Databases/DatabaseFactory.h>
|
||||
#include <Databases/IDatabase.h>
|
||||
@ -353,8 +354,9 @@ void InterpreterCreateQuery::checkSupportedTypes(const ColumnsDescription & colu
|
||||
{
|
||||
const auto & settings = context.getSettingsRef();
|
||||
bool allow_low_cardinality = settings.allow_experimental_low_cardinality_type != 0;
|
||||
bool allow_decimal = settings.allow_experimental_decimal_type;
|
||||
|
||||
if (allow_low_cardinality)
|
||||
if (allow_low_cardinality && allow_decimal)
|
||||
return;
|
||||
|
||||
auto check_types = [&](const NamesAndTypesList & list)
|
||||
@ -368,6 +370,12 @@ void InterpreterCreateQuery::checkSupportedTypes(const ColumnsDescription & colu
|
||||
+ "Set setting allow_experimental_low_cardinality_type = 1 in order to allow it.";
|
||||
throw Exception(message, ErrorCodes::ILLEGAL_COLUMN);
|
||||
}
|
||||
if (!allow_decimal && column.type && isDecimal(*column.type))
|
||||
{
|
||||
String message = "Cannot create table with column " + column.name + " which type is " + column.type->getName()
|
||||
+ ". Set setting allow_experimental_decimal_type = 1 in order to allow it.";
|
||||
throw Exception(message, ErrorCodes::ILLEGAL_COLUMN);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -272,6 +272,7 @@ struct Settings
|
||||
M(SettingUInt64, low_cardinality_max_dictionary_size, 8192, "Maximum size (in rows) of shared global dictionary for LowCardinality type.") \
|
||||
M(SettingBool, low_cardinality_use_single_dictionary_for_part, false, "LowCardinality type serialization setting. If is true, than will use additional keys when global dictionary overflows. Otherwise, will create several shared dictionaries.") \
|
||||
M(SettingBool, allow_experimental_low_cardinality_type, false, "Allows to create table with LowCardinality types.") \
|
||||
M(SettingBool, allow_experimental_decimal_type, false, "Enables Decimal data type.") \
|
||||
\
|
||||
M(SettingBool, prefer_localhost_replica, 1, "1 - always send query to local replica, if it exists. 0 - choose replica to send query between local and remote ones according to load_balancing") \
|
||||
M(SettingUInt64, max_fetch_partition_retries_count, 5, "Amount of retries while fetching partition from another host.") \
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <DataTypes/DataTypeArray.h>
|
||||
#include <DataTypes/DataTypeTuple.h>
|
||||
#include <DataTypes/DataTypesNumber.h>
|
||||
#include <DataTypes/DataTypesDecimal.h>
|
||||
#include <DataTypes/DataTypeString.h>
|
||||
#include <DataTypes/DataTypeFixedString.h>
|
||||
#include <DataTypes/DataTypeDate.h>
|
||||
@ -73,6 +74,46 @@ static Field convertNumericType(const Field & from, const IDataType & type)
|
||||
}
|
||||
|
||||
|
||||
template <typename From, typename To>
|
||||
static Field convertIntToDecimalType(const Field & from, const To & type)
|
||||
{
|
||||
using FieldType = typename To::FieldType;
|
||||
|
||||
From value = from.get<From>();
|
||||
if (!type.canStoreWhole(value))
|
||||
throw Exception("Number is too much to place in " + type.getName(), ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||
|
||||
FieldType scaled_value = type.getScaleMultiplier() * value;
|
||||
return Field(typename NearestFieldType<FieldType>::Type(scaled_value));
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
static Field convertStringToDecimalType(const Field & from, const DataTypeDecimal<T> & type)
|
||||
{
|
||||
using FieldType = typename DataTypeDecimal<T>::FieldType;
|
||||
|
||||
const String & str_value = from.get<String>();
|
||||
T value = type.parseFromString(str_value);
|
||||
return Field(typename NearestFieldType<FieldType>::Type(value));
|
||||
}
|
||||
|
||||
|
||||
template <typename To>
|
||||
static Field convertDecimalType(const Field & from, const To & type)
|
||||
{
|
||||
if (from.getType() == Field::Types::UInt64)
|
||||
return convertIntToDecimalType<UInt64>(from, type);
|
||||
if (from.getType() == Field::Types::Int64)
|
||||
return convertIntToDecimalType<Int64>(from, type);
|
||||
if (from.getType() == Field::Types::String)
|
||||
return convertStringToDecimalType(from, type);
|
||||
|
||||
throw Exception("Type mismatch in IN or VALUES section. Expected: " + type.getName() + ". Got: "
|
||||
+ Field::Types::toString(from.getType()), ErrorCodes::TYPE_MISMATCH);
|
||||
}
|
||||
|
||||
|
||||
DayNum stringToDate(const String & s)
|
||||
{
|
||||
ReadBufferFromString in(s);
|
||||
@ -124,6 +165,9 @@ Field convertFieldToTypeImpl(const Field & src, const IDataType & type)
|
||||
if (typeid_cast<const DataTypeInt64 *>(&type)) return convertNumericType<Int64>(src, type);
|
||||
if (typeid_cast<const DataTypeFloat32 *>(&type)) return convertNumericType<Float32>(src, type);
|
||||
if (typeid_cast<const DataTypeFloat64 *>(&type)) return convertNumericType<Float64>(src, type);
|
||||
if (auto * ptype = typeid_cast<const DataTypeDecimal<Dec32> *>(&type)) return convertDecimalType(src, *ptype);
|
||||
if (auto * ptype = typeid_cast<const DataTypeDecimal<Dec64> *>(&type)) return convertDecimalType(src, *ptype);
|
||||
if (auto * ptype = typeid_cast<const DataTypeDecimal<Dec128> *>(&type)) return convertDecimalType(src, *ptype);
|
||||
|
||||
const bool is_date = typeid_cast<const DataTypeDate *>(&type);
|
||||
bool is_datetime = false;
|
||||
|
@ -1,73 +1,105 @@
|
||||
#include <Poco/Ext/SessionPoolHelpers.h>
|
||||
#include <Storages/transformQueryForExternalDatabase.h>
|
||||
#include <Storages/StorageODBC.h>
|
||||
#include <Storages/StorageFactory.h>
|
||||
#include <Interpreters/evaluateConstantExpression.h>
|
||||
#include <Dictionaries/ODBCBlockInputStream.h>
|
||||
#include <Dictionaries/validateODBCConnectionString.h>
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Interpreters/evaluateConstantExpression.h>
|
||||
#include <Parsers/ASTLiteral.h>
|
||||
#include <Storages/StorageFactory.h>
|
||||
#include <Storages/StorageODBC.h>
|
||||
#include <Storages/transformQueryForExternalDatabase.h>
|
||||
#include <Poco/Ext/SessionPoolHelpers.h>
|
||||
#include <Poco/Util/AbstractConfiguration.h>
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
|
||||
#include <IO/ReadHelpers.h>
|
||||
#include <IO/ReadWriteBufferFromHTTP.h>
|
||||
#include <Poco/File.h>
|
||||
#include <Poco/Net/HTTPRequest.h>
|
||||
#include <Poco/Path.h>
|
||||
#include <Common/ShellCommand.h>
|
||||
#include <ext/range.h>
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
|
||||
extern const int EXTERNAL_EXECUTABLE_NOT_FOUND;
|
||||
extern const int EXTERNAL_SERVER_IS_NOT_RESPONDING;
|
||||
}
|
||||
|
||||
|
||||
StorageODBC::StorageODBC(
|
||||
const std::string & name,
|
||||
StorageODBC::StorageODBC(const std::string & table_name_,
|
||||
const std::string & connection_string,
|
||||
const std::string & remote_database_name,
|
||||
const std::string & remote_table_name,
|
||||
const ColumnsDescription & columns_)
|
||||
: IStorage{columns_}
|
||||
, name(name)
|
||||
, remote_database_name(remote_database_name)
|
||||
, remote_table_name(remote_table_name)
|
||||
const std::string & remote_database_name_,
|
||||
const std::string & remote_table_name_,
|
||||
const ColumnsDescription & columns_,
|
||||
const Context & context_)
|
||||
: IStorageURLBase(Poco::URI(), context_, table_name_, ODBCBridgeHelper::DEFAULT_FORMAT, columns_)
|
||||
, odbc_bridge_helper(context_, connection_string)
|
||||
, remote_database_name(remote_database_name_)
|
||||
, remote_table_name(remote_table_name_)
|
||||
, log(&Poco::Logger::get("StorageODBC"))
|
||||
{
|
||||
pool = createAndCheckResizePocoSessionPool([&]
|
||||
{
|
||||
return std::make_shared<Poco::Data::SessionPool>("ODBC", validateODBCConnectionString(connection_string));
|
||||
});
|
||||
const auto & config = context_global.getConfigRef();
|
||||
size_t bridge_port = config.getUInt("odbc_bridge.port", ODBCBridgeHelper::DEFAULT_PORT);
|
||||
std::string bridge_host = config.getString("odbc_bridge.host", ODBCBridgeHelper::DEFAULT_HOST);
|
||||
|
||||
uri.setHost(bridge_host);
|
||||
uri.setPort(bridge_port);
|
||||
uri.setScheme("http");
|
||||
}
|
||||
|
||||
BlockInputStreams StorageODBC::read(
|
||||
const Names & column_names,
|
||||
std::string StorageODBC::getReadMethod() const
|
||||
{
|
||||
return ODBCBridgeHelper::MAIN_METHOD;
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> StorageODBC::getReadURIParams(const Names & column_names,
|
||||
const SelectQueryInfo & /*query_info*/,
|
||||
const Context & /*context*/,
|
||||
QueryProcessingStage::Enum & /*processed_stage*/,
|
||||
size_t max_block_size) const
|
||||
{
|
||||
NamesAndTypesList cols;
|
||||
for (const String & name : column_names)
|
||||
{
|
||||
auto column_data = getColumn(name);
|
||||
cols.emplace_back(column_data.name, column_data.type);
|
||||
}
|
||||
return odbc_bridge_helper.getURLParams(cols, max_block_size);
|
||||
}
|
||||
|
||||
std::function<void(std::ostream &)> StorageODBC::getReadPOSTDataCallback(const Names & /*column_names*/,
|
||||
const SelectQueryInfo & query_info,
|
||||
const Context & context,
|
||||
QueryProcessingStage::Enum & /*processed_stage*/,
|
||||
size_t /*max_block_size*/) const
|
||||
{
|
||||
String query = transformQueryForExternalDatabase(
|
||||
*query_info.query, getColumns().ordinary, IdentifierQuotingStyle::DoubleQuotes, remote_database_name, remote_table_name, context);
|
||||
|
||||
return [query](std::ostream & os) { os << "query=" << query; };
|
||||
}
|
||||
|
||||
BlockInputStreams StorageODBC::read(const Names & column_names,
|
||||
const SelectQueryInfo & query_info,
|
||||
const Context & context,
|
||||
QueryProcessingStage::Enum & processed_stage,
|
||||
size_t max_block_size,
|
||||
unsigned /*num_streams*/)
|
||||
unsigned num_streams)
|
||||
{
|
||||
check(column_names);
|
||||
processed_stage = QueryProcessingStage::FetchColumns;
|
||||
String query = transformQueryForExternalDatabase(
|
||||
*query_info.query, getColumns().ordinary, IdentifierQuotingStyle::DoubleQuotes, remote_database_name, remote_table_name, context);
|
||||
|
||||
Block sample_block;
|
||||
for (const String & name : column_names)
|
||||
{
|
||||
auto column_data = getColumn(name);
|
||||
sample_block.insert({ column_data.type, column_data.name });
|
||||
}
|
||||
|
||||
return { std::make_shared<ODBCBlockInputStream>(pool->get(), query, sample_block, max_block_size) };
|
||||
odbc_bridge_helper.startODBCBridgeSync();
|
||||
return IStorageURLBase::read(column_names, query_info, context, processed_stage, max_block_size, num_streams);
|
||||
}
|
||||
|
||||
|
||||
void registerStorageODBC(StorageFactory & factory)
|
||||
{
|
||||
factory.registerStorage("ODBC", [](const StorageFactory::Arguments & args)
|
||||
{
|
||||
factory.registerStorage("ODBC", [](const StorageFactory::Arguments & args) {
|
||||
ASTs & engine_args = args.engine_args;
|
||||
|
||||
if (engine_args.size() != 3)
|
||||
throw Exception(
|
||||
"Storage ODBC requires exactly 3 parameters: ODBC('DSN', database, table).",
|
||||
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
"Storage ODBC requires exactly 3 parameters: ODBC('DSN', database, table).", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
|
||||
for (size_t i = 0; i < 2; ++i)
|
||||
engine_args[i] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[i], args.local_context);
|
||||
@ -76,8 +108,8 @@ void registerStorageODBC(StorageFactory & factory)
|
||||
static_cast<const ASTLiteral &>(*engine_args[0]).value.safeGet<String>(),
|
||||
static_cast<const ASTLiteral &>(*engine_args[1]).value.safeGet<String>(),
|
||||
static_cast<const ASTLiteral &>(*engine_args[2]).value.safeGet<String>(),
|
||||
args.columns);
|
||||
args.columns,
|
||||
args.context);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,44 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <Storages/StorageURL.h>
|
||||
#include <Common/ODBCBridgeHelper.h>
|
||||
#include <ext/shared_ptr_helper.h>
|
||||
#include <Storages/IStorage.h>
|
||||
#include <Poco/Data/SessionPool.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
/** Implements storage in the ODBC database.
|
||||
* Use ENGINE = odbc(connection_string, table_name)
|
||||
* Example ENGINE = odbc('dsn=test', table)
|
||||
* Read only.
|
||||
*/
|
||||
class StorageODBC : public ext::shared_ptr_helper<StorageODBC>, public IStorage
|
||||
class StorageODBC : public ext::shared_ptr_helper<StorageODBC>, public IStorageURLBase
|
||||
{
|
||||
public:
|
||||
StorageODBC(
|
||||
const std::string & name,
|
||||
const std::string & connection_string,
|
||||
const std::string & remote_database_name,
|
||||
const std::string & remote_table_name,
|
||||
const ColumnsDescription & columns_);
|
||||
std::string getName() const override
|
||||
{
|
||||
return "ODBC";
|
||||
}
|
||||
|
||||
std::string getName() const override { return "ODBC"; }
|
||||
std::string getTableName() const override { return name; }
|
||||
|
||||
BlockInputStreams read(
|
||||
const Names & column_names,
|
||||
BlockInputStreams read(const Names & column_names,
|
||||
const SelectQueryInfo & query_info,
|
||||
const Context & context,
|
||||
QueryProcessingStage::Enum & processed_stage,
|
||||
size_t max_block_size,
|
||||
unsigned num_streams) override;
|
||||
|
||||
|
||||
protected:
|
||||
StorageODBC(const std::string & table_name_,
|
||||
const std::string & connection_string,
|
||||
const std::string & remote_database_name,
|
||||
const std::string & remote_table_name,
|
||||
const ColumnsDescription & columns_,
|
||||
const Context & context_);
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
ODBCBridgeHelper odbc_bridge_helper;
|
||||
std::string remote_database_name;
|
||||
std::string remote_table_name;
|
||||
|
||||
std::shared_ptr<Poco::Data::SessionPool> pool;
|
||||
Poco::Logger * log;
|
||||
|
||||
std::string getReadMethod() const override;
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> getReadURIParams(const Names & column_names,
|
||||
const SelectQueryInfo & query_info,
|
||||
const Context & context,
|
||||
QueryProcessingStage::Enum & processed_stage,
|
||||
size_t max_block_size) const override;
|
||||
|
||||
std::function<void(std::ostream &)> getReadPOSTDataCallback(const Names & column_names,
|
||||
const SelectQueryInfo & query_info,
|
||||
const Context & context,
|
||||
QueryProcessingStage::Enum & processed_stage,
|
||||
size_t max_block_size) const override;
|
||||
};
|
||||
}
|
||||
|
@ -24,12 +24,12 @@ namespace ErrorCodes
|
||||
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
|
||||
}
|
||||
|
||||
StorageURL::StorageURL(const Poco::URI & uri_,
|
||||
IStorageURLBase::IStorageURLBase(const Poco::URI & uri_,
|
||||
const Context & context_,
|
||||
const std::string & table_name_,
|
||||
const String & format_name_,
|
||||
const ColumnsDescription & columns_,
|
||||
Context & context_)
|
||||
: IStorage(columns_), uri(uri_), format_name(format_name_), table_name(table_name_), context_global(context_)
|
||||
const ColumnsDescription & columns_)
|
||||
: IStorage(columns_), uri(uri_), context_global(context_), format_name(format_name_), table_name(table_name_)
|
||||
{
|
||||
}
|
||||
|
||||
@ -39,6 +39,8 @@ namespace
|
||||
{
|
||||
public:
|
||||
StorageURLBlockInputStream(const Poco::URI & uri,
|
||||
const std::string & method,
|
||||
std::function<void(std::ostream &)> callback,
|
||||
const String & format,
|
||||
const String & name_,
|
||||
const Block & sample_block,
|
||||
@ -47,7 +49,7 @@ namespace
|
||||
const ConnectionTimeouts & timeouts)
|
||||
: name(name_)
|
||||
{
|
||||
read_buf = std::make_unique<ReadWriteBufferFromHTTP>(uri, Poco::Net::HTTPRequest::HTTP_GET, nullptr, timeouts);
|
||||
read_buf = std::make_unique<ReadWriteBufferFromHTTP>(uri, method, callback, timeouts);
|
||||
|
||||
reader = FormatFactory::instance().getInput(format, *read_buf, sample_block, context, max_block_size);
|
||||
}
|
||||
@ -89,7 +91,7 @@ namespace
|
||||
StorageURLBlockOutputStream(const Poco::URI & uri,
|
||||
const String & format,
|
||||
const Block & sample_block_,
|
||||
Context & context,
|
||||
const Context & context,
|
||||
const ConnectionTimeouts & timeouts)
|
||||
: sample_block(sample_block_)
|
||||
{
|
||||
@ -127,16 +129,45 @@ namespace
|
||||
}
|
||||
|
||||
|
||||
BlockInputStreams StorageURL::read(
|
||||
const Names & /*column_names*/,
|
||||
std::string IStorageURLBase::getReadMethod() const
|
||||
{
|
||||
return Poco::Net::HTTPRequest::HTTP_GET;
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> IStorageURLBase::getReadURIParams(const Names & /*column_names*/,
|
||||
const SelectQueryInfo & /*query_info*/,
|
||||
const Context & context,
|
||||
const Context & /*context*/,
|
||||
QueryProcessingStage::Enum & /*processed_stage*/,
|
||||
size_t /*max_block_size*/) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::function<void(std::ostream &)> IStorageURLBase::getReadPOSTDataCallback(const Names & /*column_names*/,
|
||||
const SelectQueryInfo & /*query_info*/,
|
||||
const Context & /*context*/,
|
||||
QueryProcessingStage::Enum & /*processed_stage*/,
|
||||
size_t /*max_block_size*/) const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
BlockInputStreams IStorageURLBase::read(const Names & column_names,
|
||||
const SelectQueryInfo & query_info,
|
||||
const Context & context,
|
||||
QueryProcessingStage::Enum & processed_stage,
|
||||
size_t max_block_size,
|
||||
unsigned /*num_streams*/)
|
||||
{
|
||||
return {std::make_shared<StorageURLBlockInputStream>(
|
||||
uri,
|
||||
auto request_uri = uri;
|
||||
auto params = getReadURIParams(column_names, query_info, context, processed_stage, max_block_size);
|
||||
for (const auto & [param, value] : params)
|
||||
request_uri.addQueryParameter(param, value);
|
||||
|
||||
return {std::make_shared<StorageURLBlockInputStream>(request_uri,
|
||||
getReadMethod(),
|
||||
getReadPOSTDataCallback(column_names, query_info, context, processed_stage, max_block_size),
|
||||
format_name,
|
||||
getName(),
|
||||
getSampleBlock(),
|
||||
@ -145,9 +176,9 @@ BlockInputStreams StorageURL::read(
|
||||
ConnectionTimeouts::getHTTPTimeouts(context.getSettingsRef()))};
|
||||
}
|
||||
|
||||
void StorageURL::rename(const String & /*new_path_to_db*/, const String & /*new_database_name*/, const String & /*new_table_name*/) {}
|
||||
void IStorageURLBase::rename(const String & /*new_path_to_db*/, const String & /*new_database_name*/, const String & /*new_table_name*/) {}
|
||||
|
||||
BlockOutputStreamPtr StorageURL::write(const ASTPtr & /*query*/, const Settings & /*settings*/)
|
||||
BlockOutputStreamPtr IStorageURLBase::write(const ASTPtr & /*query*/, const Settings & /*settings*/)
|
||||
{
|
||||
return std::make_shared<StorageURLBlockOutputStream>(
|
||||
uri, format_name, getSampleBlock(), context_global, ConnectionTimeouts::getHTTPTimeouts(context_global.getSettingsRef()));
|
||||
@ -155,8 +186,7 @@ BlockOutputStreamPtr StorageURL::write(const ASTPtr & /*query*/, const Settings
|
||||
|
||||
void registerStorageURL(StorageFactory & factory)
|
||||
{
|
||||
factory.registerStorage("URL", [](const StorageFactory::Arguments & args)
|
||||
{
|
||||
factory.registerStorage("URL", [](const StorageFactory::Arguments & args) {
|
||||
ASTs & engine_args = args.engine_args;
|
||||
|
||||
if (!(engine_args.size() == 1 || engine_args.size() == 2))
|
||||
@ -175,5 +205,4 @@ void registerStorageURL(StorageFactory & factory)
|
||||
return StorageURL::create(uri, args.table_name, format_name, args.columns, args.context);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,14 +13,9 @@ namespace DB
|
||||
* HTTP POST when insert is called. In POST request the data is send
|
||||
* using Chunked transfer encoding, so server have to support it.
|
||||
*/
|
||||
class StorageURL : public ext::shared_ptr_helper<StorageURL>, public IStorage
|
||||
class IStorageURLBase : public IStorage
|
||||
{
|
||||
public:
|
||||
String getName() const override
|
||||
{
|
||||
return "URL";
|
||||
}
|
||||
|
||||
String getTableName() const override
|
||||
{
|
||||
return table_name;
|
||||
@ -38,18 +33,49 @@ public:
|
||||
void rename(const String & new_path_to_db, const String & new_database_name, const String & new_table_name) override;
|
||||
|
||||
protected:
|
||||
IStorageURLBase(const Poco::URI & uri_,
|
||||
const Context & context_,
|
||||
const std::string & table_name_,
|
||||
const String & format_name_,
|
||||
const ColumnsDescription & columns_);
|
||||
|
||||
Poco::URI uri;
|
||||
const Context & context_global;
|
||||
|
||||
private:
|
||||
String format_name;
|
||||
String table_name;
|
||||
|
||||
virtual std::string getReadMethod() const;
|
||||
|
||||
virtual std::vector<std::pair<std::string, std::string>> getReadURIParams(const Names & column_names,
|
||||
const SelectQueryInfo & query_info,
|
||||
const Context & context,
|
||||
QueryProcessingStage::Enum & processed_stage,
|
||||
size_t max_block_size) const;
|
||||
|
||||
virtual std::function<void(std::ostream &)> getReadPOSTDataCallback(const Names & column_names,
|
||||
const SelectQueryInfo & query_info,
|
||||
const Context & context,
|
||||
QueryProcessingStage::Enum & processed_stage,
|
||||
size_t max_block_size) const;
|
||||
};
|
||||
|
||||
class StorageURL : public ext::shared_ptr_helper<StorageURL>, public IStorageURLBase
|
||||
{
|
||||
public:
|
||||
StorageURL(const Poco::URI & uri_,
|
||||
const std::string & table_name_,
|
||||
const String & format_name_,
|
||||
const ColumnsDescription & columns_,
|
||||
Context & context_);
|
||||
Context & context_)
|
||||
: IStorageURLBase(uri_, context_, table_name_, format_name_, columns_)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
Poco::URI uri;
|
||||
String format_name;
|
||||
String table_name;
|
||||
Context & context_global;
|
||||
|
||||
Logger * log = &Logger::get("StorageURL");
|
||||
String getName() const override
|
||||
{
|
||||
return "URL";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include <Parsers/ASTFunction.h>
|
||||
#include <Parsers/ASTLiteral.h>
|
||||
#include <Storages/StorageODBC.h>
|
||||
#include <Dictionaries/validateODBCConnectionString.h>
|
||||
#include <TableFunctions/ITableFunction.h>
|
||||
#include <TableFunctions/TableFunctionFactory.h>
|
||||
#include <Common/Exception.h>
|
||||
@ -72,7 +71,7 @@ StoragePtr TableFunctionODBC::executeImpl(const ASTPtr & ast_function, const Con
|
||||
for (int i = 0; i < 2; ++i)
|
||||
args[i] = evaluateConstantExpressionOrIdentifierAsLiteral(args[i], context);
|
||||
|
||||
std::string connection_string = validateODBCConnectionString(static_cast<const ASTLiteral &>(*args[0]).value.safeGet<String>());
|
||||
std::string connection_string = static_cast<const ASTLiteral &>(*args[0]).value.safeGet<String>();
|
||||
std::string table_name = static_cast<const ASTLiteral &>(*args[1]).value.safeGet<String>();
|
||||
|
||||
Poco::Data::ODBC::SessionImpl session(connection_string, DBMS_DEFAULT_CONNECT_TIMEOUT_SEC);
|
||||
@ -110,7 +109,7 @@ StoragePtr TableFunctionODBC::executeImpl(const ASTPtr & ast_function, const Con
|
||||
columns.emplace_back(reinterpret_cast<char *>(column_name), getDataType(type));
|
||||
}
|
||||
|
||||
auto result = StorageODBC::create(table_name, connection_string, "", table_name, ColumnsDescription{columns});
|
||||
auto result = StorageODBC::create(table_name, connection_string, "", table_name, ColumnsDescription{columns}, context);
|
||||
result->startup();
|
||||
return result;
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
84 0 1764 1
|
||||
84 0 1764 1
|
||||
84 0 1764 1
|
||||
84.840 0.000 1799.456400 1.000
|
||||
84.84 0.00 1799.4564 1.00
|
||||
63 21 -42 882 -882 2 0
|
||||
63 21 -42 882 -882 2 0
|
||||
63 21 -42 882 -882 2 0
|
||||
63.420 21.420 -41.580 890.820 -890.820 2.020 0.505
|
||||
63.420000000 21.420000000 -41.580000000 890.820000000 -890.820000000 2.020000000 0.505000000
|
||||
63.420000000000000000 21.420000000000000000 -41.580000000000000000 890.820000000000000000 -890.820000000000000000 2.020000000000000000 0.505000000000000000
|
||||
63.42 21.42 -41.58 890.82 -890.82 2.02 0.50
|
||||
63 -21 42 882 -882 0 2
|
||||
63 -21 42 882 -882 0 2
|
||||
63 -21 42 882 -882 0 2
|
||||
63.420 -21.420 41.580 890.820 -890.820 0.495 1.980
|
||||
63.420000000 -21.420000000 41.580000000 890.820000000 -890.820000000
|
||||
63.420000000000000000 -21.420000000000000000 41.580000000000000000 890.820000000000000000 -890.820000000000000000 0.495049504950495049 1.980198019801980198
|
||||
63.42 -21.42 41.58 890.82 -890.82 0.49 1.98
|
||||
-42 42 42 42 0.420000000 0.420000000000000000 0.42000000000000000000000000000000000000 42.420 42.420000000 42.42
|
||||
0 0 0 0 0.000000000 0.000000000000000000 0.00000000000000000000000000000000000000 0.000 0.000000000 0.00
|
||||
42 -42 -42 -42 -0.420000000 -0.420000000000000000 -0.42000000000000000000000000000000000000 -42.420 -42.420000000 -42.42
|
||||
42 42 42 0.420000000 0.420000000000000000 0.42000000000000000000000000000000000000 42.420 42.420000000 42.42
|
||||
0 0 0 0.000000000 0.000000000000000000 0.00000000000000000000000000000000000000 0.000 0.000000000 0.00
|
||||
42 42 42 0.420000000 0.420000000000000000 0.42000000000000000000000000000000000000 42.420 42.420000000 42.42
|
56
dbms/tests/queries/0_stateless/00700_decimal_arithm.sql
Normal file
56
dbms/tests/queries/0_stateless/00700_decimal_arithm.sql
Normal file
@ -0,0 +1,56 @@
|
||||
SET allow_experimental_decimal_type=1;
|
||||
CREATE DATABASE IF NOT EXISTS test;
|
||||
DROP TABLE IF EXISTS test.decimal;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS test.decimal
|
||||
(
|
||||
a DECIMAL(9,0),
|
||||
b DECIMAL(18,0),
|
||||
c DECIMAL(38,0),
|
||||
d DECIMAL(9, 9),
|
||||
e DEC(18, 18),
|
||||
f dec(38, 38),
|
||||
g Decimal(9, 3),
|
||||
h decimal(18, 9),
|
||||
i deciMAL(38, 18),
|
||||
j dec(4,2)
|
||||
) ENGINE = Memory;
|
||||
|
||||
INSERT INTO test.decimal (a, b, c, d, e, f, g, h, i, j) VALUES (0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
INSERT INTO test.decimal (a, b, c, d, e, f, g, h, i, j) VALUES (42, 42, 42, 0.42, 0.42, 0.42, 42.42, 42.42, 42.42, 42.42);
|
||||
INSERT INTO test.decimal (a, b, c, d, e, f, g, h, i, j) VALUES (-42, -42, -42, -0.42, -0.42, -0.42, -42.42, -42.42, -42.42, -42.42);
|
||||
|
||||
SELECT a + a, a - a, a * a, a / a FROM test.decimal WHERE a = 42;
|
||||
SELECT b + b, b - b, b * b, b / b FROM test.decimal WHERE b = 42;
|
||||
SELECT c + c, c - c, c * c, c / c FROM test.decimal WHERE c = 42;
|
||||
SELECT e + e, e - e, e * e, e / e FROM test.decimal WHERE e > 0; -- { serverError 69 }
|
||||
SELECT f + f, f - f, f * f, f / f FROM test.decimal WHERE f > 0; -- { serverError 69 }
|
||||
SELECT g + g, g - g, g * g, g / g FROM test.decimal WHERE g > 0;
|
||||
SELECT h + h, h - h, h * h, h / h FROM test.decimal WHERE h > 0; -- { serverError 407 }
|
||||
SELECT i + i, i - i, i * i, i / i FROM test.decimal WHERE i > 0; -- { serverError 407 }
|
||||
SELECT j + j, j - j, j * j, j / j FROM test.decimal WHERE j > 0;
|
||||
|
||||
SELECT a + 21, a - 21, a - 84, a * 21, a * -21, a / 21, a / 84 FROM test.decimal WHERE a = 42;
|
||||
SELECT b + 21, b - 21, b - 84, b * 21, b * -21, b / 21, b / 84 FROM test.decimal WHERE b = 42;
|
||||
SELECT c + 21, c - 21, c - 84, c * 21, c * -21, c / 21, c / 84 FROM test.decimal WHERE c = 42;
|
||||
SELECT e + 21, e - 21, e - 84, e * 21, e * -21, e / 21, e / 84 FROM test.decimal WHERE e > 0; -- { serverError 407 }
|
||||
SELECT f + 21, f - 21, f - 84, f * 21, f * -21, f / 21, f / 84 FROM test.decimal WHERE f > 0; -- { serverError 407 }
|
||||
SELECT g + 21, g - 21, g - 84, g * 21, g * -21, g / 21, g / 84 FROM test.decimal WHERE g > 0;
|
||||
SELECT h + 21, h - 21, h - 84, h * 21, h * -21, h / 21, h / 84 FROM test.decimal WHERE h > 0;
|
||||
SELECT i + 21, i - 21, i - 84, i * 21, i * -21, i / 21, i / 84 FROM test.decimal WHERE i > 0;
|
||||
SELECT j + 21, j - 21, j - 84, j * 21, j * -21, j / 21, j / 84 FROM test.decimal WHERE j > 0;
|
||||
|
||||
SELECT 21 + a, 21 - a, 84 - a, 21 * a, -21 * a, 21 / a, 84 / a FROM test.decimal WHERE a = 42;
|
||||
SELECT 21 + b, 21 - b, 84 - b, 21 * b, -21 * b, 21 / b, 84 / b FROM test.decimal WHERE b = 42;
|
||||
SELECT 21 + c, 21 - c, 84 - c, 21 * c, -21 * c, 21 / c, 84 / c FROM test.decimal WHERE c = 42;
|
||||
SELECT 21 + e, 21 - e, 84 - e, 21 * e, -21 * e, 21 / e, 84 / e FROM test.decimal WHERE e > 0; -- { serverError 407 }
|
||||
SELECT 21 + f, 21 - f, 84 - f, 21 * f, -21 * f, 21 / f, 84 / f FROM test.decimal WHERE f > 0; -- { serverError 407 }
|
||||
SELECT 21 + g, 21 - g, 84 - g, 21 * g, -21 * g, 21 / g, 84 / g FROM test.decimal WHERE g > 0;
|
||||
SELECT 21 + h, 21 - h, 84 - h, 21 * h, -21 * h FROM test.decimal WHERE h > 0; --overflow 21 / h, 84 / h
|
||||
SELECT 21 + i, 21 - i, 84 - i, 21 * i, -21 * i, 21 / i, 84 / i FROM test.decimal WHERE i > 0;
|
||||
SELECT 21 + j, 21 - j, 84 - j, 21 * j, -21 * j, 21 / j, 84 / j FROM test.decimal WHERE j > 0;
|
||||
|
||||
SELECT a, -a, -b, -c, -d, -e, -f, -g, -h, -j from test.decimal ORDER BY a;
|
||||
SELECT abs(a), abs(b), abs(c), abs(d), abs(e), abs(f), abs(g), abs(h), abs(j) from test.decimal ORDER BY a;
|
||||
|
||||
DROP TABLE IF EXISTS test.decimal;
|
@ -0,0 +1,21 @@
|
||||
-999999999 -999999999999999999 0 -0.999999999 0.000000000000000000 0.00000000000000000000000000000000000000 -9999.99999 0.000000000 0.000000000000000000 0
|
||||
-1 -1 -1 -0.000000001 0.000000000000000000 0.00000000000000000000000000000000000000 -0.00001 -0.000000001 0.000000000000000000 -1
|
||||
0 0 -99999999999999999999999999999999999999 0.000000000 0.000000000000000000 0.00000000000000000000000000000000000000 0.00000 0.000000000 0.000000000000000000 0
|
||||
0 0 0 0.000000000 -0.999999999999999999 0.00000000000000000000000000000000000000 0.00000 -999999999.999999999 0.000000000000000000 0
|
||||
0 0 0 0.000000000 -0.000000000000000001 -0.00000000000000000000000000000000000001 0.00000 0.000000000 0.000000000000000000 0
|
||||
0 0 0 0.000000000 0.000000000000000000 -0.99999999999999999999999999999999999999 0.00000 0.000000000 0.000000000000000000 0
|
||||
0 0 0 0.000000000 0.000000000000000000 0.00000000000000000000000000000000000000 0.00000 0.000000000 -99999999999999999999.999999999999999999 0
|
||||
0 0 0 0.000000000 0.000000000000000000 0.00000000000000000000000000000000000000 0.00000 0.000000000 -0.000000000000000001 0
|
||||
0 0 0 0.000000000 0.000000000000000000 0.00000000000000000000000000000000000000 0.00000 0.000000000 0.000000000000000000 0
|
||||
0 0 0 0.000000000 0.000000000000000000 0.00000000000000000000000000000000000000 0.00000 0.000000000 0.000000000000000000 0
|
||||
0 0 0 0.000000000 0.000000000000000000 0.00000000000000000000000000000000000000 0.00000 0.000000000 0.000000000000000000 0
|
||||
0 0 0 0.000000000 0.000000000000000000 0.00000000000000000000000000000000000000 0.00000 0.000000000 0.000000000000000000 0
|
||||
0 0 0 0.000000000 0.000000000000000000 0.00000000000000000000000000000000000000 0.00000 0.000000000 0.000000000000000001 0
|
||||
0 0 0 0.000000000 0.000000000000000000 0.00000000000000000000000000000000000000 0.00000 0.000000000 99999999999999999999.999999999999999999 0
|
||||
0 0 0 0.000000000 0.000000000000000000 0.99999999999999999999999999999999999999 0.00000 0.000000000 0.000000000000000000 0
|
||||
0 0 0 0.000000000 0.000000000000000001 0.00000000000000000000000000000000000001 0.00000 0.000000000 0.000000000000000000 0
|
||||
0 0 0 0.000000000 0.999999999999999999 0.00000000000000000000000000000000000000 0.00000 999999999.999999999 0.000000000000000000 0
|
||||
0 0 99999999999999999999999999999999999999 0.000000000 0.000000000000000000 0.00000000000000000000000000000000000000 0.00000 0.000000000 0.000000000000000000 0
|
||||
1 1 1 0.000000001 0.000000000000000000 0.00000000000000000000000000000000000000 0.00001 0.000000001 0.000000000000000000 1
|
||||
42 42 0 0.000000000 0.000000000000000000 0.00000000000000000000000000000000000000 0.99999 0.000000000 0.000000000000000000 0
|
||||
999999999 999999999999999999 0 0.999999999 0.000000000000000000 0.00000000000000000000000000000000000000 9999.99999 0.000000000 0.000000000000000000 0
|
94
dbms/tests/queries/0_stateless/00700_decimal_bounds.sql
Normal file
94
dbms/tests/queries/0_stateless/00700_decimal_bounds.sql
Normal file
@ -0,0 +1,94 @@
|
||||
SET allow_experimental_decimal_type=1;
|
||||
CREATE DATABASE IF NOT EXISTS test;
|
||||
DROP TABLE IF EXISTS test.decimal;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS test.decimal (x DECIMAL(10, -2)) ENGINE = Memory; -- { serverError 69 }
|
||||
CREATE TABLE IF NOT EXISTS test.decimal (x DECIMAL(10, 15)) ENGINE = Memory; -- { serverError 69 }
|
||||
CREATE TABLE IF NOT EXISTS test.decimal (x DECIMAL(0, 0)) ENGINE = Memory; -- { serverError 69 }
|
||||
|
||||
CREATE TABLE IF NOT EXISTS test.decimal
|
||||
(
|
||||
a DECIMAL(9,0),
|
||||
b DECIMAL(18,0),
|
||||
c DECIMAL(38,0),
|
||||
d DECIMAL(9, 9),
|
||||
e DECIMAL(18, 18),
|
||||
f DECIMAL(38, 38),
|
||||
g Decimal(9, 5),
|
||||
h decimal(18, 9),
|
||||
i deciMAL(38, 18),
|
||||
j DECIMAL(1,0)
|
||||
) ENGINE = Memory;
|
||||
|
||||
INSERT INTO test.decimal (a) VALUES (1000000000); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (a) VALUES (-1000000000); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (b) VALUES (1000000000000000000); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (b) VALUES (-1000000000000000000); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (c) VALUES (100000000000000000000000000000000000000); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (c) VALUES (-100000000000000000000000000000000000000); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (d) VALUES (1); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (d) VALUES (-1); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (e) VALUES (1000000000000000000); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (e) VALUES (-1000000000000000000); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (f) VALUES (1); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (f) VALUES (-1); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (g) VALUES (10000); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (g) VALUES (-10000); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (h) VALUES (1000000000); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (h) VALUES (-1000000000); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (i) VALUES (100000000000000000000); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (i) VALUES (-100000000000000000000); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (j) VALUES (10); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (j) VALUES (-10); -- { clientError 69 }
|
||||
|
||||
INSERT INTO test.decimal (a) VALUES (0.1); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (a) VALUES (-0.1); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (b) VALUES (0.1); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (b) VALUES (-0.1); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (c) VALUES (0.1); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (c) VALUES (-0.1); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (d) VALUES (0.0000000001); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (d) VALUES (-0.0000000001); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (e) VALUES (0.0000000000000000001); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (e) VALUES (-0.0000000000000000001); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (f) VALUES (0.000000000000000000000000000000000000001); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (f) VALUES (-0.000000000000000000000000000000000000001); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (g) VALUES (0.000001); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (g) VALUES (-0.000001); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (h) VALUES (0.0000000001); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (h) VALUES (-0.0000000001); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (i) VALUES (0.0000000000000000001); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (i) VALUES (-0.0000000000000000001); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (j) VALUES (0.1); -- { clientError 69 }
|
||||
INSERT INTO test.decimal (j) VALUES (-0.1); -- { clientError 69 }
|
||||
|
||||
INSERT INTO test.decimal (a, b, d, g) VALUES (999999999, 999999999999999999, 0.999999999, 9999.99999);
|
||||
INSERT INTO test.decimal (a, b, d, g) VALUES (-999999999, -999999999999999999, -0.999999999, -9999.99999);
|
||||
INSERT INTO test.decimal (c) VALUES (99999999999999999999999999999999999999);
|
||||
INSERT INTO test.decimal (c) VALUES (-99999999999999999999999999999999999999);
|
||||
INSERT INTO test.decimal (f) VALUES (0.99999999999999999999999999999999999999);
|
||||
INSERT INTO test.decimal (f) VALUES (-0.99999999999999999999999999999999999999);
|
||||
INSERT INTO test.decimal (e, h) VALUES (0.999999999999999999, 999999999.999999999);
|
||||
INSERT INTO test.decimal (e, h) VALUES (-0.999999999999999999, -999999999.999999999);
|
||||
INSERT INTO test.decimal (i) VALUES (99999999999999999999.999999999999999999);
|
||||
INSERT INTO test.decimal (i) VALUES (-99999999999999999999.999999999999999999);
|
||||
|
||||
INSERT INTO test.decimal (a, b, c, d, g, j, h) VALUES (1, 1, 1, 0.000000001, 0.00001, 1, 0.000000001);
|
||||
INSERT INTO test.decimal (a, b, c, d, g, j, h) VALUES (-1, -1, -1, -0.000000001, -0.00001, -1, -0.000000001);
|
||||
INSERT INTO test.decimal (e, f) VALUES (0.000000000000000001, 0.00000000000000000000000000000000000001);
|
||||
INSERT INTO test.decimal (e, f) VALUES (-0.000000000000000001, -0.00000000000000000000000000000000000001);
|
||||
INSERT INTO test.decimal (i) VALUES (0.000000000000000001);
|
||||
INSERT INTO test.decimal (i) VALUES (-0.000000000000000001);
|
||||
|
||||
INSERT INTO test.decimal (a, b, c, d, e, f, g, h, i, j) VALUES (0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
INSERT INTO test.decimal (a, b, c, d, e, f, g, h, i, j) VALUES (-0, -0, -0, -0, -0, -0, -0, -0, -0, -0);
|
||||
INSERT INTO test.decimal (a, b, c, d, e, f, g, h, i, j) VALUES (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
|
||||
INSERT INTO test.decimal (a, b, c, d, e, f, g, h, i, j) VALUES (-0.0, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0);
|
||||
|
||||
INSERT INTO test.decimal (a, b, g) VALUES ('42.00000', 42.0000000000000000000000000000000, '0.999990');
|
||||
INSERT INTO test.decimal (a) VALUES ('-9x'); -- { clientError 72 }
|
||||
INSERT INTO test.decimal (a) VALUES ('0x1'); -- { clientError 72 }
|
||||
INSERT INTO test.decimal (a) VALUES ('1e2'); -- { clientError 72 }
|
||||
|
||||
SELECT * FROM test.decimal ORDER BY a, b, c, d, e, f, g, h, i, j;
|
||||
DROP TABLE IF EXISTS test.decimal;
|
@ -0,0 +1,37 @@
|
||||
-42 -42 1 0 0 0 1 1
|
||||
42 42 1 0 0 0 1 1
|
||||
-42 -42.42000 0 0 1 1 0 1
|
||||
42 42.42000 0 1 0 1 1 0
|
||||
1 1 1
|
||||
0 0 0
|
||||
-42 0 0 0 0
|
||||
42 1 1 1 1
|
||||
-42 0 0 0 0
|
||||
42 1 1 1 1
|
||||
0.420000000 0.420000000000000000 0.42000000000000000000000000000000000000
|
||||
42.42 42.420000000 42.420000000000000000 42.42
|
||||
-42.42 -42.420000000 -42.420000000000000000 -42.42
|
||||
42 42 42
|
||||
42 42 42
|
||||
42 42 42
|
||||
42 42 42
|
||||
-42 -42.42000 -42 -42.00000
|
||||
42 42.00000 42 42.42000
|
||||
-42 -42 -42.42000
|
||||
0 0 0.00000
|
||||
0 0 0.00000
|
||||
42 42 42.42000
|
||||
1 0
|
||||
1 0
|
||||
1 0
|
||||
1 0
|
||||
2147483648 0 1
|
||||
9223372036854775808 0 1
|
||||
0 1
|
||||
0 1
|
||||
0 1
|
||||
0 1
|
||||
0 1
|
||||
0 1
|
||||
-42 -42 -42 -0.420000000 -0.420000000000000000 -0.42000000000000000000000000000000000000 -42.42000 -42.420000000 -42.420000000000000000 -42.42
|
||||
42 42 42 0.420000000 0.420000000000000000 0.42000000000000000000000000000000000000 42.42000 42.420000000 42.420000000000000000 42.42
|
69
dbms/tests/queries/0_stateless/00700_decimal_compare.sql
Normal file
69
dbms/tests/queries/0_stateless/00700_decimal_compare.sql
Normal file
@ -0,0 +1,69 @@
|
||||
SET allow_experimental_decimal_type=1;
|
||||
CREATE DATABASE IF NOT EXISTS test;
|
||||
DROP TABLE IF EXISTS test.decimal;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS test.decimal
|
||||
(
|
||||
a DECIMAL(9,0),
|
||||
b DECIMAL(18,0),
|
||||
c DECIMAL(38,0),
|
||||
d DECIMAL(9, 9),
|
||||
e DEC(18, 18),
|
||||
f dec(38, 38),
|
||||
g Decimal(9, 5),
|
||||
h decimal(18, 9),
|
||||
i deciMAL(38, 18),
|
||||
j dec(4,2)
|
||||
) ENGINE = Memory;
|
||||
|
||||
INSERT INTO test.decimal (a, b, c, d, e, f, g, h, i, j) VALUES (42, 42, 42, 0.42, 0.42, 0.42, 42.42, 42.42, 42.42, 42.42);
|
||||
INSERT INTO test.decimal (a, b, c, d, e, f, g, h, i, j) VALUES (-42, -42, -42, -0.42, -0.42, -0.42, -42.42, -42.42, -42.42, -42.42);
|
||||
|
||||
SELECT a > toFloat64(0) FROM test.decimal; -- { serverError 43 }
|
||||
SELECT g > toFloat32(0) FROM test.decimal; -- { serverError 43 }
|
||||
SELECT a > '0.0' FROM test.decimal; -- { serverError 43 }
|
||||
|
||||
SELECT a, b, a = b, a < b, a > b, a != b, a <= b, a >= b FROM test.decimal ORDER BY a;
|
||||
SELECT a, g, a = g, a < g, a > g, a != g, a <= g, a >= g FROM test.decimal ORDER BY a;
|
||||
SELECT a > 0, b > 0, g > 0 FROM test.decimal ORDER BY a DESC;
|
||||
SELECT a, g > toInt8(0), g > toInt16(0), g > toInt32(0), g > toInt64(0) FROM test.decimal ORDER BY a;
|
||||
SELECT a, g > toUInt8(0), g > toUInt16(0), g > toUInt32(0), g > toUInt64(0) FROM test.decimal ORDER BY a;
|
||||
SELECT a, b, g FROM test.decimal WHERE a IN(42) AND b IN(42) AND g IN(42);
|
||||
SELECT a, b, g FROM test.decimal WHERE a > 0 AND a <= 42 AND b <= 42 AND g <= 42;
|
||||
|
||||
SELECT d, e, f from test.decimal WHERE d > 0 AND d < 1 AND e > 0 AND e < 1 AND f > 0 AND f < 1;
|
||||
SELECT j, h, i, j from test.decimal WHERE j > 42 AND h > 42 AND h > 42 AND j > 42;
|
||||
SELECT j, h, i, j from test.decimal WHERE j < 42 AND h < 42 AND h < 42 AND j < 42;
|
||||
SELECT a, b, c FROM test.decimal WHERE a = toInt8(42) AND b = toInt8(42) AND c = toInt8(42);
|
||||
SELECT a, b, c FROM test.decimal WHERE a = toInt16(42) AND b = toInt16(42) AND c = toInt16(42);
|
||||
SELECT a, b, c FROM test.decimal WHERE a = toInt32(42) AND b = toInt32(42) AND c = toInt32(42);
|
||||
SELECT a, b, c FROM test.decimal WHERE a = toInt64(42) AND b = toInt64(42) AND c = toInt64(42);
|
||||
SELECT a, b, c FROM test.decimal WHERE a = toFloat32(42); -- { serverError 43 }
|
||||
SELECT a, b, c FROM test.decimal WHERE a = toFloat64(42); -- { serverError 43 }
|
||||
|
||||
SELECT least(a, b), least(a, g), greatest(a, b), greatest(a, g) FROM test.decimal ORDER BY a;
|
||||
SELECT least(a, 0), least(b, 0), least(g, 0) FROM test.decimal ORDER BY a;
|
||||
SELECT greatest(a, 0), greatest(b, 0), greatest(g, 0) FROM test.decimal ORDER BY a;
|
||||
|
||||
SELECT (a, d, g) = (b, e, h), (a, d, g) != (b, e, h) FROM test.decimal ORDER BY a;
|
||||
SELECT (a, d, g) = (c, f, i), (a, d, g) != (c, f, i) FROM test.decimal ORDER BY a;
|
||||
|
||||
SELECT toUInt32(2147483648) AS x, a == x FROM test.decimal WHERE a = 42; -- { serverError 407 }
|
||||
SELECT toUInt64(2147483648) AS x, b == x, x == ((b - 42) + x) FROM test.decimal WHERE a = 42;
|
||||
SELECT toUInt64(9223372036854775808) AS x, b == x FROM test.decimal WHERE a = 42; -- { serverError 407 }
|
||||
SELECT toUInt64(9223372036854775808) AS x, c == x, x == ((c - 42) + x) FROM test.decimal WHERE a = 42;
|
||||
|
||||
SELECT g = 10000, (g - g + 10000) == 10000 FROM test.decimal WHERE a = 42;
|
||||
SELECT 10000 = g, 10000 = (g - g + 10000) FROM test.decimal WHERE a = 42;
|
||||
SELECT g = 30000 FROM test.decimal WHERE a = 42; -- { serverError 407 }
|
||||
SELECT 30000 = g FROM test.decimal WHERE a = 42; -- { serverError 407 }
|
||||
SELECT h = 30000, (h - g + 30000) = 30000 FROM test.decimal WHERE a = 42;
|
||||
SELECT 30000 = h, 30000 = (h - g + 30000) FROM test.decimal WHERE a = 42;
|
||||
SELECT h = 10000000000 FROM test.decimal WHERE a = 42; -- { serverError 407 }
|
||||
SELECT i = 10000000000, (i - g + 10000000000) = 10000000000 FROM test.decimal WHERE a = 42;
|
||||
SELECT 10000000000 = i, 10000000000 = (i - g + 10000000000) FROM test.decimal WHERE a = 42;
|
||||
|
||||
SELECT min(a), min(b), min(c), min(d), min(e), min(f), min(g), min(h), min(i), min(j) FROM test.decimal;
|
||||
SELECT max(a), max(b), max(c), max(d), max(e), max(f), max(g), max(h), max(i), max(j) FROM test.decimal;
|
||||
|
||||
DROP TABLE IF EXISTS test.decimal;
|
@ -4,7 +4,7 @@
|
||||
|
||||
Массив из элементов типа `T`.
|
||||
|
||||
`T` может любым, в том числе, массивом. Используйте многомерные массивы с осторожностью. ClickHouse поддерживает многомерные массивы ограниченно, например, их нельзя хранить в таблицах семейства `MergeTree`.
|
||||
`T` может любым, в том числе, массивом. Таким образом поддержаны многомерные массивы.
|
||||
|
||||
## Создание массива
|
||||
|
||||
|
@ -57,6 +57,6 @@ ClickHouse предоставляет различные способы разм
|
||||
|
||||
## Репликация данных и поддержка целостности
|
||||
|
||||
Используется асинхронная multimaster репликация. После записи на любую доступную реплику, данные распространяются на все остальные реплики в фоне. Система поддерживает полную идентичность данных на разных репликах. Восстановление после большинства сбоев осуществляется автоматически, а в сложных случаях — полуавтоматически.
|
||||
Используется асинхронная multimaster репликация. После записи на любую доступную реплику, данные распространяются на все остальные реплики в фоне. Система поддерживает полную идентичность данных на разных репликах. Восстановление после большинства сбоев осуществляется автоматически, а в сложных случаях — полуавтоматически. При необходимости, можно [включить кворумную запись](../operations/settings/settings.md#setting-insert_quorum) данных.
|
||||
|
||||
Подробнее смотрите раздел [Репликация данных](../operations/table_engines/replication.md#table_engines-replication).
|
||||
|
@ -345,7 +345,7 @@ ClickHouse проверит условия `min_part_size` и `min_part_size_rat
|
||||
- hostname - опционально, имя хоста, с которого отсылаются логи
|
||||
- facility - [категория syslog](https://en.wikipedia.org/wiki/Syslog#Facility),
|
||||
записанная в верхнем регистре, с префиксом "LOG_": (``LOG_USER``, ``LOG_DAEMON``, ``LOG_LOCAL3`` и прочие).
|
||||
Значения по-умолчанию: при указанном ``address`` - ``LOG_USER``, иначе - ``LOG_DAEMON``
|
||||
Значения по умолчанию: при указанном ``address`` - ``LOG_USER``, иначе - ``LOG_DAEMON``
|
||||
- format - формат сообщений. Возможные значения - ``bsd`` и ``syslog``
|
||||
|
||||
|
||||
|
@ -58,7 +58,7 @@ ClickHouse применяет настройку в тех случаях, ко
|
||||
|
||||
## fsync_metadata
|
||||
|
||||
Включить или отключить fsync при записи .sql файлов. По-умолчанию включено.
|
||||
Включить или отключить fsync при записи .sql файлов. По умолчанию включено.
|
||||
|
||||
Имеет смысл выключать, если на сервере миллионы мелких таблиц-чанков, которые постоянно создаются и уничтожаются.
|
||||
|
||||
@ -99,7 +99,7 @@ ClickHouse применяет настройку в тех случаях, ко
|
||||
|
||||
Служит для тех же целей что и `max_block_size`, но задает реккомедуемый размер блоков в байтах, выбирая адаптивное количество строк в блоке.
|
||||
При этом размер блока не может быть более `max_block_size` строк.
|
||||
По-умолчанию выключен (равен 0), работает только при чтении из MergeTree-движков.
|
||||
По умолчанию выключен (равен 0), работает только при чтении из MergeTree-движков.
|
||||
|
||||
<a name="settings_settings-log_queries"></a>
|
||||
|
||||
@ -348,10 +348,68 @@ ClickHouse применяет настройку в тех случаях, ко
|
||||
|
||||
Символ, интерпретируемый как разделитель в данных формата CSV. По умолчанию — `,`.
|
||||
|
||||
<!--a name="settings-join_use_nulls"></a-->
|
||||
<a name="settings-join_use_nulls"></a>
|
||||
|
||||
## join_use_nulls {: #settings-join_use_nulls}
|
||||
## join_use_nulls
|
||||
|
||||
Влияет на поведение [JOIN](../../query_language/select.md#query_language-join).
|
||||
|
||||
При `join_use_nulls=1` `JOIN` ведёт себя как в стандартном SQL, т.е. если при слиянии возникают пустые ячейки, то тип соответствующего поля преобразуется к [Nullable](../../data_types/nullable.md#data_type-nullable), а пустые ячейки заполняются значениями [NULL](../../query_language/syntax.md#null-literal).
|
||||
|
||||
<a name="setting-insert_quorum"></a>
|
||||
|
||||
## insert_quorum
|
||||
|
||||
Включает кворумную запись.
|
||||
|
||||
- Если `insert_quorum < 2`, то кворумная запись выключена.
|
||||
- Если `insert_quorum >= 2`, то кворумная запись включена.
|
||||
|
||||
Значение по умолчанию — 0.
|
||||
|
||||
**Кворумная запись**
|
||||
|
||||
`INSERT` завершается успешно только в том случае, когда ClickHouse смог без ошибки записать данные в `insert_quorum` реплик за время `insert_quorum_timeout`. Если по любой причине количество реплик с успешной записью не достигнет `insert_quorum`, то запись считается не состоявшейся и ClickHouse удалит вставленный блок из всех реплик, куда уже успел записать данные.
|
||||
|
||||
Все реплики в кворуме консистентны, т.е. содержат данные всех более ранних запросов `INSERT`. Последовательность `INSERT` линеаризуется.
|
||||
|
||||
При чтении данных, записанных с `insert_quorum` можно использовать настройку [select_sequential_consistency](#setting-select_sequential_consistency).
|
||||
|
||||
**ClickHouse генерирует исключение**
|
||||
|
||||
- Если количество доступных реплик на момент запроса меньше `insert_quorum`.
|
||||
- При попытке записать данные в момент, когда предыдущий блок ещё не вставлен в `insert_quorum` реплик. Эта ситуация может возникнуть, если пользователь вызвал `INSERT` прежде, чем завершился предыдущий с `insert_quorum`.
|
||||
|
||||
**См. также параметры**
|
||||
|
||||
- [insert_quorum_timeout](#setting-insert_quorum_timeout)
|
||||
- [select_sequential_consistency](#setting-select_sequential_consistency)
|
||||
|
||||
<a name="setting-insert_quorum_timeout"></a>
|
||||
|
||||
## insert_quorum_timeout
|
||||
|
||||
Время ожидания кворумной записи в секундах. Если время прошло, а запись так не состоялась, то ClickHouse сгенерирует исключение и клиент должен повторить запрос на запись того же блока на эту же или любую другую реплику.
|
||||
|
||||
По умолчанию 60 секунд.
|
||||
|
||||
**См. также параметры**
|
||||
|
||||
- [insert_quorum](#setting-insert_quorum)
|
||||
- [select_sequential_consistency](#setting-select_sequential_consistency)
|
||||
|
||||
<a name="setting-select_sequential_consistency"></a>
|
||||
|
||||
## select_sequential_consistency
|
||||
|
||||
Включение/выключение последовательной консистентности для запросов `SELECT`:
|
||||
|
||||
- 0 — выключена. Значение по умолчанию.
|
||||
- 1 — включена.
|
||||
|
||||
Когда последовательная консистентность включена, то ClickHouse позволит клиенту выполнить запрос `SELECT` только к тем репликам, которые содержат данные всех предыдущих запросов `INSERT`, выполненных с `insert_quorum`. Если клиент обратится к неполной реплике, то ClickHouse сгенерирует исключение. В запросе SELECT не будут участвовать данные, которые ещё не были записаны на кворум реплик.
|
||||
|
||||
См. также параметры:
|
||||
|
||||
- [insert_quorum](#setting-insert_quorum)
|
||||
- [insert_quorum_timeout](#setting-insert_quorum_timeout)
|
||||
|
@ -56,7 +56,7 @@
|
||||
|
||||
Репликация асинхронная, мульти-мастер. Запросы `INSERT` (а также `ALTER`) можно отправлять на любой доступный сервер. Данные вставятся на сервер, где выполнен запрос, а затем скопируются на остальные серверы. В связи с асинхронностью, только что вставленные данные появляются на остальных репликах с небольшой задержкой. Если часть реплик недоступна, данные на них запишутся тогда, когда они станут доступны. Если реплика доступна, то задержка составляет столько времени, сколько требуется для передачи блока сжатых данных по сети.
|
||||
|
||||
По-умолчанию, запрос INSERT ждёт подтверждения записи только от одной реплики. Если данные были успешно записаны только на одну реплику, и сервер с этой репликой перестал существовать, то записанные данные будут потеряны. Вы можете включить подтверждение записи от нескольких реплик, используя настройку `insert_quorum`.
|
||||
По умолчанию, запрос INSERT ждёт подтверждения записи только от одной реплики. Если данные были успешно записаны только на одну реплику, и сервер с этой репликой перестал существовать, то записанные данные будут потеряны. Вы можете включить подтверждение записи от нескольких реплик, используя настройку [insert_quorum](../settings/settings.md#setting-insert_quorum).
|
||||
|
||||
Каждый блок данных записывается атомарно. Запрос INSERT разбивается на блоки данных размером до `max_insert_block_size = 1048576` строк. То есть, если в запросе `INSERT` менее 1048576 строк, то он делается атомарно.
|
||||
|
||||
|
@ -57,7 +57,7 @@ pointInEllipses(x, y, x₀, y₀, a₀, b₀,...,xₙ, yₙ, aₙ, bₙ)
|
||||
`1`, если точка внутри хотя бы одного из эллипсов, `0`, если нет.
|
||||
|
||||
|
||||
**Примеры**
|
||||
**Пример**
|
||||
|
||||
```sql
|
||||
SELECT pointInEllipses(55.755831, 37.617673, 55.755831, 37.617673, 1.0, 2.0)
|
||||
@ -68,3 +68,34 @@ SELECT pointInEllipses(55.755831, 37.617673, 55.755831, 37.617673, 1.0, 2.0)
|
||||
│ 1 │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## pointInPolygon
|
||||
|
||||
Проверяет, принадлежит ли точка многоугольнику на плоскости.
|
||||
|
||||
```
|
||||
pointInPolygon((x, y), [(a, b), (c, d) ...], ...)
|
||||
```
|
||||
|
||||
**Входные значения**
|
||||
|
||||
- `(x, y)` — координаты точки на плоскости. Тип данных — [Tuple](../../data_types/tuple.md#data_type-tuple) - кортеж из двух чисел.
|
||||
- `[(a, b), (c, d) ...]` — вершины многоугольника. Тип данных — [Array](../../data_types/array.md#data_type-array). Каждая вершина представлена парой координат `(a, b)`. Вершины следует указывать в порядке обхода по или против часовой стрелки. Минимальное количество вершин — 3. Многоугольник должен быть константным.
|
||||
- функция поддерживает также многоугольники с дырками (вырезанными кусками). Для этого случая, добавьте многоугольники, описывающие вырезанные куски, дополнительными аргументами функции. Функция не поддерживает неодносвязные многоугольники.
|
||||
|
||||
**Возвращаемые значения**
|
||||
|
||||
`1`, если точка внутри многоугольника, `0`, если нет.
|
||||
Если точка находится на границе многоугольника, функция может возвращать как 0, так и 1.
|
||||
|
||||
|
||||
**Пример**
|
||||
|
||||
```sql
|
||||
SELECT pointInPolygon((3., 3.), [(6, 0), (8, 4), (5, 8), (0, 2)]) AS res
|
||||
```
|
||||
```
|
||||
┌─res─┐
|
||||
│ 1 │
|
||||
└─────┘
|
||||
```
|
||||
|
@ -200,7 +200,7 @@ KILL QUERY WHERE user='username' SYNC
|
||||
|
||||
Readonly-пользователи могут останавливать только свои запросы.
|
||||
|
||||
По-умолчанию используется асинхронный вариант запроса (`ASYNC`), который не дожидается подтверждения остановки запросов.
|
||||
По умолчанию используется асинхронный вариант запроса (`ASYNC`), который не дожидается подтверждения остановки запросов.
|
||||
|
||||
Синхронный вариант (`SYNC`) ожидает остановки всех запросов и построчно выводит информацию о процессах по ходу их остановки.
|
||||
Ответ содержит колонку `kill_status`, которая может принимать следующие значения:
|
||||
|
@ -31,3 +31,96 @@ inline uint64_t intExp10(int x)
|
||||
|
||||
return table[x];
|
||||
}
|
||||
|
||||
namespace common {
|
||||
|
||||
inline int exp10_i32(int x)
|
||||
{
|
||||
static const int values[] = {
|
||||
1,
|
||||
10,
|
||||
100,
|
||||
1000,
|
||||
10000,
|
||||
100000,
|
||||
1000000,
|
||||
10000000,
|
||||
100000000,
|
||||
1000000000
|
||||
};
|
||||
return values[x];
|
||||
}
|
||||
|
||||
inline int64_t exp10_i64(int x)
|
||||
{
|
||||
static const int64_t values[] = {
|
||||
1ll,
|
||||
10ll,
|
||||
100ll,
|
||||
1000ll,
|
||||
10000ll,
|
||||
100000ll,
|
||||
1000000ll,
|
||||
10000000ll,
|
||||
100000000ll,
|
||||
1000000000ll,
|
||||
10000000000ll,
|
||||
100000000000ll,
|
||||
1000000000000ll,
|
||||
10000000000000ll,
|
||||
100000000000000ll,
|
||||
1000000000000000ll,
|
||||
10000000000000000ll,
|
||||
100000000000000000ll,
|
||||
1000000000000000000ll
|
||||
};
|
||||
return values[x];
|
||||
}
|
||||
|
||||
inline __int128 exp10_i128(int x)
|
||||
{
|
||||
static const __int128 values[] = {
|
||||
static_cast<__int128>(1ll),
|
||||
static_cast<__int128>(10ll),
|
||||
static_cast<__int128>(100ll),
|
||||
static_cast<__int128>(1000ll),
|
||||
static_cast<__int128>(10000ll),
|
||||
static_cast<__int128>(100000ll),
|
||||
static_cast<__int128>(1000000ll),
|
||||
static_cast<__int128>(10000000ll),
|
||||
static_cast<__int128>(100000000ll),
|
||||
static_cast<__int128>(1000000000ll),
|
||||
static_cast<__int128>(10000000000ll),
|
||||
static_cast<__int128>(100000000000ll),
|
||||
static_cast<__int128>(1000000000000ll),
|
||||
static_cast<__int128>(10000000000000ll),
|
||||
static_cast<__int128>(100000000000000ll),
|
||||
static_cast<__int128>(1000000000000000ll),
|
||||
static_cast<__int128>(10000000000000000ll),
|
||||
static_cast<__int128>(100000000000000000ll),
|
||||
static_cast<__int128>(1000000000000000000ll),
|
||||
static_cast<__int128>(1000000000000000000ll) * 10ll,
|
||||
static_cast<__int128>(1000000000000000000ll) * 100ll,
|
||||
static_cast<__int128>(1000000000000000000ll) * 1000ll,
|
||||
static_cast<__int128>(1000000000000000000ll) * 10000ll,
|
||||
static_cast<__int128>(1000000000000000000ll) * 100000ll,
|
||||
static_cast<__int128>(1000000000000000000ll) * 1000000ll,
|
||||
static_cast<__int128>(1000000000000000000ll) * 10000000ll,
|
||||
static_cast<__int128>(1000000000000000000ll) * 100000000ll,
|
||||
static_cast<__int128>(1000000000000000000ll) * 1000000000ll,
|
||||
static_cast<__int128>(1000000000000000000ll) * 10000000000ll,
|
||||
static_cast<__int128>(1000000000000000000ll) * 100000000000ll,
|
||||
static_cast<__int128>(1000000000000000000ll) * 1000000000000ll,
|
||||
static_cast<__int128>(1000000000000000000ll) * 10000000000000ll,
|
||||
static_cast<__int128>(1000000000000000000ll) * 100000000000000ll,
|
||||
static_cast<__int128>(1000000000000000000ll) * 1000000000000000ll,
|
||||
static_cast<__int128>(1000000000000000000ll) * 10000000000000000ll,
|
||||
static_cast<__int128>(1000000000000000000ll) * 100000000000000000ll,
|
||||
static_cast<__int128>(1000000000000000000ll) * 100000000000000000ll * 10ll,
|
||||
static_cast<__int128>(1000000000000000000ll) * 100000000000000000ll * 100ll,
|
||||
static_cast<__int128>(1000000000000000000ll) * 100000000000000000ll * 1000ll
|
||||
};
|
||||
return values[x];
|
||||
}
|
||||
|
||||
} // common
|
||||
|
@ -145,6 +145,11 @@ public:
|
||||
return layer; /// layer выставляется в классе-наследнике BaseDaemonApplication.
|
||||
}
|
||||
|
||||
/// close all process FDs except
|
||||
/// 0-2 -- stdin, stdout, stderr
|
||||
/// also doesn't close global internal pipes for signal handling
|
||||
void closeFDs();
|
||||
|
||||
protected:
|
||||
/// Возвращает TaskManager приложения
|
||||
/// все методы task_manager следует вызывать из одного потока
|
||||
@ -159,6 +164,9 @@ protected:
|
||||
/// thread safe
|
||||
virtual void handleSignal(int signal_id);
|
||||
|
||||
/// initialize termination process and signal handlers
|
||||
virtual void initializeTerminationAndSignalProcessing();
|
||||
|
||||
/// реализация обработки сигналов завершения через pipe не требует блокировки сигнала с помощью sigprocmask во всех потоках
|
||||
void waitForTerminationRequest()
|
||||
#if POCO_CLICKHOUSE_PATCH || POCO_VERSION >= 0x02000000 // in old upstream poco not vitrual
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <signal.h>
|
||||
#include <cxxabi.h>
|
||||
#include <execinfo.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if USE_UNWIND
|
||||
#define UNW_LOCAL_ONLY
|
||||
@ -54,6 +55,7 @@
|
||||
#include <Poco/NumberFormatter.h>
|
||||
#include <Poco/Condition.h>
|
||||
#include <Poco/SyslogChannel.h>
|
||||
#include <Poco/DirectoryIterator.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <IO/WriteBufferFromFileDescriptor.h>
|
||||
#include <IO/ReadBufferFromFileDescriptor.h>
|
||||
@ -845,8 +847,40 @@ std::string BaseDaemon::getDefaultCorePath() const
|
||||
return "/opt/cores/";
|
||||
}
|
||||
|
||||
void BaseDaemon::closeFDs()
|
||||
{
|
||||
#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__))
|
||||
Poco::File proc_path{"/dev/fd"};
|
||||
#else
|
||||
Poco::File proc_path{"/proc/self/fd"};
|
||||
#endif
|
||||
if (proc_path.isDirectory()) /// Hooray, proc exists
|
||||
{
|
||||
Poco::DirectoryIterator itr(proc_path), end;
|
||||
for (; itr != end; ++itr)
|
||||
{
|
||||
long fd = DB::parse<long>(itr.name());
|
||||
if (fd > 2 && fd != signal_pipe.read_fd && fd != signal_pipe.write_fd)
|
||||
::close(fd);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
long max_fd = -1;
|
||||
#ifdef _SC_OPEN_MAX
|
||||
max_fd = sysconf(_SC_OPEN_MAX);
|
||||
if (max_fd == -1)
|
||||
#endif
|
||||
max_fd = 256; /// bad fallback
|
||||
for (long fd = 3; fd < max_fd; ++fd)
|
||||
if (fd != signal_pipe.read_fd && fd != signal_pipe.write_fd)
|
||||
::close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseDaemon::initialize(Application & self)
|
||||
{
|
||||
closeFDs();
|
||||
task_manager.reset(new Poco::TaskManager);
|
||||
ServerApplication::initialize(self);
|
||||
|
||||
@ -1031,6 +1065,19 @@ void BaseDaemon::initialize(Application & self)
|
||||
throw Poco::Exception("Cannot change directory to " + core_path);
|
||||
}
|
||||
|
||||
initializeTerminationAndSignalProcessing();
|
||||
|
||||
logRevision();
|
||||
|
||||
for (const auto & key : DB::getMultipleKeysFromConfig(config(), "", "graphite"))
|
||||
{
|
||||
graphite_writers.emplace(key, std::make_unique<GraphiteWriter>(key));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BaseDaemon::initializeTerminationAndSignalProcessing()
|
||||
{
|
||||
std::set_terminate(terminate_handler);
|
||||
|
||||
/// We want to avoid SIGPIPE when working with sockets and pipes, and just handle return value/errno instead.
|
||||
@ -1071,15 +1118,9 @@ void BaseDaemon::initialize(Application & self)
|
||||
static KillingErrorHandler killing_error_handler;
|
||||
Poco::ErrorHandler::set(&killing_error_handler);
|
||||
|
||||
logRevision();
|
||||
|
||||
signal_listener.reset(new SignalListener(*this));
|
||||
signal_listener_thread.start(*signal_listener);
|
||||
|
||||
for (const auto & key : DB::getMultipleKeysFromConfig(config(), "", "graphite"))
|
||||
{
|
||||
graphite_writers.emplace(key, std::make_unique<GraphiteWriter>(key));
|
||||
}
|
||||
}
|
||||
|
||||
void BaseDaemon::logRevision() const
|
||||
|
@ -1,5 +1,5 @@
|
||||
option (ENABLE_MYSQL "Enable MySQL" ${OS_LINUX})
|
||||
if (OS_LINUX)
|
||||
if (OS_LINUX_X86_64)
|
||||
option (USE_INTERNAL_MYSQL_LIBRARY "Set to FALSE to use system mysqlclient library instead of bundled" ${NOT_UNBUNDLED})
|
||||
else ()
|
||||
option (USE_INTERNAL_MYSQL_LIBRARY "Set to FALSE to use system mysqlclient library instead of bundled" OFF)
|
||||
|
Loading…
Reference in New Issue
Block a user