diff --git a/.github/workflows/windows_msvc.yml b/.github/workflows/windows_msvc.yml new file mode 100644 index 00000000..5ac5178b --- /dev/null +++ b/.github/workflows/windows_msvc.yml @@ -0,0 +1,60 @@ +name: Windows + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +env: + BUILD_TYPE: Release + GTEST_FILTER: --gtest_filter=-"*" + CLICKHOUSE_USER: clickhouse_cpp_cicd + CLICKHOUSE_PASSWORD: clickhouse_cpp_cicd + # + # CLICKHOUSE_HOST: localhost + # CLICKHOUSE_PORT: 9000 + # CLICKHOUSE_USER: default + # CLICKHOUSE_PASSWORD: + # CLICKHOUSE_DB: default + # + # CLICKHOUSE_SECURE_HOST: github.demo.trial.altinity.cloud + # CLICKHOUSE_SECURE_PORT: 9440 + # CLICKHOUSE_SECURE_USER: demo + # CLICKHOUSE_SECURE_PASSWORD: demo + # CLICKHOUSE_SECURE_DB: default + # + # CLICKHOUSE_SECURE2_HOST: gh-api.clickhouse.tech + # CLICKHOUSE_SECURE2_PORT: 9440 + # CLICKHOUSE_SECURE2_USER: explorer + # CLICKHOUSE_SECURE2_PASSWORD: + # CLICKHOUSE_SECURE2_DB: default + +jobs: + build: + runs-on: windows-latest + + steps: + - uses: actions/checkout@v2 + - uses: ilammy/msvc-dev-cmd@v1 + + - name: Configure CMake + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_TESTS=ON + + - name: Build + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + + - name: Start tls offoader proxy + shell: bash + # that mimics non-secure clickhouse running on localhost + # by tunneling queries to remote tls server + # (needed because we can't start real clickhouse instance on windows) + run: | + choco install wget + wget https://github.com/filimonov/go-tlsoffloader/releases/download/v0.1.2/go-tlsoffloader_0.1.2_Windows_x86_64.tar.gz + tar -xvzf go-tlsoffloader_0.1.2_Windows_x86_64.tar.gz + ./go-tlsoffloader.exe -l localhost:9000 -b github.demo.trial.altinity.cloud:9440 & + + - name: Test + working-directory: ${{github.workspace}}/build/ut + run: Release\clickhouse-cpp-ut.exe "${{env.GTEST_FILTER}}" diff --git a/clickhouse/base/compressed.cpp b/clickhouse/base/compressed.cpp index a240f3d3..18d89f03 100644 --- a/clickhouse/base/compressed.cpp +++ b/clickhouse/base/compressed.cpp @@ -142,7 +142,7 @@ void CompressedOutput::Compress(const void * data, size_t len) { (const char*)data, (char*)compressed_buffer_.data() + HEADER_SIZE, len, - compressed_buffer_.size() - HEADER_SIZE); + static_cast(compressed_buffer_.size() - HEADER_SIZE)); if (compressed_size <= 0) throw std::runtime_error("Failed to compress chunk of " + std::to_string(len) + " bytes, " "LZ4 error: " + std::to_string(compressed_size)); @@ -164,7 +164,7 @@ void CompressedOutput::Compress(const void * data, size_t len) { } void CompressedOutput::PreallocateCompressBuffer(size_t input_size) { - const auto estimated_compressed_buffer_size = LZ4_compressBound(input_size); + const auto estimated_compressed_buffer_size = LZ4_compressBound(static_cast(input_size)); if (estimated_compressed_buffer_size <= 0) throw std::runtime_error("Failed to estimate compressed buffer size, LZ4 error: " + std::to_string(estimated_compressed_buffer_size)); diff --git a/clickhouse/base/platform.h b/clickhouse/base/platform.h index 9f53da1a..c9c04699 100644 --- a/clickhouse/base/platform.h +++ b/clickhouse/base/platform.h @@ -13,6 +13,8 @@ #if defined(_win32_) || defined(_win64_) # define _win_ +# define _WIN32_WINNT 0x0600 // The WSAPoll function is defined on Windows Vista and later. +# define WIN32_LEAN_AND_MEAN 1 // don't include too much header automatically #endif #if defined(_linux_) || defined (_darwin_) diff --git a/clickhouse/base/socket.cpp b/clickhouse/base/socket.cpp index b00e3207..e014bdb2 100644 --- a/clickhouse/base/socket.cpp +++ b/clickhouse/base/socket.cpp @@ -14,11 +14,33 @@ # include # include # include -# include #endif namespace clickhouse { +#if defined(_win_) +char const* windowsErrorCategory::name() const noexcept { + return "WindowsSocketError"; +} + +std::string windowsErrorCategory::message(int c) const { + char error[UINT8_MAX]; + auto len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, static_cast(c), 0, error, sizeof(error), nullptr); + if (len == 0) { + return "unknown"; + } + while (len && (error[len - 1] == '\r' || error[len - 1] == '\n')) { + --len; + } + return std::string(error, len); +} + +windowsErrorCategory const& windowsErrorCategory::category() { + static windowsErrorCategory c; + return c; +} +#endif + namespace { class LocalNames : public std::unordered_set { @@ -37,6 +59,22 @@ class LocalNames : public std::unordered_set { } }; +inline int getSocketErrorCode() { +#if defined(_win_) + return WSAGetLastError(); +#else + return errno; +#endif +} + +const std::error_category& getErrorCategory() noexcept { +#if defined(_win_) + return windowsErrorCategory::category(); +#else + return std::system_category(); +#endif +} + void SetNonBlock(SOCKET fd, bool value) { #if defined(_unix_) int flags; @@ -55,8 +93,7 @@ void SetNonBlock(SOCKET fd, bool value) { return ioctl(fd, FIOBIO, &flags); #endif if (ret == -1) { - throw std::system_error( - errno, std::system_category(), "fail to set nonblocking mode"); + throw std::system_error(getSocketErrorCode(), getErrorCategory(), "fail to set nonblocking mode"); } #elif defined(_win_) unsigned long inbuf = value; @@ -68,8 +105,7 @@ void SetNonBlock(SOCKET fd, bool value) { } if (WSAIoctl(fd, FIONBIO, &inbuf, sizeof(inbuf), &outbuf, sizeof(outbuf), &written, 0, 0) == SOCKET_ERROR) { - throw std::system_error( - errno, std::system_category(), "fail to set nonblocking mode"); + throw std::system_error(getSocketErrorCode(), getErrorCategory(), "fail to set nonblocking mode"); } #endif } @@ -94,8 +130,13 @@ SOCKET SocketConnect(const NetworkAddress& addr) { SetNonBlock(s, true); if (connect(s, res->ai_addr, (int)res->ai_addrlen) != 0) { - int err = errno; - if (err == EINPROGRESS || err == EAGAIN || err == EWOULDBLOCK) { + int err = getSocketErrorCode(); + if ( + err == EINPROGRESS || err == EAGAIN || err == EWOULDBLOCK +#if defined(_win_) + || err == WSAEWOULDBLOCK || err == WSAEINPROGRESS +#endif + ) { pollfd fd; fd.fd = s; fd.events = POLLOUT; @@ -103,7 +144,7 @@ SOCKET SocketConnect(const NetworkAddress& addr) { ssize_t rval = Poll(&fd, 1, 5000); if (rval == -1) { - throw std::system_error(errno, std::system_category(), "fail to connect"); + throw std::system_error(getSocketErrorCode(), getErrorCategory(), "fail to connect"); } if (rval > 0) { socklen_t len = sizeof(err); @@ -122,11 +163,9 @@ SOCKET SocketConnect(const NetworkAddress& addr) { } } if (last_err > 0) { - throw std::system_error(last_err, std::system_category(), "fail to connect"); + throw std::system_error(last_err, getErrorCategory(), "fail to connect"); } - throw std::system_error( - errno, std::system_category(), "fail to connect" - ); + throw std::system_error(getSocketErrorCode(), getErrorCategory(), "fail to connect"); } } // namespace @@ -156,7 +195,7 @@ NetworkAddress::NetworkAddress(const std::string& host, const std::string& port) const int error = getaddrinfo(host.c_str(), port.c_str(), &hints, &info_); if (error) { - throw std::system_error(errno, std::system_category()); + throw std::system_error(getSocketErrorCode(), getErrorCategory()); } } @@ -262,14 +301,10 @@ size_t SocketInput::DoRead(void* buf, size_t len) { } if (ret == 0) { - throw std::system_error( - errno, std::system_category(), "closed" - ); + throw std::system_error(getSocketErrorCode(), getErrorCategory(), "closed"); } - throw std::system_error( - errno, std::system_category(), "can't receive string data" - ); + throw std::system_error(getSocketErrorCode(), getErrorCategory(), "can't receive string data"); } bool SocketInput::Skip(size_t /*bytes*/) { @@ -292,9 +327,7 @@ size_t SocketOutput::DoWrite(const void* data, size_t len) { #endif if (::send(s_, (const char*)data, (int)len, flags) != (int)len) { - throw std::system_error( - errno, std::system_category(), "fail to send " + std::to_string(len) + " bytes of data" - ); + throw std::system_error(getSocketErrorCode(), getErrorCategory(), "fail to send " + std::to_string(len) + " bytes of data"); } return len; diff --git a/clickhouse/base/socket.h b/clickhouse/base/socket.h index 00bda042..38c7d5c4 100644 --- a/clickhouse/base/socket.h +++ b/clickhouse/base/socket.h @@ -1,8 +1,8 @@ #pragma once +#include "platform.h" #include "input.h" #include "output.h" -#include "platform.h" #include #include @@ -24,7 +24,7 @@ #endif #include - +#include struct addrinfo; @@ -47,6 +47,17 @@ class NetworkAddress { struct addrinfo* info_; }; +#if defined(_win_) + +class windowsErrorCategory : public std::error_category { +public: + char const* name() const noexcept override final; + std::string message(int c) const override final; + + static windowsErrorCategory const& category(); +}; + +#endif class Socket { public: diff --git a/clickhouse/client.cpp b/clickhouse/client.cpp index 47f5c370..f738f5fe 100644 --- a/clickhouse/client.cpp +++ b/clickhouse/client.cpp @@ -328,9 +328,10 @@ void Client::Impl::ResetConnection() { socket = std::make_unique(address); if (options_.tcp_keepalive) { - socket->SetTcpKeepAlive(options_.tcp_keepalive_idle.count(), - options_.tcp_keepalive_intvl.count(), - options_.tcp_keepalive_cnt); + socket->SetTcpKeepAlive( + static_cast(options_.tcp_keepalive_idle.count()), + static_cast(options_.tcp_keepalive_intvl.count()), + static_cast(options_.tcp_keepalive_cnt)); } if (options_.tcp_nodelay) { socket->SetTcpNoDelay(options_.tcp_nodelay); diff --git a/clickhouse/columns/decimal.cpp b/clickhouse/columns/decimal.cpp index 46c0bcda..489dab5f 100644 --- a/clickhouse/columns/decimal.cpp +++ b/clickhouse/columns/decimal.cpp @@ -134,7 +134,7 @@ void ColumnDecimal::Append(const std::string& value) { bool sign = true; bool has_dot = false; - int zeros = 0; + size_t zeros = 0; while (c != end) { if (*c == '-') { diff --git a/tests/simple/CMakeLists.txt b/tests/simple/CMakeLists.txt index ad5d44e4..9f223630 100644 --- a/tests/simple/CMakeLists.txt +++ b/tests/simple/CMakeLists.txt @@ -3,7 +3,7 @@ ADD_EXECUTABLE (simple-test ) TARGET_LINK_LIBRARIES (simple-test - clickhouse-cpp-lib + clickhouse-cpp-lib-static ) IF (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") diff --git a/ut/CMakeLists.txt b/ut/CMakeLists.txt index 5cc125b1..bdb9bc98 100644 --- a/ut/CMakeLists.txt +++ b/ut/CMakeLists.txt @@ -25,7 +25,7 @@ ADD_EXECUTABLE (clickhouse-cpp-ut ) TARGET_LINK_LIBRARIES (clickhouse-cpp-ut - clickhouse-cpp-lib + clickhouse-cpp-lib-static gtest-lib ) IF (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") diff --git a/ut/columns_ut.cpp b/ut/columns_ut.cpp index 0b13dba6..754877f6 100644 --- a/ut/columns_ut.cpp +++ b/ut/columns_ut.cpp @@ -457,8 +457,17 @@ TEST(ColumnsCase, Int128) { absl::MakeInt128(0x8000000000000000ll, 0), Int128(0) }); + EXPECT_EQ(-1, col->At(0)); - EXPECT_EQ(0xffffffffffffffffll, col->At(1)); + + EXPECT_EQ(absl::MakeInt128(0, 0xffffffffffffffffll), col->At(1)); + EXPECT_EQ(0ll, absl::Int128High64(col->At(1))); + EXPECT_EQ(0xffffffffffffffffull, absl::Int128Low64(col->At(1))); + + EXPECT_EQ(absl::MakeInt128(0xffffffffffffffffll, 0), col->At(2)); + EXPECT_EQ(static_cast(0xffffffffffffffffll), absl::Int128High64(col->At(2))); + EXPECT_EQ(0ull, absl::Int128Low64(col->At(2))); + EXPECT_EQ(0, col->At(4)); } diff --git a/ut/readonly_client_test.cpp b/ut/readonly_client_test.cpp index 17bf7d36..181a25de 100644 --- a/ut/readonly_client_test.cpp +++ b/ut/readonly_client_test.cpp @@ -18,6 +18,8 @@ void ReadonlyClientTest::TearDown() { client_.reset(); } +// Sometimes gtest fails to detect that this test is instantiated elsewhere, suppress the error explicitly. +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ReadonlyClientTest); TEST_P(ReadonlyClientTest, Select) { const auto & queries = std::get<1>(GetParam()); diff --git a/ut/tcp_server.cpp b/ut/tcp_server.cpp index cd479446..24794e09 100644 --- a/ut/tcp_server.cpp +++ b/ut/tcp_server.cpp @@ -1,13 +1,19 @@ #include "tcp_server.h" #include -#include #include #include #include -#include + +#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN64) +# include +#else +# include +# include +# include +#endif + #include -#include namespace clickhouse { @@ -23,7 +29,7 @@ LocalTcpServer::~LocalTcpServer() { void LocalTcpServer::start() { //setup a socket sockaddr_in servAddr; - bzero((char*)&servAddr, sizeof(servAddr)); + memset((char*)&servAddr, 0, sizeof(servAddr)); servAddr.sin_family = AF_INET; servAddr.sin_addr.s_addr = htonl(INADDR_ANY); servAddr.sin_port = htons(port_); @@ -33,7 +39,14 @@ void LocalTcpServer::start() { throw std::runtime_error("Error establishing server socket"); } int enable = 1; - if (setsockopt(serverSd_, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { + +#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN64) + auto res = setsockopt(serverSd_, SOL_SOCKET, SO_REUSEADDR, (const char*)&enable, sizeof(enable)); +#else + auto res = setsockopt(serverSd_, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)); +#endif + + if (res < 0) { std::cerr << "setsockopt(SO_REUSEADDR) failed" << std::endl; } //bind the socket to its local address @@ -47,8 +60,14 @@ void LocalTcpServer::start() { void LocalTcpServer::stop() { if(serverSd_ > 0) { + +#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN64) + shutdown(serverSd_, SD_BOTH); + closesocket(serverSd_); +#else shutdown(serverSd_, SHUT_RDWR); close(serverSd_); +#endif serverSd_ = -1; } } diff --git a/ut/utils.h b/ut/utils.h index 0bde2c73..430dcec2 100644 --- a/ut/utils.h +++ b/ut/utils.h @@ -39,9 +39,8 @@ struct Timer private: static auto Now() { - struct timespec ts; - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); - return std::chrono::nanoseconds(ts.tv_sec * 1000000000LL + ts.tv_nsec); + std::chrono::nanoseconds ns = std::chrono::high_resolution_clock::now().time_since_epoch(); + return ns; } private: