CLICKHOUSE-3878: Move last interaction with ODBC from main code to bridge, split Handlers file on three different, slightly remove copy-paste code

This commit is contained in:
alesapin 2018-08-19 20:09:54 +03:00
parent b38cc88d9d
commit f6c9587d0a
23 changed files with 288 additions and 151 deletions

View File

@ -1,12 +1,30 @@
add_library (clickhouse-odbc-bridge-lib add_library (clickhouse-odbc-bridge-lib
Handlers.cpp PingHandler.cpp
MainHandler.cpp
ColumnInfoHandler.cpp
HandlerFactory.cpp HandlerFactory.cpp
ODBCBridge.cpp ODBCBridge.cpp
validateODBCConnectionString.cpp
) )
target_link_libraries (clickhouse-odbc-bridge-lib clickhouse_common_io daemon dbms) target_link_libraries (clickhouse-odbc-bridge-lib clickhouse_common_io daemon dbms)
target_include_directories (clickhouse-odbc-bridge-lib PUBLIC ${ClickHouse_SOURCE_DIR}/libs/libdaemon/include) target_include_directories (clickhouse-odbc-bridge-lib PUBLIC ${ClickHouse_SOURCE_DIR}/libs/libdaemon/include)
if (USE_POCO_SQLODBC)
target_link_libraries (clickhouse-odbc-bridge-lib ${Poco_SQLODBC_LIBRARY})
target_include_directories (clickhouse-odbc-bridge-lib SYSTEM PRIVATE ${ODBC_INCLUDE_DIRECTORIES} ${Poco_SQLODBC_INCLUDE_DIRS})
endif ()
if (USE_POCO_DATAODBC)
target_link_libraries (clickhouse-odbc-bridge-lib ${Poco_DataODBC_LIBRARY})
target_include_directories (clickhouse-odbc-bridge-lib SYSTEM PRIVATE ${ODBC_INCLUDE_DIRECTORIES} ${Poco_DataODBC_INCLUDE_DIRS})
endif()
if (ENABLE_TESTS)
add_subdirectory (tests)
endif ()
if (CLICKHOUSE_SPLIT_BINARY) if (CLICKHOUSE_SPLIT_BINARY)
add_executable (clickhouse-odbc-bridge odbc-bridge.cpp) add_executable (clickhouse-odbc-bridge odbc-bridge.cpp)
target_link_libraries (clickhouse-odbc-bridge clickhouse-odbc-bridge-lib) target_link_libraries (clickhouse-odbc-bridge clickhouse-odbc-bridge-lib)

View File

@ -0,0 +1,123 @@
#include "ColumnInfoHandler.h"
#if USE_POCO_SQLODBC || USE_POCO_DATAODBC
#include <Poco/Data/ODBC/ODBCException.h>
#include <Poco/Data/ODBC/SessionImpl.h>
#include <Poco/Data/ODBC/Utility.h>
#include <Poco/Net/HTTPServerRequest.h>
#include <Poco/Net/HTTPServerResponse.h>
#include <DataTypes/DataTypeFactory.h>
#include <IO/WriteBufferFromHTTPServerResponse.h>
#include <IO/WriteHelpers.h>
#include <Common/HTMLForm.h>
#include <common/logger_useful.h>
#include <ext/scope_guard.h>
#include "validateODBCConnectionString.h"
namespace DB
{
namespace
{
DataTypePtr getDataType(SQLSMALLINT type)
{
const auto & factory = DataTypeFactory::instance();
switch (type)
{
case SQL_INTEGER:
return factory.get("Int32");
case SQL_SMALLINT:
return factory.get("Int16");
case SQL_FLOAT:
return factory.get("Float32");
case SQL_REAL:
return factory.get("Float32");
case SQL_DOUBLE:
return factory.get("Float64");
case SQL_DATETIME:
return factory.get("DateTime");
case SQL_TYPE_TIMESTAMP:
return factory.get("DateTime");
case SQL_TYPE_DATE:
return factory.get("Date");
default:
return factory.get("String");
}
}
}
void ODBCColumnsInfoHandler::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("table"))
{
process_error("No 'table' param in request URL");
return;
}
if (!params.has("connection_string"))
{
process_error("No 'connection_string' in request URL");
return;
}
std::string table_name = params.get("table");
std::string connection_string = params.get("connection_string");
LOG_TRACE(log, "Will fetch info for table '" << table_name << "'");
LOG_TRACE(log, "Got connection str '" << connection_string << "'");
try
{
Poco::Data::ODBC::SessionImpl session(validateODBCConnectionString(connection_string), DBMS_DEFAULT_CONNECT_TIMEOUT_SEC);
SQLHDBC hdbc = session.dbc().handle();
SQLHSTMT hstmt = nullptr;
if (Poco::Data::ODBC::Utility::isError(SQLAllocStmt(hdbc, &hstmt)))
throw Poco::Data::ODBC::ODBCException("Could not allocate connection handle.");
SCOPE_EXIT(SQLFreeStmt(hstmt, SQL_DROP));
/// TODO Why not do SQLColumns instead?
std::string query = "SELECT * FROM " + table_name + " WHERE 1 = 0";
if (Poco::Data::ODBC::Utility::isError(Poco::Data::ODBC::SQLPrepare(hstmt, reinterpret_cast<SQLCHAR *>(&query[0]), query.size())))
throw Poco::Data::ODBC::DescriptorException(session.dbc());
if (Poco::Data::ODBC::Utility::isError(SQLExecute(hstmt)))
throw Poco::Data::ODBC::StatementException(hstmt);
SQLSMALLINT cols = 0;
if (Poco::Data::ODBC::Utility::isError(SQLNumResultCols(hstmt, &cols)))
throw Poco::Data::ODBC::StatementException(hstmt);
/// TODO cols not checked
NamesAndTypesList columns;
for (SQLSMALLINT ncol = 1; ncol <= cols; ++ncol)
{
SQLSMALLINT type = 0;
/// TODO Why 301?
SQLCHAR column_name[301];
/// TODO Result is not checked.
Poco::Data::ODBC::SQLDescribeCol(hstmt, ncol, column_name, sizeof(column_name), NULL, &type, NULL, NULL, NULL);
columns.emplace_back(reinterpret_cast<char *>(column_name), getDataType(type));
}
WriteBufferFromHTTPServerResponse out(request, response, keep_alive_timeout);
writeStringBinary(columns.toString(), out);
}
catch (...)
{
process_error("Error getting columns from ODBC '" + getCurrentExceptionMessage(false) + "'");
tryLogCurrentException(log);
}
}
}
#endif

View File

@ -0,0 +1,29 @@
#pragma once
#include <Common/config.h>
#include <Interpreters/Context.h>
#include <Poco/Logger.h>
#include <Poco/Net/HTTPRequestHandler.h>
#if USE_POCO_SQLODBC || USE_POCO_DATAODBC
/** The structure of the table is taken from the mysql query "SELECT * FROM table WHERE 1=0".
* If there is no such table, an exception is thrown.
*/
namespace DB
{
class ODBCColumnsInfoHandler : public Poco::Net::HTTPRequestHandler
{
public:
ODBCColumnsInfoHandler(size_t keep_alive_timeout_, std::shared_ptr<Context> context_)
: log(&Poco::Logger::get("ODBCColumnsInfoHandler")), keep_alive_timeout(keep_alive_timeout_), context(context_)
{
}
void handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response) override;
private:
Poco::Logger * log;
size_t keep_alive_timeout;
std::shared_ptr<Context> context;
};
}
#endif

View File

@ -1,4 +1,6 @@
#include "HandlerFactory.h" #include "HandlerFactory.h"
#include "PingHandler.h"
#include "ColumnInfoHandler.h"
#include <Common/HTMLForm.h> #include <Common/HTMLForm.h>
#include <Poco/Ext/SessionPoolHelpers.h> #include <Poco/Ext/SessionPoolHelpers.h>
@ -9,15 +11,24 @@ namespace DB
{ {
Poco::Net::HTTPRequestHandler * HandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest & request) Poco::Net::HTTPRequestHandler * HandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest & request)
{ {
const auto & uri = request.getURI(); Poco::URI uri{request.getURI()};
LOG_TRACE(log, "Request URI: " + uri); LOG_TRACE(log, "Request URI: " + uri.toString());
if (uri == "/ping" && request.getMethod() == Poco::Net::HTTPRequest::HTTP_GET) if (uri.getPath() == "/ping" && request.getMethod() == Poco::Net::HTTPRequest::HTTP_GET)
return new PingHandler(keep_alive_timeout); return new PingHandler(keep_alive_timeout);
if (request.getMethod() == Poco::Net::HTTPRequest::HTTP_POST) if (request.getMethod() == Poco::Net::HTTPRequest::HTTP_POST)
return new ODBCHandler(pool_map, keep_alive_timeout, context); {
if (uri.getPath() == "/colinfo")
#if USE_POCO_SQLODBC || USE_POCO_DATAODBC
return new ODBCColumnsInfoHandler(keep_alive_timeout, context);
#else
return nullptr;
#endif
else
return new ODBCHandler(pool_map, keep_alive_timeout, context);
}
return nullptr; return nullptr;
} }
} }

