Merge pull request #15111 from vitlibar/grpc-protocol

Implement GRPC protocol.
This commit is contained in:
Vitaly Baranov 2020-11-25 09:00:32 +03:00 committed by GitHub
commit 08b3707842
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 3655 additions and 44 deletions

6
.gitmodules vendored
View File

@ -44,6 +44,7 @@
[submodule "contrib/protobuf"]
path = contrib/protobuf
url = https://github.com/ClickHouse-Extras/protobuf.git
branch = v3.13.0.1
[submodule "contrib/boost"]
path = contrib/boost
url = https://github.com/ClickHouse-Extras/boost.git
@ -107,6 +108,7 @@
[submodule "contrib/grpc"]
path = contrib/grpc
url = https://github.com/ClickHouse-Extras/grpc.git
branch = v1.33.2
[submodule "contrib/aws"]
path = contrib/aws
url = https://github.com/ClickHouse-Extras/aws-sdk-cpp.git
@ -200,3 +202,7 @@
[submodule "contrib/xz"]
path = contrib/xz
url = https://github.com/xz-mirror/xz
[submodule "contrib/abseil-cpp"]
path = contrib/abseil-cpp
url = https://github.com/ClickHouse-Extras/abseil-cpp.git
branch = lts_2020_02_25

View File

@ -0,0 +1,19 @@
#define _GNU_SOURCE
#include <sys/socket.h>
#include <errno.h>
#include <fcntl.h>
#include "syscall.h"
int accept4(int fd, struct sockaddr *restrict addr, socklen_t *restrict len, int flg)
{
if (!flg) return accept(fd, addr, len);
int ret = socketcall_cp(accept4, fd, addr, len, flg, 0, 0);
if (ret>=0 || (errno != ENOSYS && errno != EINVAL)) return ret;
ret = accept(fd, addr, len);
if (ret<0) return ret;
if (flg & SOCK_CLOEXEC)
__syscall(SYS_fcntl, ret, F_SETFD, FD_CLOEXEC);
if (flg & SOCK_NONBLOCK)
__syscall(SYS_fcntl, ret, F_SETFL, O_NONBLOCK);
return ret;
}

View File

@ -0,0 +1,37 @@
#include <sys/epoll.h>
#include <signal.h>
#include <errno.h>
#include "syscall.h"
int epoll_create(int size)
{
return epoll_create1(0);
}
int epoll_create1(int flags)
{
int r = __syscall(SYS_epoll_create1, flags);
#ifdef SYS_epoll_create
if (r==-ENOSYS && !flags) r = __syscall(SYS_epoll_create, 1);
#endif
return __syscall_ret(r);
}
int epoll_ctl(int fd, int op, int fd2, struct epoll_event *ev)
{
return syscall(SYS_epoll_ctl, fd, op, fd2, ev);
}
int epoll_pwait(int fd, struct epoll_event *ev, int cnt, int to, const sigset_t *sigs)
{
int r = __syscall(SYS_epoll_pwait, fd, ev, cnt, to, sigs, _NSIG/8);
#ifdef SYS_epoll_wait
if (r==-ENOSYS && !sigs) r = __syscall(SYS_epoll_wait, fd, ev, cnt, to);
#endif
return __syscall_ret(r);
}
int epoll_wait(int fd, struct epoll_event *ev, int cnt, int to)
{
return epoll_pwait(fd, ev, cnt, to, 0);
}

View File

@ -0,0 +1,23 @@
#include <sys/eventfd.h>
#include <unistd.h>
#include <errno.h>
#include "syscall.h"
int eventfd(unsigned int count, int flags)
{
int r = __syscall(SYS_eventfd2, count, flags);
#ifdef SYS_eventfd
if (r==-ENOSYS && !flags) r = __syscall(SYS_eventfd, count);
#endif
return __syscall_ret(r);
}
int eventfd_read(int fd, eventfd_t *value)
{
return (sizeof(*value) == read(fd, value, sizeof(*value))) ? 0 : -1;
}
int eventfd_write(int fd, eventfd_t value)
{
return (sizeof(value) == write(fd, &value, sizeof(value))) ? 0 : -1;
}

View File

@ -0,0 +1,45 @@
#include <sys/auxv.h>
#include <unistd.h> // __environ
#include <errno.h>
// We don't have libc struct available here. Compute aux vector manually.
static unsigned long * __auxv = NULL;
static unsigned long __auxv_secure = 0;
static size_t __find_auxv(unsigned long type)
{
size_t i;
for (i = 0; __auxv[i]; i += 2)
{
if (__auxv[i] == type)
return i + 1;
}
return (size_t) -1;
}
__attribute__((constructor)) static void __auxv_init()
{
size_t i;
for (i = 0; __environ[i]; i++);
__auxv = (unsigned long *) (__environ + i + 1);
size_t secure_idx = __find_auxv(AT_SECURE);
if (secure_idx != ((size_t) -1))
__auxv_secure = __auxv[secure_idx];
}
unsigned long getauxval(unsigned long type)
{
if (type == AT_SECURE)
return __auxv_secure;
if (__auxv)
{
size_t index = __find_auxv(type);
if (index != ((size_t) -1))
return __auxv[index];
}
errno = ENOENT;
return 0;
}

View File

@ -0,0 +1,8 @@
#define _GNU_SOURCE
#include <stdlib.h>
#include <sys/auxv.h>
char * secure_getenv(const char * name)
{
return getauxval(AT_SECURE) ? NULL : getenv(name);
}

View File

@ -13,3 +13,11 @@ long __syscall(syscall_arg_t, ...);
__attribute__((visibility("hidden")))
void *__vdsosym(const char *, const char *);
#define syscall(...) __syscall_ret(__syscall(__VA_ARGS__))
#define socketcall(...) __syscall_ret(__socketcall(__VA_ARGS__))
#define __socketcall(nm,a,b,c,d,e,f) __syscall(SYS_##nm, a, b, c, d, e, f)
#define socketcall_cp socketcall

View File

