diff --git a/clickhouse/client.cpp b/clickhouse/client.cpp index e568df4d..3d57ab5a 100644 --- a/clickhouse/client.cpp +++ b/clickhouse/client.cpp @@ -531,6 +531,10 @@ Block Client::Impl::BeginInsert(Query query) { throw ValidationError("cannot execute query while executing another operation"); } + if (query.HasEventCallbacks()) { + throw ValidationError("Query callbacks are not supported in BeginInsert"); + } + EnsureNull en(static_cast(&query), &events_); if (options_.ping_before_query) { @@ -1377,8 +1381,8 @@ void Client::Insert(const std::string& table_name, const std::string& query_id, impl_->Insert(table_name, query_id, block); } -Block Client::BeginInsert(const std::string& query) { - return impl_->BeginInsert(Query(query)); +Block Client::BeginInsert(const Query& query) { + return impl_->BeginInsert(query); } Block Client::BeginInsert(const std::string& query, const std::string& query_id) { diff --git a/clickhouse/client.h b/clickhouse/client.h index 2717510e..f1d50057 100644 --- a/clickhouse/client.h +++ b/clickhouse/client.h @@ -306,7 +306,9 @@ class Client { void Insert(const std::string& table_name, const std::string& query_id, const Block& block); /// Start an \p INSERT statement, insert batches of data, then finish the insert. - Block BeginInsert(const std::string& query); + /// Queries with event callbacks are not allowed.If the query has any event callbacks set, this + /// call throws ValidationError. + Block BeginInsert(const Query& query); Block BeginInsert(const std::string& query, const std::string& query_id); /// Insert data using a \p block returned by \p BeginInsert. diff --git a/clickhouse/query.h b/clickhouse/query.h index 0bec97f6..27965918 100644 --- a/clickhouse/query.h +++ b/clickhouse/query.h @@ -179,6 +179,12 @@ class Query : public QueryEvents { return *this; } + /// True if any server-event handler is installed. + inline bool HasEventCallbacks() const { + return exception_cb_ || progress_cb_ || select_cb_ || select_cancelable_cb_ + || select_server_log_cb_ || profile_events_callback_cb_ || profile_callback_cb_; + } + static const std::string default_query_id; private: diff --git a/ut/client_ut.cpp b/ut/client_ut.cpp index 3b6b2d5c..8742d4d2 100644 --- a/ut/client_ut.cpp +++ b/ut/client_ut.cpp @@ -541,6 +541,44 @@ TEST_P(ClientCase, InsertData) { EXPECT_EQ(exp, row); } +TEST_P(ClientCase, BeginInsertDoesNotAllowCallbacks) { + client_->Execute( + "CREATE TEMPORARY TABLE IF NOT EXISTS test_clickhouse_cpp_begin_insert_callback (id UInt64)"); + + Query prototype("INSERT INTO test_clickhouse_cpp_begin_insert_callback VALUES"); + Query query = prototype; + client_->BeginInsert(query); // this should not throw any exceptions + client_->EndInsert(); + + query = prototype; + query.OnData([](const Block &){ std::terminate(); }); + EXPECT_THROW(client_->BeginInsert(query), ValidationError); + + query = prototype; + query.OnDataCancelable([](const Block &){ return false; }); + EXPECT_THROW(client_->BeginInsert(query), ValidationError); + + query = prototype; + query.OnException([](const Exception &){ std::terminate(); }); + EXPECT_THROW(client_->BeginInsert(query), ValidationError); + + query = prototype; + query.OnProgress([](const Progress &){ std::terminate(); }); + EXPECT_THROW(client_->BeginInsert(query), ValidationError); + + query = prototype; + query.OnServerLog([](const Block &){ return false; }); + EXPECT_THROW(client_->BeginInsert(query), ValidationError); + + query = prototype; + query.OnProfileEvents([](const Block &){ return false; }); + EXPECT_THROW(client_->BeginInsert(query), ValidationError); + + query = prototype; + query.OnProfile([](const Profile &){ return false; }); + EXPECT_THROW(client_->BeginInsert(query), ValidationError); +} + TEST_P(ClientCase, Nullable) { /// Create a table. client_->Execute(