View File

@ -3,7 +3,8 @@
#include <Poco/Logger.h> #include <Poco/Logger.h>
#include <Poco/Net/HTTPRequestHandler.h> #include <Poco/Net/HTTPRequestHandler.h>
#include <Poco/Net/HTTPRequestHandlerFactory.h> #include <Poco/Net/HTTPRequestHandlerFactory.h>
#include "Handlers.h" #include "MainHandler.h"
#include "ColumnInfoHandler.h"
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wunused-parameter"
@ -13,7 +14,7 @@
namespace DB namespace DB
{ {
/** Factory for '/ping' and '/' handlers. /** Factory for '/ping', '/' and '/colinfo' handlers.
* Also stores Session pools for ODBC connections * Also stores Session pools for ODBC connections
*/ */
class HandlerFactory : public Poco::Net::HTTPRequestHandlerFactory class HandlerFactory : public Poco::Net::HTTPRequestHandlerFactory

View File

@ -1,30 +1,24 @@
#include "Handlers.h" #include "MainHandler.h"
#include <Common/HTMLForm.h>
#include "validateODBCConnectionString.h"
#include <memory> #include <memory>
#include <DataStreams/IBlockOutputStream.h>
#include <DataStreams/copyData.h> #include <DataStreams/copyData.h>
#include <DataTypes/DataTypeFactory.h> #include <DataTypes/DataTypeFactory.h>
#include <Dictionaries/ODBCBlockInputStream.h> #include <Dictionaries/ODBCBlockInputStream.h>
#include <Formats/BinaryRowInputStream.h> #include <Formats/BinaryRowInputStream.h>
#include <Formats/FormatFactory.h> #include <Formats/FormatFactory.h>
#include <IO/ReadBufferFromIStream.h>
#include <IO/WriteBufferFromHTTPServerResponse.h> #include <IO/WriteBufferFromHTTPServerResponse.h>
#include <IO/WriteHelpers.h> #include <IO/WriteHelpers.h>
#include <Interpreters/Context.h> #include <Interpreters/Context.h>
#include <boost/algorithm/string.hpp>
#include <boost/tokenizer.hpp>
#include <Poco/Ext/SessionPoolHelpers.h> #include <Poco/Ext/SessionPoolHelpers.h>
#include <Poco/Net/HTTPServerRequest.h> #include <Poco/Net/HTTPServerRequest.h>
#include <Poco/Net/HTTPServerResponse.h> #include <Poco/Net/HTTPServerResponse.h>
#include <Common/HTMLForm.h>
#include <common/logger_useful.h> #include <common/logger_useful.h>
namespace DB namespace DB
{ {
namespace ErrorCodes
{
extern const int BAD_REQUEST_PARAMETER;
}
namespace namespace
{ {
std::unique_ptr<Block> parseColumns(std::string && column_string) std::unique_ptr<Block> parseColumns(std::string && column_string)
@ -44,7 +38,7 @@ ODBCHandler::PoolPtr ODBCHandler::getPool(const std::string & connection_str)
if (!pool_map->count(connection_str)) if (!pool_map->count(connection_str))
{ {
pool_map->emplace(connection_str, createAndCheckResizePocoSessionPool([connection_str] { pool_map->emplace(connection_str, createAndCheckResizePocoSessionPool([connection_str] {
return std::make_shared<Poco::Data::SessionPool>("ODBC", connection_str); return std::make_shared<Poco::Data::SessionPool>("ODBC", validateODBCConnectionString(connection_str));
})); }));
} }
return pool_map->at(connection_str); return pool_map->at(connection_str);
@ -129,18 +123,4 @@ void ODBCHandler::handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Ne
tryLogCurrentException(log); 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");
}
}
} }

View File

@ -46,15 +46,4 @@ private:
PoolPtr getPool(const std::string & connection_str); 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;
};
} }