@ -40,24 +40,10 @@ static int checkver(Verdef *def, int vsym, const char *vername, char *strings)
#define OK_TYPES (1<<STT_NOTYPE | 1<<STT_OBJECT | 1<<STT_FUNC | 1<<STT_COMMON)
#define OK_BINDS (1<<STB_GLOBAL | 1<<STB_WEAK | 1<<STB_GNU_UNIQUE)
extern char** environ;
static Ehdr *eh = NULL;
void *__vdsosym(const char *vername, const char *name);
// We don't have libc struct available here. Compute aux vector manually.
__attribute__((constructor)) static void auxv_init()
{
size_t i, *auxv;
for (i=0; environ[i]; i++);
auxv = (void *)(environ+i+1);
for (i=0; auxv[i] != AT_SYSINFO_EHDR; i+=2)
if (!auxv[i]) return;
if (!auxv[i+1]) return;
eh = (void *)auxv[i+1];
}
void *__vdsosym(const char *vername, const char *name)
{
size_t i;
Ehdr * eh = (void *) getauxval(AT_SYSINFO_EHDR);
if (!eh) return 0;
Phdr *ph = (void *)((char *)eh + eh->e_phoff);
size_t *dynv=0, base=-1;

View File

@ -6,11 +6,9 @@ Defines the following variables:
The include directories of the gRPC framework, including the include directories of the C++ wrapper.
``gRPC_LIBRARIES``
The libraries of the gRPC framework.
``gRPC_UNSECURE_LIBRARIES``
The libraries of the gRPC framework without SSL.
``_gRPC_CPP_PLUGIN``
``gRPC_CPP_PLUGIN``
The plugin for generating gRPC client and server C++ stubs from `.proto` files
``_gRPC_PYTHON_PLUGIN``
``gRPC_PYTHON_PLUGIN``
The plugin for generating gRPC client and server Python stubs from `.proto` files
The following :prop_tgt:`IMPORTED` targets are also defined:
@ -19,6 +17,13 @@ The following :prop_tgt:`IMPORTED` targets are also defined:
``grpc_cpp_plugin``
``grpc_python_plugin``
Set the following variables to adjust the behaviour of this script:
``gRPC_USE_UNSECURE_LIBRARIES``
if set gRPC_LIBRARIES will be filled with the unsecure version of the libraries (i.e. without SSL)
instead of the secure ones.
``gRPC_DEBUG`
if set the debug message will be printed.
Add custom commands to process ``.proto`` files to C++::
protobuf_generate_grpc_cpp(<SRCS> <HDRS>
[DESCRIPTORS <DESC>] [EXPORT_MACRO <MACRO>] [<ARGN>...])
@ -242,6 +247,7 @@ find_library(gRPC_LIBRARY NAMES grpc)
find_library(gRPC_CPP_LIBRARY NAMES grpc++)
find_library(gRPC_UNSECURE_LIBRARY NAMES grpc_unsecure)
find_library(gRPC_CPP_UNSECURE_LIBRARY NAMES grpc++_unsecure)
find_library(gRPC_CARES_LIBRARY NAMES cares)
set(gRPC_LIBRARIES)
if(gRPC_USE_UNSECURE_LIBRARIES)
@ -259,6 +265,7 @@ else()
set(gRPC_LIBRARIES ${gRPC_LIBRARIES} ${gRPC_CPP_LIBRARY})
endif()
endif()
set(gRPC_LIBRARIES ${gRPC_LIBRARIES} ${gRPC_CARES_LIBRARY})
# Restore the original find library ordering.
if(gRPC_USE_STATIC_LIBS)
@ -278,11 +285,11 @@ else()
endif()
# Get full path to plugin.
find_program(_gRPC_CPP_PLUGIN
find_program(gRPC_CPP_PLUGIN
NAMES grpc_cpp_plugin
DOC "The plugin for generating gRPC client and server C++ stubs from `.proto` files")
find_program(_gRPC_PYTHON_PLUGIN
find_program(gRPC_PYTHON_PLUGIN
NAMES grpc_python_plugin
DOC "The plugin for generating gRPC client and server Python stubs from `.proto` files")
@ -317,14 +324,14 @@ endif()
#include(FindPackageHandleStandardArgs.cmake)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(gRPC
REQUIRED_VARS gRPC_LIBRARY gRPC_CPP_LIBRARY gRPC_UNSECURE_LIBRARY gRPC_CPP_UNSECURE_LIBRARY
gRPC_INCLUDE_DIR gRPC_CPP_INCLUDE_DIR _gRPC_CPP_PLUGIN _gRPC_PYTHON_PLUGIN)
REQUIRED_VARS gRPC_LIBRARY gRPC_CPP_LIBRARY gRPC_UNSECURE_LIBRARY gRPC_CPP_UNSECURE_LIBRARY gRPC_CARES_LIBRARY
gRPC_INCLUDE_DIR gRPC_CPP_INCLUDE_DIR gRPC_CPP_PLUGIN gRPC_PYTHON_PLUGIN)
if(gRPC_FOUND)
if(gRPC_DEBUG)
message(STATUS "gRPC: INCLUDE_DIRS=${gRPC_INCLUDE_DIRS}")
message(STATUS "gRPC: LIBRARIES=${gRPC_LIBRARIES}")
message(STATUS "gRPC: CPP_PLUGIN=${_gRPC_CPP_PLUGIN}")
message(STATUS "gRPC: PYTHON_PLUGIN=${_gRPC_PYTHON_PLUGIN}")
message(STATUS "gRPC: CPP_PLUGIN=${gRPC_CPP_PLUGIN}")
message(STATUS "gRPC: PYTHON_PLUGIN=${gRPC_PYTHON_PLUGIN}")
endif()
endif()

View File

@ -37,8 +37,8 @@ if(NOT USE_INTERNAL_GRPC_LIBRARY)
if(NOT gRPC_INCLUDE_DIRS OR NOT gRPC_LIBRARIES)
message(${RECONFIGURE_MESSAGE_LEVEL} "Can't find system gRPC library")
set(EXTERNAL_GRPC_LIBRARY_FOUND 0)
elseif(NOT _gRPC_CPP_PLUGIN)
message(${RECONFIGURE_MESSAGE_LEVEL} "Can't find system grcp_cpp_plugin")
elseif(NOT gRPC_CPP_PLUGIN)
message(${RECONFIGURE_MESSAGE_LEVEL} "Can't find system grpc_cpp_plugin")
set(EXTERNAL_GRPC_LIBRARY_FOUND 0)
else()
set(EXTERNAL_GRPC_LIBRARY_FOUND 1)
@ -53,8 +53,8 @@ if(NOT EXTERNAL_GRPC_LIBRARY_FOUND AND NOT MISSING_INTERNAL_GRPC_LIBRARY)
else()
set(gRPC_LIBRARIES grpc grpc++)
endif()
set(_gRPC_CPP_PLUGIN $<TARGET_FILE:grpc_cpp_plugin>)
set(_gRPC_PROTOC_EXECUTABLE $<TARGET_FILE:protobuf::protoc>)
set(gRPC_CPP_PLUGIN $<TARGET_FILE:grpc_cpp_plugin>)
set(gRPC_PYTHON_PLUGIN $<TARGET_FILE:grpc_python_plugin>)
include("${ClickHouse_SOURCE_DIR}/contrib/grpc-cmake/protobuf_generate_grpc.cmake")
@ -62,4 +62,4 @@ if(NOT EXTERNAL_GRPC_LIBRARY_FOUND AND NOT MISSING_INTERNAL_GRPC_LIBRARY)
set(USE_GRPC 1)
endif()
message(STATUS "Using gRPC=${USE_GRPC}: ${gRPC_INCLUDE_DIRS} : ${gRPC_LIBRARIES} : ${_gRPC_CPP_PLUGIN}")
message(STATUS "Using gRPC=${USE_GRPC}: ${gRPC_INCLUDE_DIRS} : ${gRPC_LIBRARIES} : ${gRPC_CPP_PLUGIN}")

1
contrib/abseil-cpp vendored Submodule

@ -0,0 +1 @@
Subproject commit 4f3b686f86c3ebaba7e4e926e62a79cb1c659a54

2
contrib/grpc vendored

@ -1 +1 @@
Subproject commit a6570b863cf76c9699580ba51c7827d5bffaac43
Subproject commit 7436366ceb341ba5c00ea29f1645e02a2b70bf93

View File

@ -1,6 +1,7 @@
set(_gRPC_SOURCE_DIR "${ClickHouse_SOURCE_DIR}/contrib/grpc")
set(_gRPC_BINARY_DIR "${ClickHouse_BINARY_DIR}/contrib/grpc")
# Use re2 from ClickHouse contrib, not from gRPC third_party.
if(NOT RE2_INCLUDE_DIR)
message(FATAL_ERROR " grpc: The location of the \"re2\" library is unknown")
endif()
@ -8,6 +9,7 @@ set(gRPC_RE2_PROVIDER "clickhouse" CACHE STRING "" FORCE)
set(_gRPC_RE2_INCLUDE_DIR "${RE2_INCLUDE_DIR}")
set(_gRPC_RE2_LIBRARIES "${RE2_LIBRARY}")
# Use zlib from ClickHouse contrib, not from gRPC third_party.
if(NOT ZLIB_INCLUDE_DIRS)
message(FATAL_ERROR " grpc: The location of the \"zlib\" library is unknown")
endif()
@ -15,6 +17,7 @@ set(gRPC_ZLIB_PROVIDER "clickhouse" CACHE STRING "" FORCE)
set(_gRPC_ZLIB_INCLUDE_DIR "${ZLIB_INCLUDE_DIRS}")
set(_gRPC_ZLIB_LIBRARIES "${ZLIB_LIBRARIES}")
# Use protobuf from ClickHouse contrib, not from gRPC third_party.
if(NOT Protobuf_INCLUDE_DIR OR NOT Protobuf_LIBRARY)
message(FATAL_ERROR " grpc: The location of the \"protobuf\" library is unknown")
elseif (NOT Protobuf_PROTOC_EXECUTABLE)
@ -29,21 +32,33 @@ set(_gRPC_PROTOBUF_PROTOC "protoc")
set(_gRPC_PROTOBUF_PROTOC_EXECUTABLE "${Protobuf_PROTOC_EXECUTABLE}")
set(_gRPC_PROTOBUF_PROTOC_LIBRARIES "${Protobuf_PROTOC_LIBRARY}")
# Use OpenSSL from ClickHouse contrib, not from gRPC third_party.
set(gRPC_SSL_PROVIDER "clickhouse" CACHE STRING "" FORCE)
set(_gRPC_SSL_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR})
set(_gRPC_SSL_LIBRARIES ${OPENSSL_LIBRARIES})
# Use abseil-cpp from ClickHouse contrib, not from gRPC third_party.
set(gRPC_ABSL_PROVIDER "clickhouse" CACHE STRING "" FORCE)
set(ABSL_ROOT_DIR "${ClickHouse_SOURCE_DIR}/contrib/abseil-cpp")
if(NOT EXISTS "${ABSL_ROOT_DIR}/CMakeLists.txt")
message(FATAL_ERROR " grpc: submodule third_party/abseil-cpp is missing. To fix try run: \n git submodule update --init --recursive")
endif()
add_subdirectory("${ABSL_ROOT_DIR}" "${ClickHouse_BINARY_DIR}/contrib/abseil-cpp")
# Choose to build static or shared library for c-ares.
if (MAKE_STATIC_LIBRARIES)
set(CARES_STATIC ON CACHE BOOL "" FORCE)
set(CARES_SHARED OFF CACHE BOOL "" FORCE)
else ()
set(CARES_STATIC OFF CACHE BOOL "" FORCE)
set(CARES_SHARED ON CACHE BOOL "" FORCE)
endif ()
# We don't want to build C# extensions.
set(gRPC_BUILD_CSHARP_EXT OFF)
# We don't want to build abseil tests, so we temporarily switch BUILD_TESTING off.
set(_gRPC_ORIG_BUILD_TESTING ${BUILD_TESTING})
set(BUILD_TESTING OFF)
add_subdirectory("${_gRPC_SOURCE_DIR}" "${_gRPC_BINARY_DIR}")
set(BUILD_TESTING ${_gRPC_ORIG_BUILD_TESTING})
# The contrib/grpc/CMakeLists.txt redefined the PROTOBUF_GENERATE_GRPC_CPP() function for its own purposes,
# so we need to redefine it back.
include("${ClickHouse_SOURCE_DIR}/contrib/grpc-cmake/protobuf_generate_grpc.cmake")

2
contrib/protobuf vendored

@ -1 +1 @@
Subproject commit 445d1ae73a450b1e94622e7040989aa2048402e3
Subproject commit 73b12814204ad9068ba352914d0dc244648b48ee

View File

@ -56,6 +56,7 @@ RUN apt-get update \
libprotoc-dev \
libgrpc++-dev \
protobuf-compiler-grpc \
libc-ares-dev \
rapidjson-dev \
libsnappy-dev \
libparquet-dev \

View File

@ -10,6 +10,11 @@ RUN apt-get update --yes \
gpg-agent \
debsig-verify \
strace \
protobuf-compiler \
protobuf-compiler-grpc \
libprotoc-dev \
libgrpc++-dev \
libc-ares-dev \
--yes --no-install-recommends
#RUN wget -nv -O - http://files.viva64.com/etc/pubkey.txt | sudo apt-key add -
@ -33,7 +38,8 @@ RUN set -x \
&& dpkg -i "${PKG_VERSION}.deb"
CMD echo "Running PVS version $PKG_VERSION" && cd /repo_folder && pvs-studio-analyzer credentials $LICENCE_NAME $LICENCE_KEY -o ./licence.lic \
&& cmake . -D"ENABLE_EMBEDDED_COMPILER"=OFF && ninja re2_st \
&& cmake . -D"ENABLE_EMBEDDED_COMPILER"=OFF -D"USE_INTERNAL_PROTOBUF_LIBRARY"=OFF -D"USE_INTERNAL_GRPC_LIBRARY"=OFF \
&& ninja re2_st clickhouse_grpc_protos \
&& pvs-studio-analyzer analyze -o pvs-studio.log -e contrib -j 4 -l ./licence.lic; \
plog-converter -a GA:1,2 -t fullhtml -o /test_output/pvs-studio-html-report pvs-studio.log; \
plog-converter -a GA:1,2 -t tasklist -o /test_output/pvs-studio-task-report.txt pvs-studio.log

View File

@ -112,6 +112,8 @@ add_subdirectory (obfuscator)
add_subdirectory (install)
add_subdirectory (git-import)
#add_subdirectory (grpc-client)
if (ENABLE_CLICKHOUSE_ODBC_BRIDGE)
add_subdirectory (odbc-bridge)
endif ()

View File

@ -0,0 +1,7 @@
include_directories(${CMAKE_CURRENT_BINARY_DIR})
get_filename_component(rpc_proto "${CMAKE_CURRENT_SOURCE_DIR}/../server/grpc_protos/GrpcConnection.proto" ABSOLUTE)
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${rpc_proto})
PROTOBUF_GENERATE_GRPC_CPP(GRPC_SRCS GRPC_HDRS ${rpc_proto})
add_executable(grpc-client grpc_client.cpp ${PROTO_SRCS} ${PROTO_HDRS} ${GRPC_SRCS} ${GRPC_HDRS})
target_link_libraries(grpc-client PUBLIC grpc++ PUBLIC libprotobuf PUBLIC daemon)

View File

@ -0,0 +1,173 @@
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <thread>
#include <stdlib.h>
#include <grpc++/channel.h>
#include <grpc++/client_context.h>
#include <grpc++/create_channel.h>
#include <grpc++/security/credentials.h>
#include "GrpcConnection.grpc.pb.h"
class GRPCClient
{
public:
explicit GRPCClient(std::shared_ptr<grpc::Channel> channel)
: stub_(GRPCConnection::GRPC::NewStub(channel))
{}
std::string Query(const GRPCConnection::User& userInfo,
const std::string& query,
std::vector<std::string> insert_data = {})
{
GRPCConnection::QueryRequest request;
grpc::Status status;
GRPCConnection::QueryResponse reply;
grpc::ClientContext context;
auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(10000);
context.set_deadline(deadline);
auto user = std::make_unique<GRPCConnection::User>(userInfo);
auto querySettigs = std::make_unique<GRPCConnection::QuerySettings>();
int id = rand();
request.set_allocated_user_info(user.release());
// interactive_delay in miliseconds
request.set_interactive_delay(1000);
querySettigs->set_query(query);
querySettigs->set_format("Values");
querySettigs->set_query_id(std::to_string(id));
querySettigs->set_data_stream((insert_data.size() != 0));
(*querySettigs->mutable_settings())["max_query_size"] ="100";
request.set_allocated_query_info(querySettigs.release());
void* got_tag = (void*)1;
bool ok = false;
std::unique_ptr<grpc::ClientReaderWriter<GRPCConnection::QueryRequest, GRPCConnection::QueryResponse> > reader(stub_->Query(&context));
reader->Write(request);
auto write = [&reply, &reader, &insert_data]()
{
GRPCConnection::QueryRequest request_insert;
for (const auto& data : insert_data)
{
request_insert.set_insert_data(data);
if (reply.exception_occured().empty())
{
reader->Write(request_insert);
}
else
{
break;
}
}
request_insert.set_insert_data("");
if (reply.exception_occured().empty())
{
reader->Write(request_insert);
}
// reader->WritesDone();
};
std::thread write_thread(write);
write_thread.detach();
while (reader->Read(&reply))
{
if (!reply.output().empty())
{
std::cout << "Query Part:\n " << id<< reply.output()<<'\n';
}
else if (reply.progress().read_rows()
|| reply.progress().read_bytes()
|| reply.progress().total_rows_to_read()
|| reply.progress().written_rows()
|| reply.progress().written_bytes())
{
std::cout << "Progress " << id<< ":{\n" << "read_rows: " << reply.progress().read_rows() << '\n'
<< "read_bytes: " << reply.progress().read_bytes() << '\n'
<< "total_rows_to_read: " << reply.progress().total_rows_to_read() << '\n'
<< "written_rows: " << reply.progress().written_rows() << '\n'
<< "written_bytes: " << reply.progress().written_bytes() << '\n';
}
else if (!reply.totals().empty())
{
std::cout << "Totals:\n " << id << " " << reply.totals() <<'\n';
}
else if (!reply.extremes().empty())
{
std::cout << "Extremes:\n " << id << " " << reply.extremes() <<'\n';
}
}
if (status.ok() && reply.exception_occured().empty())
{
return "";
}
else if (status.ok() && !reply.exception_occured().empty())
{
return reply.exception_occured();
}
else
{
return "RPC failed";
}
}
private:
std::unique_ptr<GRPCConnection::GRPC::Stub> stub_;
};
int main(int argc, char** argv)
{
GRPCConnection::User userInfo1;
userInfo1.set_user("default");
userInfo1.set_password("");
userInfo1.set_quota("default");
std::cout << "Try: " << argv[1] << std::endl;
grpc::ChannelArguments ch_args;
ch_args.SetMaxReceiveMessageSize(-1);
GRPCClient client(
grpc::CreateCustomChannel(argv[1], grpc::InsecureChannelCredentials(), ch_args));
{
std::cout << client.Query(userInfo1, "CREATE TABLE t (a UInt8) ENGINE = Memory") << std::endl;
std::cout << client.Query(userInfo1, "CREATE TABLE t (a UInt8) ENGINE = Memory") << std::endl;
std::cout << client.Query(userInfo1, "INSERT INTO t VALUES", {"(1),(2),(3)", "(4),(6),(5)"}) << std::endl;
std::cout << client.Query(userInfo1, "INSERT INTO t_not_defined VALUES", {"(1),(2),(3)", "(4),(6),(5)"}) << std::endl;
std::cout << client.Query(userInfo1, "SELECT a FROM t ORDER BY a") << std::endl;
std::cout << client.Query(userInfo1, "DROP TABLE t") << std::endl;
}
{
std::cout << client.Query(userInfo1, "SELECT count() FROM numbers(1)") << std::endl;
std::cout << client.Query(userInfo1, "SELECT 100") << std::endl;
std::cout << client.Query(userInfo1, "SELECT count() FROM numbers(10000000000)") << std::endl;
std::cout << client.Query(userInfo1, "SELECT count() FROM numbers(100)") << std::endl;
}
{
std::cout << client.Query(userInfo1, "CREATE TABLE arrays_test (s String, arr Array(UInt8)) ENGINE = Memory;") << std::endl;
std::cout << client.Query(userInfo1, "INSERT INTO arrays_test VALUES ('Hello', [1,2]), ('World', [3,4,5]), ('Goodbye', []);") << std::endl;
std::cout << client.Query(userInfo1, "SELECT s FROM arrays_test") << std::endl;
std::cout << client.Query(userInfo1, "DROP TABLE arrays_test") << std::endl;
std::cout << client.Query(userInfo1, "") << std::endl;
}
{//Check null return from pipe
std::cout << client.Query(userInfo1, "CREATE TABLE table2 (x UInt8, y UInt8) ENGINE = Memory;") << std::endl;
std::cout << client.Query(userInfo1, "SELECT x FROM table2") << std::endl;
std::cout << client.Query(userInfo1, "DROP TABLE table2") << std::endl;
}
{//Check Totals
std::cout << client.Query(userInfo1, "CREATE TABLE tabl (x UInt8, y UInt8) ENGINE = Memory;") << std::endl;
std::cout << client.Query(userInfo1, "INSERT INTO tabl VALUES (1, 2), (2, 4), (3, 2), (3, 3), (3, 4);") << std::endl;
std::cout << client.Query(userInfo1, "SELECT sum(x), y FROM tabl GROUP BY y WITH TOTALS") << std::endl;
std::cout << client.Query(userInfo1, "DROP TABLE tabl") << std::endl;
}
return 0;
}

View File

@ -109,6 +109,14 @@ void ODBCBridge::defineOptions(Poco::Util::OptionSet & options)
.argument("err-log-path")
.binding("logger.errorlog"));
options.addOption(Poco::Util::Option("stdout-path", "", "stdout log path, default console")
.argument("stdout-path")
.binding("logger.stdout"));
options.addOption(Poco::Util::Option("stderr-path", "", "stderr log path, default console")
.argument("stderr-path")
.binding("logger.stderr"));
using Me = std::decay_t<decltype(*this)>;
options.addOption(Poco::Util::Option("help", "", "produce this help message")
.binding("help")
@ -127,6 +135,27 @@ void ODBCBridge::initialize(Application & self)
config().setString("logger", "ODBCBridge");
/// Redirect stdout, stderr to specified files.
/// Some libraries and sanitizers write to stderr in case of errors.
const auto stdout_path = config().getString("logger.stdout", "");
if (!stdout_path.empty())
{
if (!freopen(stdout_path.c_str(), "a+", stdout))
throw Poco::OpenFileException("Cannot attach stdout to " + stdout_path);
/// Disable buffering for stdout.
setbuf(stdout, nullptr);
}
const auto stderr_path = config().getString("logger.stderr", "");
if (!stderr_path.empty())
{
if (!freopen(stderr_path.c_str(), "a+", stderr))
throw Poco::OpenFileException("Cannot attach stderr to " + stderr_path);
/// Disable buffering for stderr.
setbuf(stderr, nullptr);
}
buildLoggers(config(), logger(), self.commandName());
BaseDaemon::logRevision();

View File

@ -64,6 +64,7 @@
#include <Common/ThreadFuzzer.h>
#include <Server/MySQLHandlerFactory.h>
#include <Server/PostgreSQLHandlerFactory.h>
#include <Server/ProtocolServerAdapter.h>
#if !defined(ARCADIA_BUILD)
@ -84,6 +85,11 @@
# include <Poco/Net/SecureServerSocket.h>
#endif
#if USE_GRPC
# include <Server/GRPCServer.h>
#endif
namespace CurrentMetrics
{
extern const Metric Revision;
@ -806,7 +812,7 @@ int Server::main(const std::vector<std::string> & /*args*/)
http_params->setTimeout(settings.http_receive_timeout);
http_params->setKeepAliveTimeout(keep_alive_timeout);
std::vector<std::unique_ptr<Poco::Net::TCPServer>> servers;
std::vector<ProtocolServerAdapter> servers;
std::vector<std::string> listen_hosts = DB::getMultipleValuesFromConfig(config(), "", "listen_host");
@ -1035,6 +1041,15 @@ int Server::main(const std::vector<std::string> & /*args*/)
LOG_INFO(log, "Listening for PostgreSQL compatibility protocol: " + address.toString());
});
#if USE_GRPC
create_server("grpc_port", [&](UInt16 port)
{
Poco::Net::SocketAddress server_address(listen_host, port);
servers.emplace_back(std::make_unique<GRPCServer>(*this, make_socket_address(listen_host, port)));
LOG_INFO(log, "Listening for gRPC protocol: " + server_address.toString());
});
#endif
/// Prometheus (if defined and not setup yet with http_port)
create_server("prometheus.port", [&](UInt16 port)
{
@ -1056,7 +1071,7 @@ int Server::main(const std::vector<std::string> & /*args*/)
global_context->enableNamedSessions();
for (auto & server : servers)
server->start();
server.start();
{
String level_str = config().getString("text_log.level", "");
@ -1088,8 +1103,8 @@ int Server::main(const std::vector<std::string> & /*args*/)
int current_connections = 0;
for (auto & server : servers)
{
server->stop();
current_connections += server->currentConnections();
server.stop();
current_connections += server.currentConnections();
}
if (current_connections)
@ -1109,7 +1124,7 @@ int Server::main(const std::vector<std::string> & /*args*/)
{
current_connections = 0;
for (auto & server : servers)
current_connections += server->currentConnections();
current_connections += server.currentConnections();
if (!current_connections)
break;
sleep_current_ms += sleep_one_ms;

View File

@ -134,6 +134,34 @@
<max_connections>4096</max_connections>
<keep_alive_timeout>3</keep_alive_timeout>
<!-- gRPC protocol (see src/Server/grpc_protos/clickhouse_grpc.proto for the API)
<grpc_port>9001</grpc_port>
<grpc>
<enable_ssl>true</enable_ssl> -->
<!-- The following two files are used only if enable_ssl=1
<ssl_cert_file>/path/to/ssl_cert_file</ssl_cert_file>
<ssl_key_file>/path/to/ssl_key_file</ssl_key_file> -->
<!-- Whether server will request client for a certificate
<ssl_require_client_auth>true</ssl_require_client_auth> -->
<!-- The following file is used only if ssl_require_client_auth=1
<ssl_ca_cert_file>/path/to/ssl_ca_cert_file</ssl_ca_cert_file> -->
<!-- Default compression algorithm (applied if client doesn't specify another algorithm).
Supported algorithms: none, deflate, gzip, stream_gzip
<compression>gzip</compression> -->
<!-- Default compression level (applied if client doesn't specify another level).
Supported levels: none, low, medium, high
<compression_level>high</compression_level> -->
<!-- Send/receive message size limits in bytes. -1 means unlimited
<max_send_message_size>-1</max_send_message_size>
<max_receive_message_size>4194304</max_receive_message_size>
</grpc> -->
<!-- Maximum number of concurrent queries. -->
<max_concurrent_queries>100</max_concurrent_queries>

View File

@ -382,6 +382,10 @@ if (USE_PROTOBUF)
dbms_target_include_directories (SYSTEM BEFORE PRIVATE ${Protobuf_INCLUDE_DIR})
endif ()
if (USE_GRPC)
dbms_target_link_libraries (PUBLIC clickhouse_grpc_protos)
endif()
if (USE_HDFS)
target_link_libraries (clickhouse_common_io PUBLIC ${HDFS3_LIBRARY})
target_include_directories (clickhouse_common_io SYSTEM BEFORE PUBLIC ${HDFS3_INCLUDE_DIR})

View File

@ -525,6 +525,7 @@
M(556, SYNC_MYSQL_USER_ACCESS_ERROR)\
M(557, UNKNOWN_UNION) \
M(558, EXPECTED_ALL_OR_DISTINCT) \
M(559, INVALID_GRPC_QUERY_INFO) \
\
M(999, KEEPER_EXCEPTION) \
M(1000, POCO_EXCEPTION) \

View File

@ -326,6 +326,16 @@ struct ODBCBridgeMixin
cmd_args.push_back("--err-log-path");
cmd_args.push_back(config.getString("logger." + configPrefix() + "_errlog"));
}
if (config.has("logger." + configPrefix() + "_stdout"))
{
cmd_args.push_back("--stdout-path");
cmd_args.push_back(config.getString("logger." + configPrefix() + "_stdout"));
}
if (config.has("logger." + configPrefix() + "_stderr"))
{
cmd_args.push_back("--stderr-path");
cmd_args.push_back(config.getString("logger." + configPrefix() + "_stderr"));
}
if (config.has("logger." + configPrefix() + "_level"))
{
cmd_args.push_back("--log-level");

View File

@ -0,0 +1,13 @@
#pragma once
// .h autogenerated by cmake!
#define USE_ICU 1
#define USE_MYSQL 1
#define USE_RDKAFKA 1
#define USE_AMQPCPP 1
#define USE_EMBEDDED_COMPILER 0
#define USE_INTERNAL_LLVM_LIBRARY 0
#define USE_SSL 1
#define USE_OPENCL 0
#define USE_LDAP 1

View File

@ -85,6 +85,8 @@ public:
void restart()
{
if (vector.empty())
vector.resize(initial_size);
set(reinterpret_cast<Position>(vector.data()), vector.size());
is_finished = false;
}

View File

@ -25,6 +25,7 @@ public:
{
TCP = 1,
HTTP = 2,
GRPC = 3,
};
enum class HTTPMethod : uint8_t

View File

@ -0,0 +1,3 @@
if (USE_GRPC)
add_subdirectory (grpc_protos)
endif()

1634
src/Server/GRPCServer.cpp Normal file

File diff suppressed because it is too large Load Diff

51
src/Server/GRPCServer.h Normal file
View File

@ -0,0 +1,51 @@
#pragma once
#if !defined(ARCADIA_BUILD)
#include <Common/config.h>
#endif
#if USE_GRPC
#include <Poco/Net/SocketAddress.h>
#include "clickhouse_grpc.grpc.pb.h"
namespace Poco { class Logger; }
namespace grpc
{
class Server;
class ServerCompletionQueue;
}
namespace DB
{
class IServer;
class GRPCServer
{
public:
GRPCServer(IServer & iserver_, const Poco::Net::SocketAddress & address_to_listen_);
~GRPCServer();
/// Starts the server. A new thread will be created that waits for and accepts incoming connections.
void start();
/// Stops the server. No new connections will be accepted.
void stop();
/// Returns the number of currently handled connections.
size_t currentConnections() const;
private:
using GRPCService = clickhouse::grpc::ClickHouse::AsyncService;
class Runner;
IServer & iserver;
const Poco::Net::SocketAddress address_to_listen;
Poco::Logger * log;
GRPCService grpc_service;
std::unique_ptr<grpc::Server> grpc_server;
std::unique_ptr<grpc::ServerCompletionQueue> queue;
std::unique_ptr<Runner> runner;
};
}
#endif

View File

@ -0,0 +1,50 @@
#include <Server/ProtocolServerAdapter.h>
#include <Poco/Net/TCPServer.h>
#if USE_GRPC
#include <Server/GRPCServer.h>
#endif
namespace DB
{
class ProtocolServerAdapter::TCPServerAdapterImpl : public Impl
{
public:
explicit TCPServerAdapterImpl(std::unique_ptr<Poco::Net::TCPServer> tcp_server_) : tcp_server(std::move(tcp_server_)) {}
~TCPServerAdapterImpl() override = default;
void start() override { tcp_server->start(); }
void stop() override { tcp_server->stop(); }
size_t currentConnections() const override { return tcp_server->currentConnections(); }
private:
std::unique_ptr<Poco::Net::TCPServer> tcp_server;
};
ProtocolServerAdapter::ProtocolServerAdapter(std::unique_ptr<Poco::Net::TCPServer> tcp_server_)
{
impl = std::make_unique<TCPServerAdapterImpl>(std::move(tcp_server_));
}
#if USE_GRPC
class ProtocolServerAdapter::GRPCServerAdapterImpl : public Impl
{
public:
explicit GRPCServerAdapterImpl(std::unique_ptr<GRPCServer> grpc_server_) : grpc_server(std::move(grpc_server_)) {}
~GRPCServerAdapterImpl() override = default;
void start() override { grpc_server->start(); }
void stop() override { grpc_server->stop(); }
size_t currentConnections() const override { return grpc_server->currentConnections(); }
private:
std::unique_ptr<GRPCServer> grpc_server;
};
ProtocolServerAdapter::ProtocolServerAdapter(std::unique_ptr<GRPCServer> grpc_server_)
{
impl = std::make_unique<GRPCServerAdapterImpl>(std::move(grpc_server_));
}
#endif
}

View File

@ -0,0 +1,55 @@
#pragma once
#if !defined(ARCADIA_BUILD)
#include <Common/config.h>
#endif
#include <memory>
namespace Poco::Net { class TCPServer; }
namespace DB
{
class GRPCServer;
/// Provides an unified interface to access a protocol implementing server
/// no matter what type it has (HTTPServer, TCPServer, MySQLServer, GRPCServer, ...).
class ProtocolServerAdapter
{
public:
ProtocolServerAdapter() {}
ProtocolServerAdapter(ProtocolServerAdapter && src) = default;
ProtocolServerAdapter & operator =(ProtocolServerAdapter && src) = default;
~ProtocolServerAdapter() {}
ProtocolServerAdapter(std::unique_ptr<Poco::Net::TCPServer> tcp_server_);
#if USE_GRPC
ProtocolServerAdapter(std::unique_ptr<GRPCServer> grpc_server_);
#endif
/// Starts the server. A new thread will be created that waits for and accepts incoming connections.
void start() { impl->start(); }
/// Stops the server. No new connections will be accepted.
void stop() { impl->stop(); }
/// Returns the number of currently handled connections.
size_t currentConnections() const { return impl->currentConnections(); }
private:
class Impl
{
public:
virtual ~Impl() {}
virtual void start() = 0;
virtual void stop() = 0;
virtual size_t currentConnections() const = 0;
};
class TCPServerAdapterImpl;
class GRPCServerAdapterImpl;
std::unique_ptr<Impl> impl;
};
}

View File

@ -0,0 +1,11 @@
PROTOBUF_GENERATE_GRPC_CPP(clickhouse_grpc_proto_sources clickhouse_grpc_proto_headers clickhouse_grpc.proto)
# Ignore warnings while compiling protobuf-generated *.pb.h and *.pb.cpp files.
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -w")
# Disable clang-tidy for protobuf-generated *.pb.h and *.pb.cpp files.
set (CMAKE_CXX_CLANG_TIDY "")
add_library(clickhouse_grpc_protos ${clickhouse_grpc_proto_headers} ${clickhouse_grpc_proto_sources})
target_include_directories(clickhouse_grpc_protos SYSTEM PUBLIC ${gRPC_INCLUDE_DIRS} ${Protobuf_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries (clickhouse_grpc_protos PUBLIC ${gRPC_LIBRARIES})

View File

@ -0,0 +1,151 @@
/* This file describes gRPC protocol supported in ClickHouse.
*
* To use this protocol a client should send one or more messages of the QueryInfo type
* and then receive one or more messages of the Result type.
* According to that the service provides four methods for that:
* ExecuteQuery(QueryInfo) returns (Result)
* ExecuteQueryWithStreamInput(stream QueryInfo) returns (Result)
* ExecuteQueryWithStreamOutput(QueryInfo) returns (stream Result)
* ExecuteQueryWithStreamIO(stream QueryInfo) returns (stream Result)
* It's up to the client to choose which method to use.
* For example, ExecuteQueryWithStreamInput() allows the client to add data multiple times
* while executing a query, which is suitable for inserting many rows.
*/
syntax = "proto3";
package clickhouse.grpc;
message NameAndType {
string name = 1;
string type = 2;
}
// Desribes an external table - a table which will exists only while a query is executing.
message ExternalTable {
// Name of the table. If omitted, "_data" is used.
string name = 1;
// Columns of the table. Types are required, names can be omitted. If the names are omitted, "_1", "_2", ... is used.
repeated NameAndType columns = 2;
// Data to insert to the external table.
// If a method with streaming input (i.e. ExecuteQueryWithStreamInput() or ExecuteQueryWithStreamIO()) is used,
// then data for insertion to the same external table can be splitted between multiple QueryInfos.
string data = 3;
// Format of the data to insert to the external table.
string format = 4;
// Settings for executing that insertion, applied after QueryInfo.settings.
map<string, string> settings = 5;
}
// Information about a query which a client sends to a ClickHouse server.
// The first QueryInfo can set any of the following fields. Extra QueryInfos only add extra data.
// In extra QueryInfos only `input_data`, `external_tables`, `next_query_info` and `cancel` fields can be set.
message QueryInfo {
string query = 1;
string query_id = 2;
map<string, string> settings = 3;
// Default database.
string database = 4;
// Input data, used both as data for INSERT query and as data for the input() function.
string input_data = 5;
// Delimiter for input_data, inserted between input_data from adjacent QueryInfos.
string input_data_delimiter = 6;
// Default output format. If not specified, 'TabSeparated' is used.
string output_format = 7;
repeated ExternalTable external_tables = 8;
string user_name = 9;
string password = 10;
string quota = 11;
// Works exactly like sessions in the HTTP protocol.
string session_id = 12;
bool session_check = 13;
uint32 session_timeout = 14;
// Set `cancel` to true to stop executing the query.
bool cancel = 15;
// If true there will be at least one more QueryInfo in the input stream.
// `next_query_info` is allowed to be set only if a method with streaming input (i.e. ExecuteQueryWithStreamInput() or ExecuteQueryWithStreamIO()) is used.
bool next_query_info = 16;
}
enum LogsLevel {
LOG_NONE = 0;
LOG_FATAL = 1;
LOG_CRITICAL = 2;
LOG_ERROR = 3;
LOG_WARNING = 4;
LOG_NOTICE = 5;
LOG_INFORMATION = 6;
LOG_DEBUG = 7;
LOG_TRACE = 8;
}
message LogEntry {
uint32 time = 1;
uint32 time_microseconds = 2;
uint64 thread_id = 3;
string query_id = 4;
LogsLevel level = 5;
string source = 6;
string text = 7;
}
message Progress {
uint64 read_rows = 1;
uint64 read_bytes = 2;
uint64 total_rows_to_read = 3;
uint64 written_rows = 4;
uint64 written_bytes = 5;
}
message Stats {
uint64 rows = 1;
uint64 blocks = 2;
uint64 allocated_bytes = 3;
bool applied_limit = 4;
uint64 rows_before_limit = 5;
}
message Exception {
int32 code = 1;
string name = 2;
string display_text = 3;
string stack_trace = 4;
}
// Result of execution of a query which is sent back by the ClickHouse server to the client.
message Result {
// Output of the query, represented in the `output_format` or in a format specified in `query`.
string output = 1;
string totals = 2;
string extremes = 3;
repeated LogEntry logs = 4;
Progress progress = 5;
Stats stats = 6;
// Set by the ClickHouse server if there was an exception thrown while executing.
Exception exception = 7;
// Set by the ClickHouse server if executing was cancelled by the `cancel` field in QueryInfo.
bool cancelled = 8;
}
service ClickHouse {
rpc ExecuteQuery(QueryInfo) returns (Result) {}
rpc ExecuteQueryWithStreamInput(stream QueryInfo) returns (Result) {}
rpc ExecuteQueryWithStreamOutput(QueryInfo) returns (stream Result) {}
rpc ExecuteQueryWithStreamIO(stream QueryInfo) returns (stream Result) {}
}

View File

@ -10,6 +10,7 @@ PEERDIR(
SRCS(
GRPCServer.cpp
HTTPHandler.cpp
HTTPHandlerFactory.cpp
InterserverIOHTTPHandler.cpp
@ -20,6 +21,7 @@ SRCS(
PostgreSQLHandlerFactory.cpp
PrometheusMetricsWriter.cpp
PrometheusRequestHandler.cpp
ProtocolServerAdapter.cpp
ReplicasStatusHandler.cpp
StaticRequestHandler.cpp
TCPHandler.cpp

View File

@ -907,6 +907,10 @@ class ClickHouseInstance:
build_opts = self.query("SELECT value FROM system.build_options WHERE name = 'CXX_FLAGS'")
return "-fsanitize=thread" in build_opts
def is_built_with_address_sanitizer(self):
build_opts = self.query("SELECT value FROM system.build_options WHERE name = 'CXX_FLAGS'")
return "-fsanitize=address" in build_opts
# Connects to the instance via clickhouse-client, sends a query (1st argument) and returns the answer
def query(self, sql, stdin=None, timeout=None, settings=None, user=None, password=None, database=None,
ignore_error=False):

View File

@ -16,6 +16,7 @@ CONTAINER_NAME = "clickhouse_integration_tests"
CONFIG_DIR_IN_REPO = "programs/server"
INTERGATION_DIR_IN_REPO = "tests/integration"
SRC_DIR_IN_REPO = "src"
DIND_INTEGRATION_TESTS_IMAGE_NAME = "yandex/clickhouse-integration-tests-runner"
@ -51,6 +52,13 @@ def check_args_and_update_paths(args):
args.cases_dir = os.path.abspath(os.path.join(CLICKHOUSE_ROOT, INTERGATION_DIR_IN_REPO))
logging.info("Cases dir is not set. Will use {}".format(args.cases_dir))
if args.src_dir:
if not os.path.isabs(args.src_dir):
args.src_dir = os.path.abspath(os.path.join(CURRENT_WORK_DIR, args.src_dir))
else:
args.src_dir = os.path.abspath(os.path.join(CLICKHOUSE_ROOT, SRC_DIR_IN_REPO))
logging.info("src dir is not set. Will use {}".format(args.src_dir))
logging.info("base_configs_dir: {}, binary: {}, cases_dir: {} ".format(args.base_configs_dir, args.binary, args.cases_dir))
for path in [args.binary, args.bridge_binary, args.base_configs_dir, args.cases_dir, CLICKHOUSE_ROOT]:
@ -104,6 +112,11 @@ if __name__ == "__main__":
default=os.environ.get("CLICKHOUSE_TESTS_INTEGRATION_PATH"),
help="Path to integration tests cases and configs directory. For example tests/integration in repository")
parser.add_argument(
"--src-dir",
default=os.environ.get("CLICKHOUSE_SRC_DIR"),
help="Path to the 'src' directory in repository. Used to provide schemas (e.g. *.proto) for some tests when those schemas are located in the 'src' directory")
parser.add_argument(
"--clickhouse-root",
help="Path to repository root folder. Used to take configuration from repository default paths.")
@ -174,6 +187,7 @@ if __name__ == "__main__":
cmd = "docker run {net} {tty} --rm --name {name} --privileged --volume={bridge_bin}:/clickhouse-odbc-bridge --volume={bin}:/clickhouse \
--volume={base_cfg}:/clickhouse-config --volume={cases_dir}:/ClickHouse/tests/integration \
--volume={src_dir}/Server/grpc_protos:/ClickHouse/src/Server/grpc_protos \
--volume={name}_volume:/var/lib/docker {env_tags} -e PYTEST_OPTS='{opts}' {img} {command}".format(
net=net,
tty=tty,
@ -181,6 +195,7 @@ if __name__ == "__main__":
bridge_bin=args.bridge_binary,
base_cfg=args.base_configs_dir,
cases_dir=args.cases_dir,
src_dir=args.src_dir,
env_tags=env_tags,
opts=' '.join(args.pytest_args),
img=DIND_INTEGRATION_TESTS_IMAGE_NAME + ":" + args.docker_image_version,

View File

@ -0,0 +1 @@
_gen

View File

@ -0,0 +1,3 @@
<yandex>
<grpc_port>9001</grpc_port>
</yandex>

View File

@ -0,0 +1 @@
../../../../src/Server/grpc_protos/clickhouse_grpc.proto

View File

@ -0,0 +1,350 @@
import os
import pytest
import subprocess
import sys
import time
import grpc
from helpers.cluster import ClickHouseCluster
from threading import Thread
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
# Use grpcio-tools to generate *pb2.py files from *.proto.
proto_dir = os.path.join(SCRIPT_DIR, './protos')
gen_dir = os.path.join(SCRIPT_DIR, './_gen')
os.makedirs(gen_dir, exist_ok=True)
subprocess.check_call(
'python3 -m grpc_tools.protoc -I{proto_dir} --python_out={gen_dir} --grpc_python_out={gen_dir} \
{proto_dir}/clickhouse_grpc.proto'.format(proto_dir=proto_dir, gen_dir=gen_dir), shell=True)
sys.path.append(gen_dir)
import clickhouse_grpc_pb2
import clickhouse_grpc_pb2_grpc
# Utilities
config_dir = os.path.join(SCRIPT_DIR, './configs')
cluster = ClickHouseCluster(__file__)
node = cluster.add_instance('node', main_configs=['configs/grpc_port.xml'])
grpc_port = 9001
main_channel = None
def create_channel():
node_ip_with_grpc_port = cluster.get_instance_ip('node') + ':' + str(grpc_port)
channel = grpc.insecure_channel(node_ip_with_grpc_port)
grpc.channel_ready_future(channel).result(timeout=10)
global main_channel
if not main_channel:
main_channel = channel
return channel
def query_common(query_text, settings={}, input_data=[], input_data_delimiter='', output_format='TabSeparated', external_tables=[],
user_name='', password='', query_id='123', session_id='', stream_output=False, channel=None):
if type(input_data) == str:
input_data = [input_data]
if not channel:
channel = main_channel
stub = clickhouse_grpc_pb2_grpc.ClickHouseStub(channel)
def query_info():
input_data_part = input_data.pop(0) if input_data else ''
return clickhouse_grpc_pb2.QueryInfo(query=query_text, settings=settings, input_data=input_data_part, input_data_delimiter=input_data_delimiter,
output_format=output_format, external_tables=external_tables, user_name=user_name, password=password,
query_id=query_id, session_id=session_id, next_query_info=bool(input_data))
def send_query_info():
yield query_info()
while input_data:
input_data_part = input_data.pop(0)
yield clickhouse_grpc_pb2.QueryInfo(input_data=input_data_part, next_query_info=bool(input_data))
stream_input = len(input_data) > 1
if stream_input and stream_output:
return list(stub.ExecuteQueryWithStreamIO(send_query_info()))
elif stream_input:
return [stub.ExecuteQueryWithStreamInput(send_query_info())]
elif stream_output:
return list(stub.ExecuteQueryWithStreamOutput(query_info()))
else:
return [stub.ExecuteQuery(query_info())]
def query_no_errors(*args, **kwargs):
results = query_common(*args, **kwargs)
if results and results[-1].HasField('exception'):
raise Exception(results[-1].exception.display_text)
return results
def query(*args, **kwargs):
output = ""
for result in query_no_errors(*args, **kwargs):
output += result.output
return output
def query_and_get_error(*args, **kwargs):
results = query_common(*args, **kwargs)
if not results or not results[-1].HasField('exception'):
raise Exception("Expected to be failed but succeeded!")
return results[-1].exception
def query_and_get_totals(*args, **kwargs):
totals = ""
for result in query_no_errors(*args, **kwargs):
totals += result.totals
return totals
def query_and_get_extremes(*args, **kwargs):
extremes = ""
for result in query_no_errors(*args, **kwargs):
extremes += result.extremes
return extremes
def query_and_get_logs(*args, **kwargs):
logs = ""
for result in query_no_errors(*args, **kwargs):
for log_entry in result.logs:
#print(log_entry)
logs += log_entry.text + "\n"
return logs
class QueryThread(Thread):
def __init__(self, query_text, expected_output, query_id, use_separate_channel=False):
Thread.__init__(self)
self.query_text = query_text
self.expected_output = expected_output
self.use_separate_channel = use_separate_channel
self.query_id = query_id
def run(self):
if self.use_separate_channel:
with create_channel() as channel:
assert query(self.query_text, query_id=self.query_id, channel=channel) == self.expected_output
else:
assert query(self.query_text, query_id=self.query_id) == self.expected_output
@pytest.fixture(scope="module", autouse=True)
def start_cluster():
cluster.start()
try:
with create_channel() as channel:
yield cluster
finally:
cluster.shutdown()
@pytest.fixture(autouse=True)
def reset_after_test():
yield
query("DROP TABLE IF EXISTS t")
# Actual tests
def test_select_one():
assert query("SELECT 1") == "1\n"
def test_ordinary_query():
assert query("SELECT count() FROM numbers(100)") == "100\n"
def test_insert_query():
query("CREATE TABLE t (a UInt8) ENGINE = Memory")
query("INSERT INTO t VALUES (1),(2),(3)")
query("INSERT INTO t FORMAT TabSeparated 4\n5\n6\n")
query("INSERT INTO t VALUES", input_data="(7),(8)")
query("INSERT INTO t FORMAT TabSeparated", input_data="9\n10\n")
assert query("SELECT a FROM t ORDER BY a") == "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n"
def test_insert_query_streaming():
query("CREATE TABLE t (a UInt8) ENGINE = Memory")
query("INSERT INTO t VALUES", input_data=["(1),(2),(3),", "(5),(4),(6),", "(7),(8),(9)"])
assert query("SELECT a FROM t ORDER BY a") == "1\n2\n3\n4\n5\n6\n7\n8\n9\n"
def test_insert_query_delimiter():
query("CREATE TABLE t (a UInt8) ENGINE = Memory")
query("INSERT INTO t FORMAT CSV 1\n2", input_data=["3", "4\n5"], input_data_delimiter='\n')
assert query("SELECT a FROM t ORDER BY a") == "1\n2\n3\n4\n5\n"
query("DROP TABLE t")
query("CREATE TABLE t (a UInt8) ENGINE = Memory")
query("INSERT INTO t FORMAT CSV 1\n2", input_data=["3", "4\n5"])
assert query("SELECT a FROM t ORDER BY a") == "1\n5\n234\n"
def test_insert_default_column():
query("CREATE TABLE t (a UInt8, b Int32 DEFAULT 100, c String DEFAULT 'c') ENGINE = Memory")
query("INSERT INTO t (c, a) VALUES ('x',1),('y',2)")
query("INSERT INTO t (a) FORMAT TabSeparated", input_data="3\n4\n")
assert query("SELECT * FROM t ORDER BY a") == "1\t100\tx\n" \
"2\t100\ty\n" \
"3\t100\tc\n" \
"4\t100\tc\n"
def test_insert_splitted_row():
query("CREATE TABLE t (a UInt8) ENGINE = Memory")
query("INSERT INTO t VALUES", input_data=["(1),(2),(", "3),(5),(4),(6)"])
assert query("SELECT a FROM t ORDER BY a") == "1\n2\n3\n4\n5\n6\n"
def test_output_format():
query("CREATE TABLE t (a UInt8) ENGINE = Memory")
query("INSERT INTO t VALUES (1),(2),(3)")
assert query("SELECT a FROM t ORDER BY a FORMAT JSONEachRow") == '{"a":1}\n{"a":2}\n{"a":3}\n'
assert query("SELECT a FROM t ORDER BY a", output_format="JSONEachRow") == '{"a":1}\n{"a":2}\n{"a":3}\n'
def test_totals_and_extremes():
query("CREATE TABLE t (x UInt8, y UInt8) ENGINE = Memory")
query("INSERT INTO t VALUES (1, 2), (2, 4), (3, 2), (3, 3), (3, 4)")
assert query("SELECT sum(x), y FROM t GROUP BY y WITH TOTALS") == "4\t2\n3\t3\n5\t4\n"
assert query_and_get_totals("SELECT sum(x), y FROM t GROUP BY y WITH TOTALS") == "12\t0\n"
assert query("SELECT x, y FROM t") == "1\t2\n2\t4\n3\t2\n3\t3\n3\t4\n"
assert query_and_get_extremes("SELECT x, y FROM t", settings={"extremes": "1"}) == "1\t2\n3\t4\n"
def test_errors_handling():
e = query_and_get_error("")
#print(e)
assert "Empty query" in e.display_text
query("CREATE TABLE t (a UInt8) ENGINE = Memory")
e = query_and_get_error("CREATE TABLE t (a UInt8) ENGINE = Memory")
assert "Table default.t already exists" in e.display_text
def test_authentication():
query("CREATE USER john IDENTIFIED BY 'qwe123'")
assert query("SELECT currentUser()", user_name="john", password="qwe123") == "john\n"
def test_logs():
logs = query_and_get_logs("SELECT 1", settings={'send_logs_level':'debug'})
assert "SELECT 1" in logs
assert "Read 1 rows" in logs
assert "Peak memory usage" in logs
def test_progress():
results = query_no_errors("SELECT number, sleep(0.31) FROM numbers(8) SETTINGS max_block_size=2, interactive_delay=100000", stream_output=True)
#print(results)
assert str(results) ==\
"""[progress {
read_rows: 2
read_bytes: 16
total_rows_to_read: 8
}
, output: "0\\t0\\n1\\t0\\n"
, progress {
read_rows: 2
read_bytes: 16
}
, output: "2\\t0\\n3\\t0\\n"
, progress {
read_rows: 2
read_bytes: 16
}
, output: "4\\t0\\n5\\t0\\n"
, progress {
read_rows: 2
read_bytes: 16
}
, output: "6\\t0\\n7\\t0\\n"
, stats {
rows: 8
blocks: 4
allocated_bytes: 324
applied_limit: true
rows_before_limit: 8
}
]"""
def test_session():
session_a = "session A"
session_b = "session B"
query("SET custom_x=1", session_id=session_a)
query("SET custom_y=2", session_id=session_a)
query("SET custom_x=3", session_id=session_b)
query("SET custom_y=4", session_id=session_b)
assert query("SELECT getSetting('custom_x'), getSetting('custom_y')", session_id=session_a) == "1\t2\n"
assert query("SELECT getSetting('custom_x'), getSetting('custom_y')", session_id=session_b) == "3\t4\n"
def test_no_session():
e = query_and_get_error("SET custom_x=1")
assert "There is no session" in e.display_text
def test_input_function():
query("CREATE TABLE t (a UInt8) ENGINE = Memory")
query("INSERT INTO t SELECT col1 * col2 FROM input('col1 UInt8, col2 UInt8') FORMAT CSV", input_data=["5,4\n", "8,11\n", "10,12\n"])
assert query("SELECT a FROM t ORDER BY a") == "20\n88\n120\n"
query("INSERT INTO t SELECT col1 * col2 FROM input('col1 UInt8, col2 UInt8') FORMAT CSV 11,13")
assert query("SELECT a FROM t ORDER BY a") == "20\n88\n120\n143\n"
query("INSERT INTO t SELECT col1 * col2 FROM input('col1 UInt8, col2 UInt8') FORMAT CSV 20,10\n", input_data="15,15\n")
assert query("SELECT a FROM t ORDER BY a") == "20\n88\n120\n143\n200\n225\n"
def test_external_table():
columns = [clickhouse_grpc_pb2.NameAndType(name='UserID', type='UInt64'), clickhouse_grpc_pb2.NameAndType(name='UserName', type='String')]
ext1 = clickhouse_grpc_pb2.ExternalTable(name='ext1', columns=columns, data='1\tAlex\n2\tBen\n3\tCarl\n', format='TabSeparated')
assert query("SELECT * FROM ext1 ORDER BY UserID", external_tables=[ext1]) == "1\tAlex\n"\
"2\tBen\n"\
"3\tCarl\n"
ext2 = clickhouse_grpc_pb2.ExternalTable(name='ext2', columns=columns, data='4,Daniel\n5,Ethan\n', format='CSV')
assert query("SELECT * FROM (SELECT * FROM ext1 UNION ALL SELECT * FROM ext2) ORDER BY UserID", external_tables=[ext1, ext2]) == "1\tAlex\n"\
"2\tBen\n"\
"3\tCarl\n"\
"4\tDaniel\n"\
"5\tEthan\n"
unnamed_columns = [clickhouse_grpc_pb2.NameAndType(type='UInt64'), clickhouse_grpc_pb2.NameAndType(type='String')]
unnamed_table = clickhouse_grpc_pb2.ExternalTable(columns=unnamed_columns, data='6\tGeorge\n7\tFred\n')
assert query("SELECT * FROM _data ORDER BY _2", external_tables=[unnamed_table]) == "7\tFred\n"\
"6\tGeorge\n"
def test_external_table_streaming():
columns = [clickhouse_grpc_pb2.NameAndType(name='UserID', type='UInt64'), clickhouse_grpc_pb2.NameAndType(name='UserName', type='String')]
def send_query_info():
yield clickhouse_grpc_pb2.QueryInfo(query="SELECT * FROM exts ORDER BY UserID",
external_tables=[clickhouse_grpc_pb2.ExternalTable(name='exts', columns=columns, data='1\tAlex\n2\tBen\n3\tCarl\n')],
next_query_info=True)
yield clickhouse_grpc_pb2.QueryInfo(external_tables=[clickhouse_grpc_pb2.ExternalTable(name='exts', data='4\tDaniel\n5\tEthan\n')])
stub = clickhouse_grpc_pb2_grpc.ClickHouseStub(main_channel)
result = stub.ExecuteQueryWithStreamInput(send_query_info())
assert result.output == "1\tAlex\n"\
"2\tBen\n"\
"3\tCarl\n"\
"4\tDaniel\n"\
"5\tEthan\n"
def test_simultaneous_queries_same_channel():
threads=[]
try:
for i in range(0, 100):
thread = QueryThread("SELECT sum(number) FROM numbers(10)", expected_output="45\n", query_id='sqA'+str(i))
threads.append(thread)
thread.start()
finally:
for thread in threads:
thread.join()
def test_simultaneous_queries_multiple_channels():
threads=[]
try:
for i in range(0, 100):
thread = QueryThread("SELECT sum(number) FROM numbers(10)", expected_output="45\n", query_id='sqB'+str(i), use_separate_channel=True)
threads.append(thread)
thread.start()
finally:
for thread in threads:
thread.join()
def test_cancel_while_processing_input():
query("CREATE TABLE t (a UInt8) ENGINE = Memory")
def send_query_info():
yield clickhouse_grpc_pb2.QueryInfo(query="INSERT INTO t FORMAT TabSeparated", input_data="1\n2\n3\n", next_query_info=True)
yield clickhouse_grpc_pb2.QueryInfo(input_data="4\n5\n6\n", next_query_info=True)
yield clickhouse_grpc_pb2.QueryInfo(cancel=True)
stub = clickhouse_grpc_pb2_grpc.ClickHouseStub(main_channel)
result = stub.ExecuteQueryWithStreamInput(send_query_info())
assert result.cancelled == True
assert result.progress.written_rows == 6
assert query("SELECT a FROM t ORDER BY a") == "1\n2\n3\n4\n5\n6\n"
def test_cancel_while_generating_output():
def send_query_info():
yield clickhouse_grpc_pb2.QueryInfo(query="SELECT number, sleep(0.2) FROM numbers(10) SETTINGS max_block_size=2")
time.sleep(0.5)
yield clickhouse_grpc_pb2.QueryInfo(cancel=True)
stub = clickhouse_grpc_pb2_grpc.ClickHouseStub(main_channel)
results = list(stub.ExecuteQueryWithStreamIO(send_query_info()))
assert len(results) >= 1
assert results[-1].cancelled == True
output = ''
for result in results:
output += result.output
assert output == '0\t0\n1\t0\n2\t0\n3\t0\n'

View File

@ -0,0 +1 @@
_gen

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFhTCCA22gAwIBAgIUVM3x4bsymB902aSmqQ3OCNSfqCQwDQYJKoZIhvcNAQEL
BQAwUjELMAkGA1UEBhMCUlUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDELMAkGA1UEAwwCY2EwHhcNMjAxMDI4
MDYzNzExWhcNMzAxMDI2MDYzNzExWjBSMQswCQYDVQQGEwJSVTETMBEGA1UECAwK
U29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQsw
CQYDVQQDDAJjYTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMKSY4+t
V/E+GFt0eKUjRLYui+0662Gqp3/5U3n2NXUeeZEFf6c85v3uyyVLnr1lyWM8dYFG
gR7esvZejequG1bA6OPXbet45veiBVWDANeIXw1Mpr5191wmQ/Jb4Eic9c+GSEiE
UXWP7Ebx0WXZIuh22ESvtUYmrjHI4r1+W2HjbSPQ5ISVA7x5r1LiQzZu0bZ4FfRf
C5s0IhM3DIvvmwu9P1Zwn10/UTOcbrGfDGYklKUZ/JBrXVffMs3n16xnFLSjp59v
V6vqoiP9X7mLcMuBBbe1l+YIQZt0iN0lzqXf6+gXYRS9rLjpNKYiA8Ejbivtel25
fzi3pIF+FFHG7bRCLd78eUh2cNC+Wt7AZKXtlGyG7+hCd0fEBrmEzQWP2pXPyQI2
7SnOSa/mQzCGfETfDRWcDFN0fYA2RHLZSEp/ImxGPeCiAKMg6wecYKcPj5QVWL8F
R9RN8B2i37DEs+dtHpuGdRY8mGoVnkQiP6MXKF6RJV8PG5BFBpIWrdUr6XdXPZLs
fpXQ9iVNxRzj1Qs3x3b2Da38hxSWTSP1zbkBPlMkXRuw+5m8PpkBpiarXF+4i7MM
RCsg07AIKilTM3p21qFLnKMCgCAZX16zPVfBPBipPbk3EjfFPWQ29v5KHm6zFxn6
mBk+2nOmaSXhAjGexHjhikl76SZhA0Re17PXAgMBAAGjUzBRMB0GA1UdDgQWBBTZ
k26nfmjd7o/kNQ7wt+0xnFS3FzAfBgNVHSMEGDAWgBTZk26nfmjd7o/kNQ7wt+0x
nFS3FzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQA1vfdcnkXt
8TSDJJl+PBJTvpKjipAyilipXjpq4vjxTBVtivdUekpV7U8O28RoOOSqtLWaN7uX
rTZyRJK61mWkyJdnS+YPhUG0cvNp7faHXJf9NB74hO57j00kPE5zYvUl35/zRd28
146omM3s9dMf6yKKoESib5Ixm4zntDkqH5Yi88U6/ODwm8jJuJxkJBdYBUtRA4Ky
ETML7GL9Vm2A5QTGBue8nTlYpy1avUK8lsP8W4S3zX8lKuTok4fxmd8TsVMgYLKF
67QKDboPeJNN/Il74Yvd6KhHO5AkcvjLHbwV8NRMi8+0vtQWcpSysrZfoeFS3kcD
lJL532F8IP2mMPsx3ARKEsYZWGKtEjQIUuI0sSIbN2V01nbKQ7zj1tNBe6xhnzyx
Uthx05WWl7YLtM75/Wycesz7ZKxkKbGPu9nhvEhRJjrDT0p0Tjx+of2qKkCkdXRw
QkrGROFvkMcn/YXs95YDZ97pKb355zMiqpBXKqsPHbrgDZbbowmEOgCPiYbxgzce
TVAr00uofy+uFfrdtUGpKncmmvFtVBuNo4Pj9tdsRkDuO8Y10jFIG+2S2gKeTAsy
japTT3FjHjGm9KF/QtbK7RZdxzcn1eUuOO3mF4dj30le0E4iOh6XxFlth3ppoOeb
Z1kFHCU8MtmoINIVYP23aJy8pm1d8D/2Kw==
-----END CERTIFICATE-----

View File

@ -0,0 +1 @@
37C29522A49A2D3B8AF225D079D67FA4A62556E5

View File

@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
z6BihHf8+GVDIxxQroO9XAdgMPExVeM=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFLzCCAxcCFDfClSKkmi07ivIl0HnWf6SmJVblMA0GCSqGSIb3DQEBCwUAMFIx
CzAJBgNVBAYTAlJVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
cm5ldCBXaWRnaXRzIFB0eSBMdGQxCzAJBgNVBAMMAmNhMB4XDTIwMTAyODA2Mzcx
NFoXDTMwMDcyODA2MzcxNFowVjELMAkGA1UEBhMCUlUxEzARBgNVBAgMClNvbWUt
U3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UE
AwwGY2xpZW50MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAynkPl+kW
iMW2ACkZDN26HzCbfudqPANt6JIYFdIq3scs/uqWf7jdsrlyw4j0YeMHLe6tL/NJ
klYBjd5LcxcZe1e7nlES74ShvplX6W1TlJoIp2OVwbhQSn98ctvBsj66g2kdiKae
Bjt811bxw2FbU6aDXGZyUi2NMWNU1pRW80znXuZ0mwcSsV05eStnR2kCx9g58fUe
mEkUMDumFvNQdI2cdxCBQp7Z0lFV90jD1cHAS0pc9EQnj43gIlnjKdoUyjgavLPA
03uhVCjQwK/tIsnu5JtAn+bHhjBST4dHEEbyukoYXqdAGgMLNNble/vIANMG99mn
KnkEoUS1N3M/fpprjJzmz1ElGq1JOL5mpcho56AdY4tCzPo8crUXkMz7BJ4HcqcG
fKjN/gb+2erDK6QGQExUyHXH9d/Vho2Ok91mIxyp2a+hayHFm1pyoTth/tr1f6ZQ
MCMdgTY82ue76nehBejtD/XNN35k75jbqkjLc2lythyL8vnNa3nhml/Gupbggkyp
4iPU/04Ta2JAK1Gat+0chcVJpOiFr7K4YFNrVocoLyCRZ8KxCAdiaFbYFDAcmKr4
rCVQcu4q3ZvJuCx4eQ1M5NwJezulUykrJ02l3xzJ/r6aOBo3mMwRR0Kq5REYowTu
VXbVzhH4U2fPBuW18tQd4ZlALjURdnrqY98CAwEAATANBgkqhkiG9w0BAQsFAAOC
AgEAdP/4Nh37vRcDhZD7vqeC8WhqmRlwWzJeOpukcXuuyAhCDmktQpnYooDxYqz/
2khlPFhltt4/kfyZF0F8kQe+W+563pUd81syttxw0XvpfHDo/sQ2HYJTvowUUA+9
EViLu1wcyujdsOO3bpFz6DClMp8bQy4imC32TZyc4BSx7QBR/mJD3VE16gWl/R2K
GIpdEE6Afz4L8OMr15XE90mT6xsvX1hp3+P2UWtSVDJQrTdy2/FCMXeM1OfYP0LT
f3U5I5SPN4WJfRMIFkULbqyjU2UXuSAEg7nwlli9TgizjAXt6oJsDBYrqIWSuDog
b/1JKCQdonkyDG5U0gZ1yaT88/V+BoXVo1xJt0Jeo1L42wPii8zJHVAbi2EPCz9V
GQ6p3P6ZiSeoRa3EgYI0KxEQSNjYlhOLWrGvPW3dhrkOcVPncx0kFuwHjkKIMajZ
7gvh8CU8aZ0z58h4LIGYMAfwebdEz9XeFM0qPVNO8io0W8VxjQiv7O+OOsjkIm1E
7GjKW80orEonQ4X/uGYJv+nx1ZcV2vzNMEA5ZeoXjYBetGO9+SqBw8F7KO2Wnz+k
vXgXs04J4HcISwc5xbAb5yiZDOSBjwSUqYtwC6nt9XXX/gXnQ2BX/G6lt8mNEx/G
GHXNA6Zl/GQmONFBUqdctyFwf555LgsvhDs2WzxLxLX9HrA=
-----END CERTIFICATE-----

View File

@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
V3Xoig0C2fgC/iiuaMKUh9T9dBnFsjw=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIEmzCCAoMCAQAwVjELMAkGA1UEBhMCUlUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGY2xp
ZW50MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAynkPl+kWiMW2ACkZ
DN26HzCbfudqPANt6JIYFdIq3scs/uqWf7jdsrlyw4j0YeMHLe6tL/NJklYBjd5L
cxcZe1e7nlES74ShvplX6W1TlJoIp2OVwbhQSn98ctvBsj66g2kdiKaeBjt811bx
w2FbU6aDXGZyUi2NMWNU1pRW80znXuZ0mwcSsV05eStnR2kCx9g58fUemEkUMDum
FvNQdI2cdxCBQp7Z0lFV90jD1cHAS0pc9EQnj43gIlnjKdoUyjgavLPA03uhVCjQ
wK/tIsnu5JtAn+bHhjBST4dHEEbyukoYXqdAGgMLNNble/vIANMG99mnKnkEoUS1
N3M/fpprjJzmz1ElGq1JOL5mpcho56AdY4tCzPo8crUXkMz7BJ4HcqcGfKjN/gb+
2erDK6QGQExUyHXH9d/Vho2Ok91mIxyp2a+hayHFm1pyoTth/tr1f6ZQMCMdgTY8
2ue76nehBejtD/XNN35k75jbqkjLc2lythyL8vnNa3nhml/Gupbggkyp4iPU/04T
a2JAK1Gat+0chcVJpOiFr7K4YFNrVocoLyCRZ8KxCAdiaFbYFDAcmKr4rCVQcu4q
3ZvJuCx4eQ1M5NwJezulUykrJ02l3xzJ/r6aOBo3mMwRR0Kq5REYowTuVXbVzhH4
U2fPBuW18tQd4ZlALjURdnrqY98CAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4ICAQCS
2GjAzmqJidkh4qxwztOG1ufnZmPmVX9b2j3O284Kfm3dgqF9wKRCfKP9Te/pn+xO
zCTqO5yMcNDTVapIawZ07JWdAbRO7Laj9GYNJwnO7xDpjCcuUwfmGXRSgNQpNzL3
zuYty/sBKmwZ7+LzKG6jjaqSMtw0LglWO94GtdURilpH4VjOCkp9RraDCrKICYn4
UQhx80ieI4jwxuyZ6I5UJQuabCQ8PdrKQEBOnPGfMYqf+0JRDKIvHWQJvbx43Pyp
d69hJeY0742zRXVPk/W+2p2BhIE/uQWabLp6bqQhc6ZHINuMq1N60NdLMAu5iRoj
vhTiOB/uMG/GBOVswAnCNjn0jTlJd0qoY5EFMrXyDZ+cobmbZiQ0hUCrtV2kP7/5
swOeMTa5dqoLqklvon8RjQlJvzPYMUV6CA5vU4mmeXORNgRmYuVG3xpZkSGB+XtW
QiK0u+4bPLKGslJjowZJvlE7A/ANzwSuGqiLs/UIAnA6URlg0K6gGyzc6c9kVi2k
ryH/Yv72cZ0jE4vpHagM1Ka21VfyS6RAuQxH3OveIi6OViKqGexhPBuja1eIjxVy
yc4i9Y/iZFNXn8AH1bMeXlWoG92Rsm0zhj+Oe082LkF72gwf8p4Khw/otbquGJzX
V24aeSrQCo+lxen7tooDo6BThGjNVnZQOIQSQJqtqA==
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,19 @@
#!/bin/bash
# 1. Generate CA's private key and self-signed certificate
openssl req -newkey rsa:4096 -x509 -days 3650 -nodes -batch -keyout ca-key.pem -out ca-cert.pem -subj "/C=RU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=ca"
# 2. Generate server's private key and certificate signing request (CSR)
openssl req -newkey rsa:4096 -nodes -batch -keyout server-key.pem -out server-req.pem -subj "/C=RU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=server"
# 3. Use CA's private key to sign server's CSR and get back the signed certificate
openssl x509 -req -days 3650 -in server-req.pem -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -extfile server-ext.cnf -out server-cert.pem
# 4. Generate client's private key and certificate signing request (CSR)
openssl req -newkey rsa:4096 -nodes -batch -keyout client-key.pem -out client-req.pem -subj "/C=RU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=client"
# 5. Use CA's private key to sign client's CSR and get back the signed certificate
openssl x509 -req -days 3650 -in client-req.pem -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem
# 6. Generate one more self-signed certificate and private key for using as wrong certificate (because it's not signed by CA)
openssl req -newkey rsa:4096 -x509 -days 3650 -nodes -batch -keyout wrong-client-key.pem -out wrong-client-cert.pem -subj "/C=RU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=client"

View File

@ -0,0 +1,24 @@
<yandex>
<grpc_port>9001</grpc_port>
<grpc>
<enable_ssl>true</enable_ssl>
<!-- The following two files are used only if enable_ssl=1 -->
<ssl_cert_file>/etc/clickhouse-server/config.d/server-cert.pem</ssl_cert_file>
<ssl_key_file>/etc/clickhouse-server/config.d/server-key.pem</ssl_key_file>
<!-- Whether server will request client for a certificate -->
<ssl_require_client_auth>true</ssl_require_client_auth>
<!-- The following file is used only if ssl_require_client_auth=1 -->
<ssl_ca_cert_file>/etc/clickhouse-server/config.d/ca-cert.pem</ssl_ca_cert_file>
<!-- Default compression algorithm (applied if client doesn't specify another algorithm).
Supported algorithms: none, deflate, gzip, stream_gzip -->
<compression>gzip</compression>
<!-- Default compression level (applied if client doesn't specify another level).
Supported levels: none, low, medium, high -->
<compression_level>high</compression_level>
</grpc>
</yandex>

View File

@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFSTCCAzGgAwIBAgIUN8KVIqSaLTuK8iXQedZ/pKYlVuQwDQYJKoZIhvcNAQEL
BQAwUjELMAkGA1UEBhMCUlUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDELMAkGA1UEAwwCY2EwHhcNMjAxMDI4
MDYzNzEyWhcNMzAwNzI4MDYzNzEyWjBWMQswCQYDVQQGEwJSVTETMBEGA1UECAwK
U29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8w
DQYDVQQDDAZzZXJ2ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCv
bbMHZ/fsnqclKmRK7Br6b+qGlXfzF3m8nmbkj0sffXz1lHD9lVyDb2MghOMDstoD
set9xYwBasD3sFuIxgtVi1zMqFHe7Dj8pu8HkWm/9siZ0KkLOJQNvPPtRfcif7R6
btDJgnEEGr+GZWxoo664fYUNT4bF/Ds7hqVPxVfUFJloeuficQXbwb8oAdjuGr6D
0njXUJuE33N2C7a2kpySs6s2JosgxCIpUvPNEciWD5dJOESE4v6KlM9f15lwck+3
e4Vjp1HXKOIdw5owdtnUtqXEefQE/8NwXW1e3HckiNLSZRnv6Es7ZbP3HjmlYW34
GgHpbwxbe2A7z4ZrFjg/O6UQxNuz2vJUE+54tPr/VVkTxhrDo+ST/CH5eWhY3tq1
okatu8jbAys5XeQR+IHiYR/JPVupuynNm8fHsaWvFOcNMfckRsU50r2CysFTsJoY
Y+vNP01PU/RduPeH4iEO35s/Du/iNrTNyZQr+hc353txR2HTa9WjwnQt0xSvh/EU
LsH6TbDVsu9eJu4Eg4Z4IW9KOtUYiwn2aWlq3aUA3XBh2TSvjp8UbDVPdVn0AVdf
G0p+HBV9+fiAMEOv0YCksuj2eYtAVrFcJQ/scc6NVoRPz/+r+v5jKUDp1IpG0wLe
C/kzJ4o/S8TY5wdtZ5fUACwRs6zCE0yT9Wyc1txp9QIDAQABoxMwETAPBgNVHREE
CDAGhwQKBaxNMA0GCSqGSIb3DQEBCwUAA4ICAQB5t5kEGSpbEwrodv25k2J14Zv/
ypRgPbo3QHPgpIJnDLhioxh7ju2yPECw7x9V8QnwxLYvTJTrQ5MZDzM2z8T56bJs
jXxFaOmgE5wSOcugszqsBQ2vGgrZujR+tob2xVAVjBTHh66JBQL9pbPPDi7AAGok
Zh+LZFcjyEUs7IzkSL7UtsvV49ify76ziXuhwQdfAD/MCsnrkdrRInHmXs+umH7h
Wvh91iFKqwEhLx5l4Ojb/LREuBS7W1iXsUWdj4aQUxBxjSS7amX8UoLJTXgVYEN/
Lf8tJfHL3UqKx8WjlFEaqef7+JMW9Jn3ZB0zWaKeawatOrlGQ0E9qTTTmDVyygkT
l1q1pkPX8UAq+pq4SPOpUEn+QOfDWTFtun1rsrdLTKyO6j4Nfi2QDqwhwvUK9PGh
SJHqFCLhFTihDTfSQ39n45frOBLhbx882ex6N1biLFPzPsIFBjKwrItJsSSBzo+B
gVMfbtwxde1hk/KBhyHN9Fll9rzqhA9Xt1/4gAookeeLsWT4yX7RRdp2YrINdgw5
a4bjVo9FbRS8DGYgjDRAqq3Khp1J6EzCIWxcLNWZNfxBdI9thxQNNUQR9g8n6yM0
9oHcAib47ybEgRQ4owXCoBOujklQJmJyrVEk+yOFQxUqWu6zMrrU29j6YhqT//x+
IZu+bd0qm9vdws6oGw==
-----END CERTIFICATE-----

View File

@ -0,0 +1 @@
subjectAltName=IP:10.5.172.77

View File

@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQCvbbMHZ/fsnqcl
KmRK7Br6b+qGlXfzF3m8nmbkj0sffXz1lHD9lVyDb2MghOMDstoDset9xYwBasD3
sFuIxgtVi1zMqFHe7Dj8pu8HkWm/9siZ0KkLOJQNvPPtRfcif7R6btDJgnEEGr+G
ZWxoo664fYUNT4bF/Ds7hqVPxVfUFJloeuficQXbwb8oAdjuGr6D0njXUJuE33N2
C7a2kpySs6s2JosgxCIpUvPNEciWD5dJOESE4v6KlM9f15lwck+3e4Vjp1HXKOId
w5owdtnUtqXEefQE/8NwXW1e3HckiNLSZRnv6Es7ZbP3HjmlYW34GgHpbwxbe2A7
z4ZrFjg/O6UQxNuz2vJUE+54tPr/VVkTxhrDo+ST/CH5eWhY3tq1okatu8jbAys5
XeQR+IHiYR/JPVupuynNm8fHsaWvFOcNMfckRsU50r2CysFTsJoYY+vNP01PU/Rd
uPeH4iEO35s/Du/iNrTNyZQr+hc353txR2HTa9WjwnQt0xSvh/EULsH6TbDVsu9e
Ju4Eg4Z4IW9KOtUYiwn2aWlq3aUA3XBh2TSvjp8UbDVPdVn0AVdfG0p+HBV9+fiA
MEOv0YCksuj2eYtAVrFcJQ/scc6NVoRPz/+r+v5jKUDp1IpG0wLeC/kzJ4o/S8TY
5wdtZ5fUACwRs6zCE0yT9Wyc1txp9QIDAQABAoICAEp4ijCRrUiwjQoU9eBsXIOF
8QxS9Umn5JDyuYE7Z0ul7N8ftdughVZyeaU7ZFeUnEJHTE3nB6kuVV6Qajskv3kA
IVPr/F1i8Eoo333Z+Ad6zuulDa3qxccTOIW4VtI5Xjc/RbjVe/6fEEEQWzYMjkZZ
Wpss8Gypx8dcQF8dykwItpsI2dlh23fPxsNIDXv+tHOnbI8S9AIzDYPGMzlMAdWx
GfRx5kvxlwLt6PtcGkfBtJprnYLADghsZaAtGUEH9dlRQW3tgKuS+nJiL9l1CsHn
C3Xdw34/yQqxTpWfh6BlyQFx09HpTc6YPQ6hsR0IwDM7byZWcut+83pQfaKGGzfg
tXQvMOA8LFYKR9nBMJTvUmpwWIWfXlS2NU8AvJapdgbFvLksjPxuKKXB5zZgnaHY
QH5DqXT+QhHDWLKuo925lh7j8Z3OAZsUvKfhFstYhXRENm8/pkKpkDEDD2Pkjxll
QnQfCg+qCySgfjf5962DdqkepJUWuTCnmIJrn8H4eheMfEs8QRZAuYt6F7cNqdD/
VREGeQ45/5pfPZslK7yVn2SRcVFL/CHg8BR1YalusHtOQsXJRU3Wczd3c4/H6xJw
7xpRuKsit/W/iFjgmg8ai6Q2xJu9wcOMb12jSPIzOywadUvMuxr80z71g3ZzJUIq
VoTWR4brjgfgVwaXoJphAoIBAQDYh4MlD4y1yKCdCXbonANiM4C8as4Tiy4yHryO
Wwrm8zlBnNYlA7Dvz92JHYPYAef+uz9761B7H1UE0fQhj3qzdUdT/79ay8ITSKFf
5yZR3OBcv+GUCKCkMX+M9fhC5L3YTZNMvSjhfcf7Z6Wf7qjEQ+qgVu+vhm+++ReP
6Fa4GB1xjMpbzAj1eh5Pt504NKPYjwojHbkhzrGc3hx3auYksh2JA7wOaQsuLw0R
BJP5EaDn+ByoM77agxGcynWZNDzayy0qHkblAa4PilIbYTt/2/uBhGzCwitVAViB
LO2JMMmNSdi9/+6tAhLViY/aNLU5MuGy4g12KhovDttt4fEtAoIBAQDPaDBlYne5
TgJHxhRwyJmVrpunHolAeqIjUvDkuZfbgGNXjJmamLrHqcIAZqvLdTeRQPgG83Bg
aW6l5kjVzYa/++LCfqSSwqln/OAvzII7+h4XbwYyWn8m/R5Qg8GNAdS/T5J6HqOw
hmZAZVTo6qCcOgOnkdzCBWScEh+LsKcGJ7+WjY25MKUCSdfbFav42vlnI8TeMiKW
Yr/v9i4j6VpIl+TiVViiPCWz0yPuG/L63cqnGL2r6PgJnQloGLfRhrahAv0eClVr
qIRNzGpOJ8H28tZqIlXqtzIc7uVXGFv7mlem8qbjH3xpXtrIlTXfpyU43ZJX5D9f
2hdeYQEbeIjpAoIBAQC2cjCBqMGCZoNW7qOfyd1cAFOH72Kmuw54ssdkF7hu8yAa
dgFGfP64gmiDYTg3aRvG2pXaX2OX6sWjYxkWsiPMVJR+Za8h3TAxOXCoM0dpkPgH
jrKPHlnlH8P+zT6bdsu0F2F3NqfcoDRCVIoM4XaYsTVgVwBs4JUF0DMXUcjII6Gj
jOLxSFGALZ6ewvMZadlo3tXG0wOsRdIIgGpyAMPcxav3SAtMBRyjlIepADeClWUE
MF5NKBS3X9sWRs2iAo8IOExdAdfwnGLCu2UFpv8QdBZOgnt1+0+xt4mQhn+7qj0Z
7NBQ//ZnFqkx/lAUp3bPtFzScoCvQTiA0D76LngdAoIBAB/BQfej1dTHPs4iY6rI
68rhCu/RWw5GOGDUKBG84KU4yPz0h+gFjkNXmF5bWd4yuilBGzhlOUVpnwlDcs9Z
QlJOjG9e2G+n4iwLqX9n/mCUcNsBop8+/8zGYEfPhGjEaQBJfJG58zHpweoGGbP3
iFOjBkA3sub50Gec1ruadlgY6dpxHcvGyNzK1YOzk7rsO9cwwMmMP4Nq5xwkQvg4
mBwZNW/5xTJBpzwbDZs9KDyEjKx8uz2A08kVBAWuEya/S2LnhD1/M4jiKi+HJpeE
usvGblGdpC3lkoSfhvmIvzyMavMin66SeYfgf/rJ7Y+qlaZZNjjCt3lDeOR4UaUV
FVECggEAfstkAVKF0grLfJ0NNdQBg9AYtudiRVVdCQe9UMoJCtTLemaOZt2beDk5
yZ1JvlbuqSTBEqPNECqZMOTxaPUtY6dfkRDI8zTY+MsT2OE9Q75+HYB+Jr/GMAKn
258pjju4emDSaGjmOHFefuti7ODeELQtmoaNL7qOZuTAsoVxa5P+xvsFSq4Nkv1+
TiYLuzZdZHcIf+qZIRm5/P2mvuSmL/SZkh/2kdhPQqib8q2cpzBULj0cNpt13P0c
hB5sujZTY6PjFtZUlMIcTJ6xtzjiVM7Gf4jOf+kxvszcQpkOTg2UXdCac8+uquwU
mBf26auAKORLhL9eFF9XY8tojdEmdA==
-----END PRIVATE KEY-----

View File

@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIEmzCCAoMCAQAwVjELMAkGA1UEBhMCUlUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGc2Vy
dmVyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr22zB2f37J6nJSpk
Suwa+m/qhpV38xd5vJ5m5I9LH3189ZRw/ZVcg29jIITjA7LaA7HrfcWMAWrA97Bb
iMYLVYtczKhR3uw4/KbvB5Fpv/bImdCpCziUDbzz7UX3In+0em7QyYJxBBq/hmVs
aKOuuH2FDU+Gxfw7O4alT8VX1BSZaHrn4nEF28G/KAHY7hq+g9J411CbhN9zdgu2
tpKckrOrNiaLIMQiKVLzzRHIlg+XSThEhOL+ipTPX9eZcHJPt3uFY6dR1yjiHcOa
MHbZ1LalxHn0BP/DcF1tXtx3JIjS0mUZ7+hLO2Wz9x45pWFt+BoB6W8MW3tgO8+G
axY4PzulEMTbs9ryVBPueLT6/1VZE8Yaw6Pkk/wh+XloWN7ataJGrbvI2wMrOV3k
EfiB4mEfyT1bqbspzZvHx7GlrxTnDTH3JEbFOdK9gsrBU7CaGGPrzT9NT1P0Xbj3
h+IhDt+bPw7v4ja0zcmUK/oXN+d7cUdh02vVo8J0LdMUr4fxFC7B+k2w1bLvXibu
BIOGeCFvSjrVGIsJ9mlpat2lAN1wYdk0r46fFGw1T3VZ9AFXXxtKfhwVffn4gDBD
r9GApLLo9nmLQFaxXCUP7HHOjVaET8//q/r+YylA6dSKRtMC3gv5MyeKP0vE2OcH
bWeX1AAsEbOswhNMk/VsnNbcafUCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4ICAQAX
jRopZcCxRE3Ir/9NeoH7zl0ySWMP2m27tiO9ts/iiRqqz8RRyoyXwBxjvdWP+T8i
QO4z0YiuIBQxybBh5S8cyFsI5webf8srRfWBfrUa9Sa28XXdfVtWEkYxNy9Qg3+C
9YQh2GhIMAnnm9lqd0AjtIsMIrswDwy3t8goqXh68Vz9/lzuJXfqP4xQEsbPWy1e
7hTJI7E/BLGpSmCe20HHzmnQd3TtSWVpR/JZCorAd13ePt07rSifrrYNpPCkvEyK
h9xVWF00rp+ftwDaHPaDVuYfMVN3N0HEM+I8377P8IW8FsQGkDZe8IYCdkRZGotY
tFue8/71IekfNokrfXLJw4fCF1gGjx4WWRZvzbFvSiWQZAxwX6Jt2iA/qn05Hv6f
KO3Up9MXF6JqLG3HFRAeMNBhwMpBLYAgGN4uK5S4PBMtbujbgm0ELhDcJv0SrZ4r
am2pzL2h4aoZF8pCPyLubwicFu2SfoWhX6XPRRStuz1g57KX7f41T0xGz5U0PJ/n
Ln7tcUBxEZfhkkWHbdVBgI5CrYQtAGSUU5hE6bNEXeH5VN3TodXXFcoYvgQ2LSb5
f8QKG97x60Aqbq8VdPExPqczjalli9JSaIXfP9pL2F/1PJ6pbZPbeCLMYPvZUxSJ
YnJlvQ2V4uAQJEdEXzMns0drU5+tJTe4hB66uBE2vQ==
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFjTCCA3WgAwIBAgIUGi5763gLrWzkBKSEOkeo1gyKdlUwDQYJKoZIhvcNAQEL
BQAwVjELMAkGA1UEBhMCUlUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGY2xpZW50MB4XDTIw
MTEwMjIyMzE0NloXDTMwMTAzMTIyMzE0NlowVjELMAkGA1UEBhMCUlUxEzARBgNV
BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
ZDEPMA0GA1UEAwwGY2xpZW50MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
AgEAyJGQ+Vg/8ryZ4dgv5cIRRxQeNVNi7amKf+GGZr8ARVLlh+zcz7sWa/stRpQI
Q57F8DHuOvy+Q3FsCtwVcXBI8uNkgmTxwHlq8rqaG5usPb97VejiPrShK4U4h3XM
uZIkLC5pGpo+nHi6BtX8oBXP1BT5++cow/2kQGn0/f+fOjrjpD8jmapnyaUoEloA
laS8Rw9Qw/CGXzSRuUb+w0glDZ3w0ys7XevjWS/2lXHbDy0OnLwlhvQIJb6v/DJW
JGWTZD1cZ+JHwaLS5OE9vLlCmIAPdmqo1m2IHDsFgHsZGYcRZ9c6t1Jh0FQewol1
KFtgbPHDMSYZd2EwrdYbYConrqFleT8AxwOyQNQeZ0tkOCML2cKNtTKLcLPA2E9h
jZqJ8U1ydr1zSwSi/u6bbIhJM9LnBEAxVwzRAut2lpXdtoPQLfNd7NRfFGafOAvN
cYbhf8Drw4pBTtqmlkHguHlDHdwYWOhi/HkuvyZso3T/aQKAd/clAwH0bKfhWuTY
ugyf2yr81WornoPwSDdLis04174g9qc7mGKVjL4JKNl1kyUmGwqEGV7m37hwqy5t
bSH9SPYMLq+qesB+3Nig2O5WJC2uw9ebMTAdzhC6AKxt6WsF2eTlh8UQdCh4PT8d
cyaLBQ0y/OGdg7HU1afeHTXN0FiTvvBT/Bh1Xjbz8dfq4h8CAwEAAaNTMFEwHQYD
VR0OBBYEFKe9PZgSPYKS4G4wlDh47FW5hGaKMB8GA1UdIwQYMBaAFKe9PZgSPYKS
4G4wlDh47FW5hGaKMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB
AJsaPvL7Tb0JJhBv8I+DLXAPmX7ITnpOmK5xoSJxdJVrPEqqnA/K/nJqB0qb5+vt
oYyq91IIIMClySOmA42oNHT7UmsuFRCXMg1wW3x+DwLAHMBL0L3us+0FkCLXUTey
0QMcg3mvWTeQR5ShA8qZTXiv2z7NZudtOA6UznMjXsiHmRAJa0B+L0/JStaRmRZy
m5u59sRyoJ9C8rIFTa5gfBFahjMkm0r1QkafIkpimMLhzsofYNvJ7ZmxBoXSqs6S
Z0Orl0lpNa1sUedm7axoQ10ZXv43kHfLbEB2AIIr8AN+oFAe8Lh1+KALphGPXsFS
zjSQ6kbeSHhoaZCN87Jb0RHC5EQCN5POWSM/cq5WnUMtAtq6JttawaSxw2yP0/ps
Gl8ZUqFGAmo864BLAEbD/wXOGSpUSudN3ITdrlWAM63bmjFfEZH2rD6kmbmliZWN
JcQwwT6sinof1sDBW3qCie/vPh1HNKZG/oA85zndrOCIrMb5yHC3uhYpMeEV4zSi
m8beFGJPKoNl8Fn7Y9GA+7BuhLSaW2FZMN4bn1aub4kifohjjdjXoRwnQ79CGdkl
g7U9hmqzxcjG1ObQsCBrTmO5ju39rDTm8DJohhoAu4c+fUOwXPUE8vTXhklxphfF
lNzuwdJ8yqLxzD7ouuIL2580LJjRTEwbg0S7zxNjQAAx
-----END CERTIFICATE-----

View File

@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
G8i1dUPnAE+ptBu6wVo+RrverCmfe4Q=
-----END PRIVATE KEY-----

View File

@ -0,0 +1 @@
../../../../src/Server/grpc_protos/clickhouse_grpc.proto

View File

@ -0,0 +1,88 @@
import os
import pytest
import subprocess
import sys
import grpc
from helpers.cluster import ClickHouseCluster
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
# Use grpcio-tools to generate *pb2.py files from *.proto.
proto_dir = os.path.join(SCRIPT_DIR, './protos')
gen_dir = os.path.join(SCRIPT_DIR, './_gen')
os.makedirs(gen_dir, exist_ok=True)
subprocess.check_call(
'python3 -m grpc_tools.protoc -I{proto_dir} --python_out={gen_dir} --grpc_python_out={gen_dir} \
{proto_dir}/clickhouse_grpc.proto'.format(proto_dir=proto_dir, gen_dir=gen_dir), shell=True)
sys.path.append(gen_dir)
import clickhouse_grpc_pb2
import clickhouse_grpc_pb2_grpc
# Utilities
node_ip = '10.5.172.77' # It's important for the node to work at this IP because 'server-cert.pem' requires that (see server-ext.cnf).
grpc_port = 9001
node_ip_with_grpc_port = node_ip + ':' + str(grpc_port)
config_dir = os.path.join(SCRIPT_DIR, './configs')
cluster = ClickHouseCluster(__file__)
node = cluster.add_instance('node', ipv4_address=node_ip, main_configs=['configs/grpc_config.xml', 'configs/server-key.pem', 'configs/server-cert.pem', 'configs/ca-cert.pem'])
def create_secure_channel():
ca_cert = open(os.path.join(config_dir, 'ca-cert.pem'), 'rb').read()
client_key = open(os.path.join(config_dir, 'client-key.pem'), 'rb').read()
client_cert = open(os.path.join(config_dir, 'client-cert.pem'), 'rb').read()
credentials = grpc.ssl_channel_credentials(ca_cert, client_key, client_cert)
channel = grpc.secure_channel(node_ip_with_grpc_port, credentials)
grpc.channel_ready_future(channel).result(timeout=10)
return channel
def create_insecure_channel():
channel = grpc.insecure_channel(node_ip_with_grpc_port)
grpc.channel_ready_future(channel).result(timeout=2)
return channel
def create_secure_channel_with_wrong_client_certificate():
ca_cert = open(os.path.join(config_dir, 'ca-cert.pem'), 'rb').read()
client_key = open(os.path.join(config_dir, 'wrong-client-key.pem'), 'rb').read()
client_cert = open(os.path.join(config_dir, 'wrong-client-cert.pem'), 'rb').read()
credentials = grpc.ssl_channel_credentials(ca_cert, client_key, client_cert)
channel = grpc.secure_channel(node_ip_with_grpc_port, credentials)
grpc.channel_ready_future(channel).result(timeout=2)
return channel
def query(query_text, channel):
query_info = clickhouse_grpc_pb2.QueryInfo(query=query_text)
stub = clickhouse_grpc_pb2_grpc.ClickHouseStub(channel)
result = stub.ExecuteQuery(query_info)
if result and result.HasField('exception'):
raise Exception(result.exception.display_text)
return result.output
@pytest.fixture(scope="module", autouse=True)
def start_cluster():
cluster.start()
try:
yield cluster
finally:
cluster.shutdown()
# Actual tests
def test_secure_channel():
with create_secure_channel() as channel:
assert query("SELECT 'ok'", channel) == "ok\n"
def test_insecure_channel():
with pytest.raises(grpc.FutureTimeoutError):
with create_insecure_channel() as channel:
query("SELECT 'ok'", channel)
def test_wrong_client_certificate():
with pytest.raises(grpc.FutureTimeoutError):
with create_insecure_channel() as channel:
query("SELECT 'ok'", channel)

View File

@ -3,6 +3,8 @@
<logger>
<odbc_bridge_log>/var/log/clickhouse-server/clickhouse-odbc-bridge.log</odbc_bridge_log>
<odbc_bridge_errlog>/var/log/clickhouse-server/clickhouse-odbc-bridge.err.log</odbc_bridge_errlog>
<odbc_bridge_stdout>/var/log/clickhouse-server/clickhouse-odbc-bridge.stdout</odbc_bridge_stdout>
<odbc_bridge_stderr>/var/log/clickhouse-server/clickhouse-odbc-bridge.stderr</odbc_bridge_stderr>
<odbc_bridge_level>trace</odbc_bridge_level>
</logger>
</yandex>

View File

@ -289,6 +289,12 @@ def test_postgres_insert(started_cluster):
def test_bridge_dies_with_parent(started_cluster):
if node1.is_built_with_address_sanitizer():
# TODO: Leak sanitizer falsely reports about a leak of 16 bytes in clickhouse-odbc-bridge in this test and
# that's linked somehow with that we have replaced getauxval() in glibc-compatibility.
# The leak sanitizer calls getauxval() for its own purposes, and our replaced version doesn't seem to be equivalent in that case.
return
node1.query("select dictGetString('postgres_odbc_hashed', 'column2', toUInt64(1))")
clickhouse_pid = node1.get_process_pid("clickhouse server")

1
utils/grpc-client/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
_gen

View File

@ -0,0 +1,301 @@
#!/usr/bin/env python3
# This utility provides similar interface to clickhouse-client.
# This utility also can work in two modes: interactive and non-interactive (batch).
#
# For example,
# ./clickhouse_grpc_client.py - runs interactive mode; and
# ./clickhouse_grpc_client.py -u John -q "SELECT * FROM mytable" - runs only a specified query from the user John.
#
# Most of the command line options are the same, for more information type
# ./clickhouse_grpc_client.py --help
import argparse, cmd, os, signal, subprocess, sys, threading, time, uuid, grpc
default_host = 'localhost'
default_port = 9001
default_user_name = 'default'
default_output_format_for_interactive_mode = 'PrettyCompact'
history_filename = '~/.clickhouse_grpc_history'
history_size = 1000
stdin_bufsize = 1048576
class ClickHouseGRPCError(Exception):
pass
# Temporary override reaction on Ctrl+C.
class KeyboardInterruptHandlerOverride:
# If `handler` return True that means pressing Ctrl+C has been handled, no need to call previous handler.
def __init__(self, handler):
self.handler = handler
def __enter__(self):
self.previous_handler = signal.signal(signal.SIGINT, self.__handler)
def __exit__(self, exc_type, exc_value, traceback):
signal.signal(signal.SIGINT, self.previous_handler)
def __handler(self, signum, frame):
if not self.handler():
self.previous_handler(signum, frame)
# Print to stderr
def error_print(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
class ClickHouseGRPCClient(cmd.Cmd):
prompt="grpc :) "
def __init__(self, host=default_host, port=default_port, user_name=default_user_name, password='',
database='', output_format='', settings='', verbatim=False, show_debug_info=False):
super(ClickHouseGRPCClient, self).__init__(completekey=None)
self.host = host
self.port = port
self.user_name = user_name
self.password = password
self.database = database
self.output_format = output_format
self.settings = settings
self.verbatim = verbatim
self.show_debug_info = show_debug_info
self.channel = None
self.stub = None
self.session_id = None
def __enter__(self):
ClickHouseGRPCClient.__generate_pb2()
ClickHouseGRPCClient.__import_pb2()
self.__connect()
return self
def __exit__(self, exc_type, exc_value, traceback):
self.__disconnect()
# Executes a simple query and returns its output.
def get_simple_query_output(self, query_text):
result = self.stub.ExecuteQuery(clickhouse_grpc_pb2.QueryInfo(query=query_text, user_name=self.user_name, password=self.password,
database=self.database, output_format='TabSeparated', settings=self.settings,
session_id=self.session_id, query_id=str(uuid.uuid4())))
if self.show_debug_info:
print('\nresult={}'.format(result))
ClickHouseGRPCClient.__check_no_errors(result)
return result.output
# Executes a query using the stream IO and with ability to cancel it by pressing Ctrl+C.
def run_query(self, query_text, raise_exceptions=True, allow_cancel=False):
start_time = time.time()
cancelled = False
cancel_tries = 0
cancel_event = threading.Event()
def keyboard_interrupt_handler():
if allow_cancel:
nonlocal cancel_tries
cancel_tries = cancel_tries + 1
if cancel_tries < 3:
self.verbatim_print("Cancelling query.")
if cancel_tries == 1:
cancel_event.set()
return True
# third attempt to cancel - we use previous handler which will terminate the client.
self.verbatim_print("Couldn't cancel the query, terminating.")
return False
with KeyboardInterruptHandlerOverride(keyboard_interrupt_handler):
try:
def send_query_info():
# send main query info
info = clickhouse_grpc_pb2.QueryInfo(query=query_text, user_name=self.user_name, password=self.password,
database=self.database, output_format=self.output_format, settings=self.settings,
session_id=self.session_id, query_id=str(uuid.uuid4()))
# send input data
if not sys.stdin.isatty():
while True:
info.input_data = sys.stdin.buffer.read(stdin_bufsize)
if not info.input_data:
break
info.next_query_info = True
yield info
info = clickhouse_grpc_pb2.QueryInfo()
yield info
# wait for possible cancel
if allow_cancel:
cancel_event.wait()
if cancel_tries > 0:
yield clickhouse_grpc_pb2.QueryInfo(cancel=True)
for result in self.stub.ExecuteQueryWithStreamIO(send_query_info()):
if self.show_debug_info:
print('\nresult={}'.format(result))
ClickHouseGRPCClient.__check_no_errors(result)
print(result.output, end='')
if result.cancelled:
cancelled = True
self.verbatim_print("Query was cancelled.")
cancel_event.set()
if not cancelled:
execution_time = time.time() - start_time
self.verbatim_print('\nElapsed: {execution_time} sec.\n'.format(execution_time=execution_time))
except Exception as e:
if raise_exceptions:
raise
error_print(e)
# Establish connection.
def __connect(self):
self.verbatim_print("Connecting to {host}:{port} as user {user_name}.".format(host=self.host, port=self.port, user_name=self.user_name))
# Secure channels are supported by server but not supported by this client.
start_time = time.time()
self.channel = grpc.insecure_channel(self.host + ':' + str(self.port))
connection_time = 0
timeout=5
while True:
try:
grpc.channel_ready_future(self.channel).result(timeout=timeout)
break;
except grpc.FutureTimeoutError:
connection_time += timeout
self.verbatim_print("Couldn't connect to ClickHouse server in {connection_time} seconds.".format(connection_time=connection_time))
self.stub = clickhouse_grpc_pb2_grpc.ClickHouseStub(self.channel)
connection_time = time.time() - start_time
if self.verbatim:
version = self.get_simple_query_output("SELECT version() FORMAT TabSeparated").rstrip('\n')
self.verbatim_print("Connected to ClickHouse server version {version} via gRPC protocol in {connection_time}.".format(version=version, connection_time=connection_time))
def __disconnect(self):
if self.channel:
self.channel.close()
self.channel = None
self.stub = None
self.session_id = None
@staticmethod
def __check_no_errors(result):
if result.HasField('exception'):
raise ClickHouseGRPCError(result.exception.display_text)
# Use grpcio-tools to generate *pb2.py files from *.proto.
@staticmethod
def __generate_pb2():
script_dir = os.path.dirname(os.path.realpath(__file__))
proto_dir = os.path.join(script_dir, './protos')
gen_dir = os.path.join(script_dir, './_gen')
if os.path.exists(os.path.join(gen_dir, 'clickhouse_grpc_pb2_grpc.py')):
return
os.makedirs(gen_dir, exist_ok=True)
cmd = ['python3', '-m', 'grpc_tools.protoc', '-I'+proto_dir, '--python_out='+gen_dir, '--grpc_python_out='+gen_dir,
proto_dir+'/clickhouse_grpc.proto']
p = subprocess.Popen(cmd, stderr=subprocess.PIPE)
# We don't want to show grpc_tools warnings.
errors = p.stderr.read().decode().strip('\n').split('\n')
only_warnings = all(('Warning' in error) for error in errors)
if not only_warnings:
error_print(errors.join('\n'))
# Import the generated *pb2.py files.
@staticmethod
def __import_pb2():
script_dir = os.path.dirname(os.path.realpath(__file__))
gen_dir = os.path.join(script_dir, './_gen')
sys.path.append(gen_dir)
global clickhouse_grpc_pb2, clickhouse_grpc_pb2_grpc
import clickhouse_grpc_pb2, clickhouse_grpc_pb2_grpc
# Prints only if interactive mode is activated.
def verbatim_print(self, *args, **kwargs):
if self.verbatim:
print(*args, **kwargs)
# Overrides Cmd.preloop(). Executed once when cmdloop() is called.
def preloop(self):
super(ClickHouseGRPCClient, self).preloop()
ClickHouseGRPCClient.__read_history()
# we use session for interactive mode
self.session_id = str(uuid.uuid4())
# Overrides Cmd.postloop(). Executed once when cmdloop() is about to return.
def postloop(self):
super(ClickHouseGRPCClient, self).postloop()
ClickHouseGRPCClient.__write_history()
# Overrides Cmd.onecmd(). Runs single command.
def onecmd(self, line):
stripped = line.strip()
if stripped == 'exit' or stripped == 'quit':
return True
if stripped == '':
return False
self.run_query(line, raise_exceptions=False, allow_cancel=True)
return False
# Enables history of commands for interactive mode.
@staticmethod
def __read_history():
global readline
try:
import readline
except ImportError:
readline = None
histfile = os.path.expanduser(history_filename)
if readline and os.path.exists(histfile):
readline.read_history_file(histfile)
@staticmethod
def __write_history():
global readline
if readline:
readline.set_history_length(history_size)
histfile = os.path.expanduser(history_filename)
readline.write_history_file(histfile)
# MAIN
def main(args):
parser = argparse.ArgumentParser(description='ClickHouse client accessing server through gRPC protocol.', add_help=False)
parser.add_argument('--help', help='Show this help message and exit', action='store_true')
parser.add_argument('--host', '-h', help='The server name, localhost by default. You can use either the name or the IPv4 or IPv6 address.', default='localhost')
parser.add_argument('--port', help='The port to connect to. This port should be enabled on the ClickHouse server (see grpc_port in the config).', default=9001)
parser.add_argument('--user', '-u', dest='user_name', help='The username. Default value: default.', default='default')
parser.add_argument('--password', help='The password. Default value: empty string.', default='')
parser.add_argument('--query', '-q', help='The query to process when using non-interactive mode.', default='')
parser.add_argument('--database', '-d', help='Select the current default database. Default value: the current database from the server settings (default by default).', default='')
parser.add_argument('--format', '-f', dest='output_format', help='Use the specified default format to output the result.', default='')
parser.add_argument('--debug', dest='show_debug_info', help='Enables showing the debug information.', action='store_true')
args = parser.parse_args(args)
if args.help:
parser.print_help()
sys.exit(0)
interactive_mode = not args.query
verbatim = interactive_mode
output_format = args.output_format
if not output_format and interactive_mode:
output_format = default_output_format_for_interactive_mode
try:
with ClickHouseGRPCClient(host=args.host, port=args.port, user_name=args.user_name, password=args.password,
database=args.database, output_format=output_format, verbatim=verbatim,
show_debug_info=args.show_debug_info) as client:
if interactive_mode:
client.cmdloop()
else:
client.run_query(args.query)
except KeyboardInterrupt:
pass
except Exception as e:
error_print(e)
if verbatim:
print("\nBye")
if __name__ == '__main__':
main(sys.argv[1:])

View File

@ -0,0 +1 @@
../../../src/Server/grpc_protos/clickhouse_grpc.proto