diff --git a/BUILD.bazel b/BUILD.bazel index b51d0235..cba4eaed 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -114,6 +114,10 @@ cc_library( # Users are free to include Abseil in their own projects and convert values produced by this # library to the corresponding Abseil types. "CH_USE_ABSEIL_FOR_BIGNUM=0", + # `Client::GetCurrentEndpoint()` returns `Endpoint` by value rather than the legacy + # `std::optional`. The Bazel build does not promise backward compatibility with + # the 2.x API, so it always uses the new form (CMake's `CH_NON_OPTIONAL_CURRENT_ENDPOINT=ON`). + "CH_NON_OPTIONAL_CURRENT_ENDPOINT=1", ] + select({ # `WITH_OPENSSL` enables the TLS code paths in client.cpp / # sslsocket.cpp (same macro as the CMake option). `USE_BORINGSSL` diff --git a/CMakeLists.txt b/CMakeLists.txt index fd88ff3f..b30ce1f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,6 @@ IF ("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}") ENDIF () OPTION (WITH_OPENSSL "Use OpenSSL for TLS connections" OFF) -OPTION (CH_USE_ABSEIL_FOR_BIGNUM "Use Google Abseil for wide (128-bit) integers" ON) OPTION (WITH_SYSTEM_ABSEIL "Use system Google Abseil, otherwise vendored part part of Google Abseil will be used" OFF) OPTION (WITH_SYSTEM_LZ4 "Use system LZ4" OFF) @@ -23,7 +22,33 @@ OPTION (WITH_SYSTEM_ZSTD "Use system ZSTD" OFF) OPTION (DEBUG_DEPENDENCIES "Print debug info about dependencies duting build" ON) OPTION (CHECK_VERSION "Check that version number corresponds to git tag, usefull in CI/CD to validate that new version published on GitHub has same version in sources" OFF) OPTION (DISABLE_CLANG_LIBC_WORKAROUND "Disable linking compiler-rt & gcc_s if using clang & libstdc++" OFF) -OPTION (CH_MAP_BOOL_TO_UINT8 "Map ClickHouse Bool type to UInt8 instead of exposing a distinct Bool API." ON) + +# API compatibility options. The defaults preserve the legacy 2.x behaviour. CH_USE_3X_API switches +# all of them to the 3.x values at once; individual options may not be set to conflicting values. +OPTION (CH_USE_3X_API "Use the 3.x API: implies CH_MAP_BOOL_TO_UINT8=OFF, CH_USE_ABSEIL_FOR_BIGNUM=OFF, CH_NON_OPTIONAL_CURRENT_ENDPOINT=ON" OFF) +IF (CH_USE_3X_API) + SET (CH_3X_MAP_BOOL_TO_UINT8 OFF) + SET (CH_3X_USE_ABSEIL_FOR_BIGNUM OFF) + SET (CH_3X_NON_OPTIONAL_CURRENT_ENDPOINT ON) +ELSE () + SET (CH_3X_MAP_BOOL_TO_UINT8 ON) + SET (CH_3X_USE_ABSEIL_FOR_BIGNUM ON) + SET (CH_3X_NON_OPTIONAL_CURRENT_ENDPOINT OFF) +ENDIF () +OPTION (CH_MAP_BOOL_TO_UINT8 "Map ClickHouse Bool type to UInt8 instead of exposing a distinct Bool API." ${CH_3X_MAP_BOOL_TO_UINT8}) +OPTION (CH_USE_ABSEIL_FOR_BIGNUM "Use Google Abseil for wide (128-bit) integers" ${CH_3X_USE_ABSEIL_FOR_BIGNUM}) +OPTION (CH_NON_OPTIONAL_CURRENT_ENDPOINT "Make Client::GetCurrentEndpoint() return Endpoint by value instead of std::optional." ${CH_3X_NON_OPTIONAL_CURRENT_ENDPOINT}) + +IF (CH_USE_3X_API) + FOREACH (opt MAP_BOOL_TO_UINT8 USE_ABSEIL_FOR_BIGNUM NON_OPTIONAL_CURRENT_ENDPOINT) + IF ((CH_${opt} AND NOT CH_3X_${opt}) OR (NOT CH_${opt} AND CH_3X_${opt})) + MESSAGE (FATAL_ERROR + "CH_USE_3X_API=ON requires CH_${opt}=${CH_3X_${opt}}, but it is set to '${CH_${opt}}'. " + "Either drop the explicit CH_${opt} setting, or set it to ${CH_3X_${opt}}. " + "If the value comes from a previous configure run, delete CMakeCache.txt or pass -DCH_${opt}=${CH_3X_${opt}}.") + ENDIF () + ENDFOREACH () +ENDIF () PROJECT (CLICKHOUSE-CLIENT VERSION "${CLICKHOUSE_CPP_VERSION}" diff --git a/README.md b/README.md index 80a10283..a3f29a23 100644 --- a/README.md +++ b/README.md @@ -30,14 +30,25 @@ Here is an example with recommended settings; ```sh $ mkdir build . $ cd build -$ cmake .. -DCH_USE_ABSEIL_FOR_BIGNUM=NO -DCH_MAP_BOOL_TO_UINT8=NO +$ cmake .. -DCH_USE_3X_API=YES $ make ``` -The command above disables two legacy CMake defaults, `CH_USE_ABSEIL_FOR_BIGNUM` and -`CH_MAP_BOOL_TO_UINT8`. New projects should set both options to `OFF`. Existing projects can keep -the defaults temporarily, but should migrate to this configuration as this behavior will be removed -in the future versions of the library. +`CH_USE_3X_API` selects the new 3.x API. As the project evolves we bring new changes to the +API; sometimes these require breaking changes. When that happens we keep the old API +unchanged by default and hide the changes behind specific flags. To enable all of these changes +at once, we recommend enabling `CH_USE_3X_API`, which will be the default API in version 3.0. + +Enabling this option is equivalent to setting the following individual options: + +| Option | 2.x default | 3.x value | Effect when set to the 3.x value | +|------------------------------------|-------------|-----------|-----------------------------------------------------------------------------------------| +| `CH_MAP_BOOL_TO_UINT8` | `ON` | `OFF` | ClickHouse `Bool` maps to `clickhouse::ColumnBool` instead of `ColumnUInt8` | +| `CH_USE_ABSEIL_FOR_BIGNUM` | `ON` | `OFF` | 128-bit integers use the self-contained implementation instead of Abseil | +| `CH_NON_OPTIONAL_CURRENT_ENDPOINT` | `OFF` | `ON` | `Client::GetCurrentEndpoint()` returns `Endpoint` by value instead of `std::optional` | + +The individual options can still be set one at a time for a gradual migration; however, when +`CH_USE_3X_API=ON` is given, setting any of them to a conflicting value is a configuration error. Please refer to the workflows for the reference on dependencies/build options - https://github.com/ClickHouse/clickhouse-cpp/blob/master/.github/workflows/linux.yml @@ -97,8 +108,7 @@ Then include it from your `CMakeLists.txt`: cmake_minimum_required(VERSION 3.13) project(application-example LANGUAGES CXX) -set(CH_USE_ABSEIL_FOR_BIGNUM OFF) -set(CH_MAP_BOOL_TO_UINT8 OFF) +set(CH_USE_3X_API ON) add_subdirectory(contrib/clickhouse-cpp) @@ -116,8 +126,7 @@ project(application-example LANGUAGES CXX) include(FetchContent) -set(CH_USE_ABSEIL_FOR_BIGNUM OFF) -set(CH_MAP_BOOL_TO_UINT8 OFF) +set(CH_USE_3X_API ON) FetchContent_Declare( clickhouse_cpp diff --git a/clickhouse/CMakeLists.txt b/clickhouse/CMakeLists.txt index 6664ee4f..7b6387cc 100644 --- a/clickhouse/CMakeLists.txt +++ b/clickhouse/CMakeLists.txt @@ -142,6 +142,11 @@ ELSE () TARGET_COMPILE_DEFINITIONS (clickhouse-cpp-lib PUBLIC CH_USE_ABSEIL_FOR_BIGNUM=0) ENDIF () +IF (CH_NON_OPTIONAL_CURRENT_ENDPOINT) + TARGET_COMPILE_DEFINITIONS (clickhouse-cpp-lib PUBLIC CH_NON_OPTIONAL_CURRENT_ENDPOINT=1) +ELSE () + TARGET_COMPILE_DEFINITIONS (clickhouse-cpp-lib PUBLIC CH_NON_OPTIONAL_CURRENT_ENDPOINT=0) +ENDIF () IF (NOT BUILD_SHARED_LIBS) ADD_LIBRARY (clickhouse-cpp-lib-static ALIAS clickhouse-cpp-lib) diff --git a/clickhouse/client.cpp b/clickhouse/client.cpp index 05f03bcd..8cbf68d7 100644 --- a/clickhouse/client.cpp +++ b/clickhouse/client.cpp @@ -1412,9 +1412,15 @@ void Client::ResetConnectionEndpoint() { impl_->ResetConnectionEndpoint(); } +#if CH_NON_OPTIONAL_CURRENT_ENDPOINT +Endpoint Client::GetCurrentEndpoint() const { + return impl_->GetCurrentEndpoint().value(); +} +#else const std::optional& Client::GetCurrentEndpoint() const { return impl_->GetCurrentEndpoint(); } +#endif const ServerInfo& Client::GetServerInfo() const { return impl_->GetServerInfo(); diff --git a/clickhouse/client.h b/clickhouse/client.h index 5e13d269..dfe31536 100644 --- a/clickhouse/client.h +++ b/clickhouse/client.h @@ -340,7 +340,11 @@ class Client { /// Get current endpoint, i.e. the last successfully connected endpoint. /// It remains optional for backward compatibility, but now always contains a value. +#if CH_NON_OPTIONAL_CURRENT_ENDPOINT + Endpoint GetCurrentEndpoint() const; +#else const std::optional& GetCurrentEndpoint() const; +#endif /// Try to reconnect to different endpoints one by one only one time. If it doesn't work, throw /// an exception. The function starts with the last successfully connected endpoint. diff --git a/contrib/absl/absl/base/options.h b/contrib/absl/absl/base/options.h index 230bf1ee..9e5b26ff 100644 --- a/contrib/absl/absl/base/options.h +++ b/contrib/absl/absl/base/options.h @@ -70,8 +70,12 @@ // Include a standard library header to allow configuration based on the // standard library in use. #ifdef __cplusplus +#if __cplusplus >= 202002L +#include +#else #include #endif +#endif // ----------------------------------------------------------------------------- // Type Compatibility Options diff --git a/tests/simple/main.cpp b/tests/simple/main.cpp index adc2c909..5e95433b 100644 --- a/tests/simple/main.cpp +++ b/tests/simple/main.cpp @@ -588,7 +588,11 @@ int main() { Client client(ClientOptions(localHostEndpoint) .SetPingBeforeQuery(true)); RunTests(client); +#if CH_NON_OPTIONAL_CURRENT_ENDPOINT + std::cout << "current endpoint : " << client.GetCurrentEndpoint().host << "\n"; +#else std::cout << "current endpoint : " << client.GetCurrentEndpoint().value().host << "\n"; +#endif } { diff --git a/ut/client_ut.cpp b/ut/client_ut.cpp index 0e5a020b..88ef5779 100644 --- a/ut/client_ut.cpp +++ b/ut/client_ut.cpp @@ -41,6 +41,16 @@ ClientBoolValue MakeClientBoolValue(bool value) { #endif } +// Client::GetCurrentEndpoint() returns either Endpoint or std::optional +// depending on CH_NON_OPTIONAL_CURRENT_ENDPOINT; normalize to Endpoint for tests. +Endpoint CurrentEndpoint(const Client & client) { +#if CH_NON_OPTIONAL_CURRENT_ENDPOINT + return client.GetCurrentEndpoint(); +#else + return client.GetCurrentEndpoint().value(); +#endif +} + template std::shared_ptr createTableWithOneColumn(Client & client, const std::string & table_name, const std::string & column_name) { @@ -1784,7 +1794,7 @@ TEST_P(ConnectionSuccessTestCase, SuccessConnectionEstablished) { try { client = std::make_unique(client_options); - auto endpoint = client->GetCurrentEndpoint().value(); + auto endpoint = CurrentEndpoint(*client); ASSERT_EQ("localhost", endpoint.host); ASSERT_EQ(9000u, endpoint.port); SUCCEED(); @@ -1866,27 +1876,27 @@ TEST(ResetConnectionEndpointTest, ReconnectsCurrentBeforeFailover) { // The initial connection selects the first endpoint. Client client(options, std::move(socket_factory)); - ASSERT_EQ(primary, client.GetCurrentEndpoint().value()); + ASSERT_EQ(primary, CurrentEndpoint(client)); // A healthy current endpoint is retried without advancing. adapter->SetFailEndpoint(std::nullopt); adapter->ClearConnectRequests(); client.ResetConnectionEndpoint(); - EXPECT_EQ(primary, client.GetCurrentEndpoint().value()); + EXPECT_EQ(primary, CurrentEndpoint(client)); EXPECT_EQ(std::vector{primary}, adapter->ConnectRequests()); // Failure of the current endpoint advances to the next endpoint. adapter->SetFailEndpoint(primary); adapter->ClearConnectRequests(); client.ResetConnectionEndpoint(); - EXPECT_EQ(secondary, client.GetCurrentEndpoint().value()); + EXPECT_EQ(secondary, CurrentEndpoint(client)); EXPECT_EQ((std::vector{primary, secondary}), adapter->ConnectRequests()); // Failure of the last endpoint wraps around to the first endpoint. adapter->SetFailEndpoint(secondary); adapter->ClearConnectRequests(); client.ResetConnectionEndpoint(); - EXPECT_EQ(primary, client.GetCurrentEndpoint().value()); + EXPECT_EQ(primary, CurrentEndpoint(client)); EXPECT_EQ((std::vector{secondary, primary}), adapter->ConnectRequests()); } @@ -1896,12 +1906,12 @@ TEST_P(ResetConnectionTestCase, ResetConnectionTest) { try { client = std::make_unique(client_options); - auto endpoint = client->GetCurrentEndpoint().value(); + auto endpoint = CurrentEndpoint(*client); ASSERT_EQ("localhost", endpoint.host); ASSERT_EQ(9000u, endpoint.port); client->ResetConnection(); - endpoint = client->GetCurrentEndpoint().value(); + endpoint = CurrentEndpoint(*client); ASSERT_EQ("localhost", endpoint.host); ASSERT_EQ(9000u, endpoint.port);