View File

@ -0,0 +1,22 @@
#include "PingHandler.h"
#include <Poco/Net/HTTPServerRequest.h>
#include <Poco/Net/HTTPServerResponse.h>
#include <Common/Exception.h>
#include <IO/HTTPCommon.h>
namespace DB
{
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");
}
}
}

View File

@ -0,0 +1,17 @@
#pragma once
#include <Poco/Net/HTTPRequestHandler.h>
namespace DB
{
/** 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;
};
}

View File

@ -0,0 +1,2 @@
add_executable (validate-odbc-connection-string validate-odbc-connection-string.cpp)
target_link_libraries (validate-odbc-connection-string clickhouse-odbc-bridge-lib)

View File

@ -1,6 +1,6 @@
#include <iostream> #include <iostream>
#include <Common/Exception.h> #include <Common/Exception.h>
#include <Common/validateODBCConnectionString.h> #include "../validateODBCConnectionString.h"
using namespace DB; using namespace DB;

View File

@ -5,7 +5,7 @@
#include <common/find_first_symbols.h> #include <common/find_first_symbols.h>
#include <Common/Exception.h> #include <Common/Exception.h>
#include <Common/StringUtils/StringUtils.h> #include <Common/StringUtils/StringUtils.h>
#include <Common/validateODBCConnectionString.h> #include "validateODBCConnectionString.h"
namespace DB namespace DB

View File

@ -1,12 +1,11 @@
#include <Common/ODBCBridgeHelper.h> #include <Common/ODBCBridgeHelper.h>
#include <sstream> #include <sstream>
#include <Common/validateODBCConnectionString.h>
#include <IO/ReadHelpers.h> #include <IO/ReadHelpers.h>
#include <IO/ReadWriteBufferFromHTTP.h> #include <IO/ReadWriteBufferFromHTTP.h>
#include <Poco/Net/HTTPRequest.h>
#include <Poco/Path.h> #include <Poco/Path.h>
#include <Poco/Util/AbstractConfiguration.h> #include <Poco/Util/AbstractConfiguration.h>
#include <Poco/Net/HTTPRequest.h>
#include <Common/ShellCommand.h> #include <Common/ShellCommand.h>
#include <common/logger_useful.h> #include <common/logger_useful.h>
#include <ext/range.h> #include <ext/range.h>
@ -18,8 +17,9 @@ namespace ErrorCodes
{ {
extern const int EXTERNAL_SERVER_IS_NOT_RESPONDING; extern const int EXTERNAL_SERVER_IS_NOT_RESPONDING;
} }
ODBCBridgeHelper::ODBCBridgeHelper(const Configuration & config_, const Poco::Timespan & http_timeout_, const std::string & connection_string_) ODBCBridgeHelper::ODBCBridgeHelper(
: config(config_), http_timeout(http_timeout_), connection_string(validateODBCConnectionString(connection_string_)) const Configuration & config_, const Poco::Timespan & http_timeout_, const std::string & connection_string_)
: config(config_), http_timeout(http_timeout_), connection_string(connection_string_)
{ {
size_t bridge_port = config.getUInt("odbc_bridge.port", DEFAULT_PORT); size_t bridge_port = config.getUInt("odbc_bridge.port", DEFAULT_PORT);
std::string bridge_host = config.getString("odbc_bridge.host", DEFAULT_HOST); std::string bridge_host = config.getString("odbc_bridge.host", DEFAULT_HOST);
@ -102,4 +102,30 @@ void ODBCBridgeHelper::startODBCBridgeSync() const
throw Exception("ODBCBridgeHelper: clickhouse-odbc-bridge is not responding", ErrorCodes::EXTERNAL_SERVER_IS_NOT_RESPONDING); throw Exception("ODBCBridgeHelper: clickhouse-odbc-bridge is not responding", ErrorCodes::EXTERNAL_SERVER_IS_NOT_RESPONDING);
} }
} }
Poco::URI ODBCBridgeHelper::getMainURI() const
{
size_t bridge_port = config.getUInt("odbc_bridge.port", DEFAULT_PORT);
std::string bridge_host = config.getString("odbc_bridge.host", DEFAULT_HOST);
Poco::URI main_uri;
main_uri.setHost(bridge_host);
main_uri.setPort(bridge_port);
main_uri.setScheme("http");
main_uri.setPath(MAIN_HANDLER);
return main_uri;
}
Poco::URI ODBCBridgeHelper::getColInfoURI() const
{
size_t bridge_port = config.getUInt("odbc_bridge.port", DEFAULT_PORT);
std::string bridge_host = config.getString("odbc_bridge.host", DEFAULT_HOST);
Poco::URI col_info_uri;
col_info_uri.setHost(bridge_host);
col_info_uri.setPort(bridge_port);
col_info_uri.setScheme("http");
col_info_uri.setPath(COL_INFO_HANDLER);
return col_info_uri;
}
} }

View File

@ -35,6 +35,7 @@ public:
static constexpr inline auto DEFAULT_FORMAT = "RowBinary"; static constexpr inline auto DEFAULT_FORMAT = "RowBinary";
static constexpr inline auto PING_HANDLER = "/ping"; static constexpr inline auto PING_HANDLER = "/ping";
static constexpr inline auto MAIN_HANDLER = "/"; static constexpr inline auto MAIN_HANDLER = "/";
static constexpr inline auto COL_INFO_HANDLER = "/colinfo";
static constexpr inline auto PING_OK_ANSWER = "Ok."; static constexpr inline auto PING_OK_ANSWER = "Ok.";
ODBCBridgeHelper(const Configuration & config_, const Poco::Timespan & http_timeout_, const std::string & connection_string_); ODBCBridgeHelper(const Configuration & config_, const Poco::Timespan & http_timeout_, const std::string & connection_string_);
@ -44,5 +45,8 @@ public:
void startODBCBridge() const; void startODBCBridge() const;
void startODBCBridgeSync() const; void startODBCBridgeSync() const;
Poco::URI getMainURI() const;
Poco::URI getColInfoURI() const;
}; };
} }

View File

@ -71,6 +71,3 @@ target_link_libraries (cow_columns clickhouse_common_io)
add_executable (stopwatch stopwatch.cpp) add_executable (stopwatch stopwatch.cpp)
target_link_libraries (stopwatch clickhouse_common_io) 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)

View File

@ -75,13 +75,7 @@ ODBCDictionarySource::ODBCDictionarySource(const DictionaryStructure & dict_stru
timeouts{ConnectionTimeouts::getHTTPTimeouts(context.getSettingsRef())}, timeouts{ConnectionTimeouts::getHTTPTimeouts(context.getSettingsRef())},
global_context(context) global_context(context)
{ {
const auto & global_config = context.getConfigRef(); bridge_url = odbc_bridge_helper.getMainURI();
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);
bridge_url.setHost(bridge_host);
bridge_url.setPort(bridge_port);
bridge_url.setScheme("http");
auto url_params = odbc_bridge_helper.getURLParams(sample_block.getNamesAndTypesList().toString(), max_block_size); auto url_params = odbc_bridge_helper.getURLParams(sample_block.getNamesAndTypesList().toString(), max_block_size);
for (const auto & [name, value] : url_params) for (const auto & [name, value] : url_params)

View File

@ -5,7 +5,6 @@
#include <Storages/StorageFactory.h> #include <Storages/StorageFactory.h>
#include <Storages/StorageODBC.h> #include <Storages/StorageODBC.h>
#include <Storages/transformQueryForExternalDatabase.h> #include <Storages/transformQueryForExternalDatabase.h>
#include <Poco/Ext/SessionPoolHelpers.h>
#include <Poco/Util/AbstractConfiguration.h> #include <Poco/Util/AbstractConfiguration.h>
#include <common/logger_useful.h> #include <common/logger_useful.h>
@ -38,13 +37,7 @@ StorageODBC::StorageODBC(const std::string & table_name_,
, remote_table_name(remote_table_name_) , remote_table_name(remote_table_name_)
, log(&Poco::Logger::get("StorageODBC")) , log(&Poco::Logger::get("StorageODBC"))
{ {
const auto & config = context_global.getConfigRef(); uri = odbc_bridge_helper.getMainURI();
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");
} }
std::string StorageODBC::getReadMethod() const std::string StorageODBC::getReadMethod() const

View File

@ -6,13 +6,3 @@ list(REMOVE_ITEM clickhouse_table_functions_headers ITableFunction.h TableFuncti
add_library(clickhouse_table_functions ${clickhouse_table_functions_sources}) add_library(clickhouse_table_functions ${clickhouse_table_functions_sources})
target_link_libraries(clickhouse_table_functions clickhouse_storages_system dbms ${Poco_Foundation_LIBRARY}) target_link_libraries(clickhouse_table_functions clickhouse_storages_system dbms ${Poco_Foundation_LIBRARY})
if (USE_POCO_SQLODBC)
target_link_libraries (clickhouse_table_functions ${Poco_SQLODBC_LIBRARY})
target_include_directories (clickhouse_table_functions SYSTEM PRIVATE ${ODBC_INCLUDE_DIRECTORIES} ${Poco_SQLODBC_INCLUDE_DIRS})
endif ()
if (USE_POCO_DATAODBC)
target_link_libraries (clickhouse_table_functions ${Poco_DataODBC_LIBRARY})
target_include_directories (clickhouse_table_functions SYSTEM PRIVATE ${ODBC_INCLUDE_DIRECTORIES} ${Poco_DataODBC_INCLUDE_DIRS})
endif ()

View File

@ -1,24 +1,22 @@
#include <TableFunctions/TableFunctionODBC.h> #include <TableFunctions/TableFunctionODBC.h>
#if USE_POCO_SQLODBC || USE_POCO_DATAODBC
#include <type_traits> #include <type_traits>
#include <ext/scope_guard.h> #include <ext/scope_guard.h>
#include <DataTypes/DataTypeFactory.h> #include <DataTypes/DataTypeFactory.h>
#include <Interpreters/evaluateConstantExpression.h> #include <Interpreters/evaluateConstantExpression.h>
#include <IO/ReadHelpers.h>
#include <IO/ReadWriteBufferFromHTTP.h>
#include <Parsers/ASTFunction.h> #include <Parsers/ASTFunction.h>
#include <Parsers/ASTLiteral.h> #include <Parsers/ASTLiteral.h>
#include <Poco/Net/HTTPRequest.h>
#include <Storages/StorageODBC.h> #include <Storages/StorageODBC.h>
#include <TableFunctions/ITableFunction.h> #include <TableFunctions/ITableFunction.h>
#include <TableFunctions/TableFunctionFactory.h> #include <TableFunctions/TableFunctionFactory.h>
#include <Common/Exception.h> #include <Common/Exception.h>
#include <Common/typeid_cast.h> #include <Common/typeid_cast.h>
#include <Common/ODBCBridgeHelper.h>
#include <Core/Defines.h> #include <Core/Defines.h>
#include <Poco/Data/ODBC/ODBCException.h>
#include <Poco/Data/ODBC/SessionImpl.h>
#include <Poco/Data/ODBC/Utility.h>
namespace DB namespace DB
{ {
@ -28,33 +26,6 @@ namespace ErrorCodes
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
} }
DataTypePtr getDataType(SQLSMALLINT type)
{
const auto & factory = DataTypeFactory::instance();
switch (type)
{
case SQL_INTEGER:
return factory.get("Int32");
case SQL_SMALLINT:
return factory.get("Int16");
case SQL_FLOAT:
return factory.get("Float32");
case SQL_REAL:
return factory.get("Float32");
case SQL_DOUBLE:
return factory.get("Float64");
case SQL_DATETIME:
return factory.get("DateTime");
case SQL_TYPE_TIMESTAMP:
return factory.get("DateTime");
case SQL_TYPE_DATE:
return factory.get("Date");
default:
return factory.get("String");
}
}
StoragePtr TableFunctionODBC::executeImpl(const ASTPtr & ast_function, const Context & context) const StoragePtr TableFunctionODBC::executeImpl(const ASTPtr & ast_function, const Context & context) const
{ {
const ASTFunction & args_func = typeid_cast<const ASTFunction &>(*ast_function); const ASTFunction & args_func = typeid_cast<const ASTFunction &>(*ast_function);
@ -73,41 +44,19 @@ StoragePtr TableFunctionODBC::executeImpl(const ASTPtr & ast_function, const Con
std::string connection_string = 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>(); std::string table_name = static_cast<const ASTLiteral &>(*args[1]).value.safeGet<String>();
const auto & config = context.getConfigRef();
ODBCBridgeHelper helper(config, context.getSettingsRef().http_receive_timeout.value, connection_string);
helper.startODBCBridgeSync();
Poco::Data::ODBC::SessionImpl session(connection_string, DBMS_DEFAULT_CONNECT_TIMEOUT_SEC); Poco::URI col_info_uri = helper.getColInfoURI();
SQLHDBC hdbc = session.dbc().handle(); col_info_uri.addQueryParameter("connection_string", connection_string);
col_info_uri.addQueryParameter("table", table_name);
SQLHSTMT hstmt = nullptr; ReadWriteBufferFromHTTP buf(col_info_uri, Poco::Net::HTTPRequest::HTTP_POST, nullptr);
if (Poco::Data::ODBC::Utility::isError(SQLAllocStmt(hdbc, &hstmt))) std::string col_info;
throw Poco::Data::ODBC::ODBCException("Could not allocate connection handle."); readStringBinary(col_info, buf);
NamesAndTypesList columns = NamesAndTypesList::parse(col_info);
SCOPE_EXIT(SQLFreeStmt(hstmt, SQL_DROP));
/// TODO Why not do SQLColumns instead?
std::string query = "SELECT * FROM " + table_name + " WHERE 1 = 0";
if (Poco::Data::ODBC::Utility::isError(Poco::Data::ODBC::SQLPrepare(hstmt, reinterpret_cast<SQLCHAR *>(&query[0]), query.size())))
throw Poco::Data::ODBC::DescriptorException(session.dbc());
if (Poco::Data::ODBC::Utility::isError(SQLExecute(hstmt)))
throw Poco::Data::ODBC::StatementException(hstmt);
SQLSMALLINT cols = 0;
if (Poco::Data::ODBC::Utility::isError(SQLNumResultCols(hstmt, &cols)))
throw Poco::Data::ODBC::StatementException(hstmt);
/// TODO cols not checked
NamesAndTypesList columns;
for (SQLSMALLINT ncol = 1; ncol <= cols; ++ncol)
{
SQLSMALLINT type = 0;
/// TODO Why 301?
SQLCHAR column_name[301];
/// TODO Result is not checked.
Poco::Data::ODBC::SQLDescribeCol(hstmt, ncol, column_name, sizeof(column_name), NULL, &type, NULL, NULL, NULL);
columns.emplace_back(reinterpret_cast<char *>(column_name), getDataType(type));
}
auto result = StorageODBC::create(table_name, connection_string, "", table_name, ColumnsDescription{columns}, context); auto result = StorageODBC::create(table_name, connection_string, "", table_name, ColumnsDescription{columns}, context);
result->startup(); result->startup();
@ -120,5 +69,3 @@ void registerTableFunctionODBC(TableFunctionFactory & factory)
factory.registerFunction<TableFunctionODBC>(); factory.registerFunction<TableFunctionODBC>();
} }
} }
#endif

View File

@ -1,16 +1,12 @@
#pragma once #pragma once
#include <Common/config.h> #include <Common/config.h>
#if USE_POCO_SQLODBC || USE_POCO_DATAODBC
#include <TableFunctions/ITableFunction.h> #include <TableFunctions/ITableFunction.h>
namespace DB namespace DB
{ {
/* odbc (odbc connect string, table) - creates a temporary StorageODBC. /* odbc (odbc connect string, table) - creates a temporary StorageODBC.
* The structure of the table is taken from the mysql query "SELECT * FROM table WHERE 1=0".
* If there is no such table, an exception is thrown.
*/ */
class TableFunctionODBC : public ITableFunction class TableFunctionODBC : public ITableFunction
{ {
@ -24,5 +20,3 @@ private:
StoragePtr executeImpl(const ASTPtr & ast_function, const Context & context) const override; StoragePtr executeImpl(const ASTPtr & ast_function, const Context & context) const override;
}; };
} }
#endif