mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-24 08:32:02 +00:00
Merge pull request #15111 from vitlibar/grpc-protocol
Implement GRPC protocol.
This commit is contained in:
commit
08b3707842
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -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
|
||||
|
19
base/glibc-compatibility/musl/accept4.c
Normal file
19
base/glibc-compatibility/musl/accept4.c
Normal 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;
|
||||
}
|
37
base/glibc-compatibility/musl/epoll.c
Normal file
37
base/glibc-compatibility/musl/epoll.c
Normal 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);
|
||||
}
|
23
base/glibc-compatibility/musl/eventfd.c
Normal file
23
base/glibc-compatibility/musl/eventfd.c
Normal 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;
|
||||
}
|
45
base/glibc-compatibility/musl/getauxval.c
Normal file
45
base/glibc-compatibility/musl/getauxval.c
Normal 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;
|
||||
}
|
8
base/glibc-compatibility/musl/secure_getenv.c
Normal file
8
base/glibc-compatibility/musl/secure_getenv.c
Normal 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);
|
||||
}
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
|
@ -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
1
contrib/abseil-cpp
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 4f3b686f86c3ebaba7e4e926e62a79cb1c659a54
|
2
contrib/grpc
vendored
2
contrib/grpc
vendored
@ -1 +1 @@
|
||||
Subproject commit a6570b863cf76c9699580ba51c7827d5bffaac43
|
||||
Subproject commit 7436366ceb341ba5c00ea29f1645e02a2b70bf93
|
@ -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
2
contrib/protobuf
vendored
@ -1 +1 @@
|
||||
Subproject commit 445d1ae73a450b1e94622e7040989aa2048402e3
|
||||
Subproject commit 73b12814204ad9068ba352914d0dc244648b48ee
|
@ -56,6 +56,7 @@ RUN apt-get update \
|
||||
libprotoc-dev \
|
||||
libgrpc++-dev \
|
||||
protobuf-compiler-grpc \
|
||||
libc-ares-dev \
|
||||
rapidjson-dev \
|
||||
libsnappy-dev \
|
||||
libparquet-dev \
|
||||
|
@ -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
|
||||
|
@ -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 ()
|
||||
|
7
programs/grpc-client/CMakeLists.txt
Normal file
7
programs/grpc-client/CMakeLists.txt
Normal 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)
|
173
programs/grpc-client/grpc_client.cpp
Normal file
173
programs/grpc-client/grpc_client.cpp
Normal 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;
|
||||
}
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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})
|
||||
|
@ -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) \
|
||||
|
@ -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");
|
||||
|
13
src/Core/include/config_core.h
Normal file
13
src/Core/include/config_core.h
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ public:
|
||||
{
|
||||
TCP = 1,
|
||||
HTTP = 2,
|
||||
GRPC = 3,
|
||||
};
|
||||
|
||||
enum class HTTPMethod : uint8_t
|
||||
|
@ -0,0 +1,3 @@
|
||||
if (USE_GRPC)
|
||||
add_subdirectory (grpc_protos)
|
||||
endif()
|
1634
src/Server/GRPCServer.cpp
Normal file
1634
src/Server/GRPCServer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
51
src/Server/GRPCServer.h
Normal file
51
src/Server/GRPCServer.h
Normal 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
|
50
src/Server/ProtocolServerAdapter.cpp
Normal file
50
src/Server/ProtocolServerAdapter.cpp
Normal 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
|
||||
}
|
55
src/Server/ProtocolServerAdapter.h
Normal file
55
src/Server/ProtocolServerAdapter.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
11
src/Server/grpc_protos/CMakeLists.txt
Normal file
11
src/Server/grpc_protos/CMakeLists.txt
Normal 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})
|
151
src/Server/grpc_protos/clickhouse_grpc.proto
Normal file
151
src/Server/grpc_protos/clickhouse_grpc.proto
Normal 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) {}
|
||||
}
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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,
|
||||
|
1
tests/integration/test_grpc_protocol/.gitignore
vendored
Normal file
1
tests/integration/test_grpc_protocol/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
_gen
|
0
tests/integration/test_grpc_protocol/__init__.py
Normal file
0
tests/integration/test_grpc_protocol/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
<yandex>
|
||||
<grpc_port>9001</grpc_port>
|
||||
</yandex>
|
@ -0,0 +1 @@
|
||||
../../../../src/Server/grpc_protos/clickhouse_grpc.proto
|
350
tests/integration/test_grpc_protocol/test.py
Normal file
350
tests/integration/test_grpc_protocol/test.py
Normal 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'
|
1
tests/integration/test_grpc_protocol_ssl/.gitignore
vendored
Normal file
1
tests/integration/test_grpc_protocol_ssl/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
_gen
|
32
tests/integration/test_grpc_protocol_ssl/configs/ca-cert.pem
Normal file
32
tests/integration/test_grpc_protocol_ssl/configs/ca-cert.pem
Normal 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-----
|
@ -0,0 +1 @@
|
||||
37C29522A49A2D3B8AF225D079D67FA4A62556E5
|
52
tests/integration/test_grpc_protocol_ssl/configs/ca-key.pem
Normal file
52
tests/integration/test_grpc_protocol_ssl/configs/ca-key.pem
Normal file
@ -0,0 +1,52 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
z6BihHf8+GVDIxxQroO9XAdgMPExVeM=
|
||||
-----END PRIVATE KEY-----
|
@ -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-----
|
@ -0,0 +1,52 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDKeQ+X6RaIxbYA
|
||||
KRkM3bofMJt+52o8A23okhgV0irexyz+6pZ/uN2yuXLDiPRh4wct7q0v80mSVgGN
|
||||
3ktzFxl7V7ueURLvhKG+mVfpbVOUmginY5XBuFBKf3xy28GyPrqDaR2Ipp4GO3zX
|
||||
VvHDYVtTpoNcZnJSLY0xY1TWlFbzTOde5nSbBxKxXTl5K2dHaQLH2Dnx9R6YSRQw
|
||||
O6YW81B0jZx3EIFCntnSUVX3SMPVwcBLSlz0RCePjeAiWeMp2hTKOBq8s8DTe6FU
|
||||
KNDAr+0iye7km0Cf5seGMFJPh0cQRvK6Shhep0AaAws01uV7+8gA0wb32acqeQSh
|
||||
RLU3cz9+mmuMnObPUSUarUk4vmalyGjnoB1ji0LM+jxytReQzPsEngdypwZ8qM3+
|
||||
Bv7Z6sMrpAZATFTIdcf139WGjY6T3WYjHKnZr6FrIcWbWnKhO2H+2vV/plAwIx2B
|
||||
Njza57vqd6EF6O0P9c03fmTvmNuqSMtzaXK2HIvy+c1reeGaX8a6luCCTKniI9T/
|
||||
ThNrYkArUZq37RyFxUmk6IWvsrhgU2tWhygvIJFnwrEIB2JoVtgUMByYqvisJVBy
|
||||
7irdm8m4LHh5DUzk3Al7O6VTKSsnTaXfHMn+vpo4GjeYzBFHQqrlERijBO5VdtXO
|
||||
EfhTZ88G5bXy1B3hmUAuNRF2eupj3wIDAQABAoICACvwJZAjxv4bWamjQYpnO5K1
|
||||
K6lYctdfLyS/P3bMyvzZYExZgBtrdMDqOZtxRwdPCynOl8rGEvssqt2sldb99kur
|
||||
+E0c7u0HbUo9YLSJQxAGdUZN+Bu38cSY8drDEb+qmTMXRo93COf1VNwuQ/zQTc9T
|
||||
XUJaAQkQVYmYMrr2KgBWegdAH6Hc4ROYVXaUEq+OfEn+BGUbem41vanR5/MnDhic
|
||||
+o1kCYj5i+92Mx+crNLgXuh31MA1YhhnA3Kw6vkUGFrh3fuBTqEWPSZUTSjjhKR+
|
||||
CdeWUWnh3P6j0djsVGGV4+rFVZCE3wZGDtKCp/5F31s+cE6s7vfqlzSgAmtsGSTv
|
||||
Vd1cFf/W1qkx0y8EyUN5EdA8HMeJJdNayc8RZsbtYzByMII54ryGxSa9S5M0iKNR
|
||||
vjk3ogdz54AmQuMOVZHOoBkmNCuTPeHavNoo5xxtC+Qezrj1IvCfcMUhOFdRDTPd
|
||||
bJwJYr9X9HrD8/geSUkQeAXGXMpcOljigk7HZz9u0CdbK85su6jKvCLSmvIUIlYd
|
||||
Le77oI2CJmcgawDGHRnr8bt/GQEM3nbPt4HzMrYhKxPyrax1AhIFMfROJjeib1a/
|
||||
lGBiHFJGQ8y7qCZmgPqNMxgKZibe8IM3OmdLTqMWqn0fl+rq5TinEqVTfG7Lzd+m
|
||||
uEgSHTUDMxiB+6iv+OmRAoIBAQDumfH9dsOwyA9vd6H6V6e59SON5aRixOLZsY1M
|
||||
BEG1CdWNOP9iq5uChz2LLUmXg9KZmoBdFzf5RSO2QXDB+fZwbDM2ugq7BtW8RNj2
|
||||
piG8Hfm0JA9x6xEq9a4XulA//bs3/LqmSWNVIUqxOItPYVa2VvaIo9mrTRJ6DZCe
|
||||
+nTxZyI/QOY0haeh0OoI3bShOsgLPUrr28higD97fuDM1zWHjfRkBPOA83qObG7I
|
||||
bDd/t2xYMylBCLuG7hm/2NOPKX0a3Z4Cxwb7UOQwYAqiGPzd8ibCZaGCLVUkPD+9
|
||||
LlztaGfZMxWLPt7U+OwgCq1AMeNiGPFCNrqy/2Zo5IFhuuWtAoIBAQDZPLFys1Tx
|
||||
yagx97v9sLz/WEYkfN/Gp2DPVyFB8hMT3zj6G+FHEt72XYyQFM3GtD6JlUbwcStv
|
||||
GgW4MawA7A7Jtyj9WXAj/YeGbkZfT9cVaxiB4mYXTvLvDvAixHwob4HAJ73i3R47
|
||||
lc7E6oJqNoyQvlIRCeOWkMIZYNy7eVRWun5TdmV4w/i6TdswJ1ZcbZGm/VZeUpyT
|
||||
pBzVf3XrpFNZpCcLnsi/fHOpu2gOdP8ynoeOJ0y7pigAMrqGS+MVwKVcpVUcOPZr
|
||||
bg0HFCQ6L1fdp3UmMBGLD8lMEOcfH7aTqAO3CC0NGu7npQCmsQTeMlW5Zjwr9lwj
|
||||
DllUEyIIoek7AoIBABe9t6dpvtUDQw3rUHGxcR3+t1fyJqYxQwU4Nn/+2G7kGJyh
|
||||
tD+kJagaPtRvYCg0VKs2BVm96rLh30jOQCL89TmAeqQSZOjxKClAjHoWU7r9HJCB
|
||||
GgxSwHWO1pDD2CJV4+ARRJ0xJjIdSupRXwM6Az/HbWZxL0JtO85rNR2sfCBdC7pR
|
||||
gdZuChz+xZq2PLfiOm9r3N2VE95BNo/aMxIOw+PGCzA8keId1+79+RP2OJt7q8sm
|
||||
5A71iHtHi8ZcE8jl7u3SaVuLZDVzehEaLVStudN3ucWUCTsDuboOSKVRTKvOvK6O
|
||||
97DQQ6nbTtttVInTrPVg1/inq3IOGamMOxd3Wk0CggEBAJBF+7+mlEKkX/0Xf5Wj
|
||||
vFGFSchP2rpvTR9GyYKnh74J4ShSPdJbGRLb3E+QUX2nFWWAa4WiU2tNCAFlk8tk
|
||||
+wlCiImBzbVJWqmQMVJ5jundn4AbYRwXxcW40Qj2H1Mngy+f9T9nBP4jpxgmewiZ
|
||||
Q8+ZZp2ESM8lQpNiPhaQJNAYuW8q0Ydb96Obs3eOmI94AWS7GZI5IV34f2HXbfRa
|
||||
xTRijb/s63wo5hQSr6/ySc5PdM2XEiIJ3sUK68/nNGCs+eXGs/izaAy/IQ3TdZ/i
|
||||
82L1JRuAjxYhHNpU23pTMIlt61Gyr2Dlw0a60ezi4c3D9iTUXiAAVP6/N9pbfp3n
|
||||
EiMCggEBALJHpSPkphZT/z8+olAzmpG5hXF2wz5Tf06ripGKXOAjo77xMTqoOv8F
|
||||
MrTnNtd08vV0rKiGS3yv7yESp+wWH52h3wIAA2BBXGfWpKaf172faUfWRQNdAF9x
|
||||
bKJzHHSLOI/YIkzOuH/nZJVaN9K+8+IFEehUFrItXeqrsTIo7HyO5M7uqZb6OkVa
|
||||
cbOPbAZCkSB+5WEZ9ioOtL1qGvqIovMxixyUrBWUSnse5iiheMMym6Af5396owEP
|
||||
i4OYkjk+NuXGFCI/q2HZV28+3ENsLcgkAR5VRLRrkc1UYZahWUxoXGqDRKnGkOmc
|
||||
V3Xoig0C2fgC/iiuaMKUh9T9dBnFsjw=
|
||||
-----END PRIVATE KEY-----
|
@ -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-----
|
19
tests/integration/test_grpc_protocol_ssl/configs/generate_certs.sh
Executable file
19
tests/integration/test_grpc_protocol_ssl/configs/generate_certs.sh
Executable 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"
|
@ -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>
|
@ -0,0 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
IZu+bd0qm9vdws6oGw==
|
||||
-----END CERTIFICATE-----
|
@ -0,0 +1 @@
|
||||
subjectAltName=IP:10.5.172.77
|
@ -0,0 +1,52 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
mBf26auAKORLhL9eFF9XY8tojdEmdA==
|
||||
-----END PRIVATE KEY-----
|
@ -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-----
|
@ -0,0 +1,32 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
@ -0,0 +1,52 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
G8i1dUPnAE+ptBu6wVo+RrverCmfe4Q=
|
||||
-----END PRIVATE KEY-----
|
@ -0,0 +1 @@
|
||||
../../../../src/Server/grpc_protos/clickhouse_grpc.proto
|
88
tests/integration/test_grpc_protocol_ssl/test.py
Normal file
88
tests/integration/test_grpc_protocol_ssl/test.py
Normal 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)
|
@ -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>
|
||||
|
@ -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
1
utils/grpc-client/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
_gen
|
301
utils/grpc-client/clickhouse-grpc-client.py
Executable file
301
utils/grpc-client/clickhouse-grpc-client.py
Executable 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:])
|
1
utils/grpc-client/protos/clickhouse_grpc.proto
Symbolic link
1
utils/grpc-client/protos/clickhouse_grpc.proto
Symbolic link
@ -0,0 +1 @@
|
||||
../../../src/Server/grpc_protos/clickhouse_grpc.proto
|
Loading…
Reference in New Issue
Block a user