Skip to content

add parameterized syntax, just syntax-sugar #260

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions clickhouse/client.cpp
Original file line number Diff line number Diff line change
@@ -902,22 +902,6 @@ void Client::Execute(const Query& query) {
impl_->ExecuteQuery(query);
}

void Client::Select(const std::string& query, SelectCallback cb) {
Execute(Query(query).OnData(std::move(cb)));
}

void Client::Select(const std::string& query, const std::string& query_id, SelectCallback cb) {
Execute(Query(query, query_id).OnData(std::move(cb)));
}

void Client::SelectCancelable(const std::string& query, SelectCancelableCallback cb) {
Execute(Query(query).OnDataCancelable(std::move(cb)));
}

void Client::SelectCancelable(const std::string& query, const std::string& query_id, SelectCancelableCallback cb) {
Execute(Query(query, query_id).OnDataCancelable(std::move(cb)));
}

void Client::Select(const Query& query) {
Execute(query);
}
95 changes: 91 additions & 4 deletions clickhouse/client.h
Original file line number Diff line number Diff line change
@@ -212,14 +212,43 @@ class Client {

/// Intends for execute select queries. Data will be returned with
/// one or more call of \p cb.
void Select(const std::string& query, SelectCallback cb);
void Select(const std::string& query, const std::string& query_id, SelectCallback cb);
/// Now it supports parameterized syntax, the placeholder should be ?
/// It is just syntax-sugar, all replacing happens on the client side
template<typename... Parameterized>
void Select(const std::string& query, SelectCallback cb, Parameterized&&... params) {
this->ExecuteInner(query, [this, &cb](auto&& new_query) {
Execute(Query(std::move(new_query)).OnData(std::move(cb)));
}, std::forward<Parameterized>(params)...);
}

template<typename... Parameterized>
void Select(const std::string& query,
const std::string& query_id, SelectCallback cb, Parameterized&&... params) {
this->ExecuteInner(query, [this, &cb, queryid = query_id](auto&& new_query) {
Execute(Query(std::move(new_query), std::move(queryid)).OnData(std::move(cb)));
}, std::forward<Parameterized>(params)...);
}

/// Executes a select query which can be canceled by returning false from
/// the data handler function \p cb.
void SelectCancelable(const std::string& query, SelectCancelableCallback cb);
void SelectCancelable(const std::string& query, const std::string& query_id, SelectCancelableCallback cb);
/// Now it supports parameterized syntax, the placeholder should be ?
/// It is just syntax-sugar, all replacing happens on the client side
template<typename... Parameterized>
void SelectCancelable(const std::string& query,
SelectCancelableCallback cb, Parameterized&&... params) {
this->ExecuteInner(query, [this, &cb](auto&& new_query) {
Execute(Query(std::move(new_query)).OnDataCancelable(std::move(cb)));
}, std::forward<Parameterized>(params)...);
}

template<typename... Parameterized>
void SelectCancelable(const std::string& query,
const std::string& query_id, SelectCancelableCallback cb, Parameterized&&... params) {
this->ExecuteInner(query, [this, &cb, queryid = query_id](auto&& new_query) {
Execute(Query(std::move(new_query), std::move(queryid)).OnDataCancelable(std::move(cb)));
}, std::forward<Parameterized>(params)...);
}

/// Alias for Execute.
void Select(const Query& query);

@@ -235,6 +264,64 @@ class Client {

const ServerInfo& GetServerInfo() const;

private:
template<typename Executor, typename... Parameterized>
void ExecuteInner(const std::string& query, Executor&& executor, Parameterized&&... params) {
constexpr auto params_count = sizeof...(params);
if constexpr (params_count == 0) {
executor(query);
}
else {
auto params_tup = std::forward_as_tuple(std::forward<Parameterized>(params)...);
auto new_query = this->RecombineQuery<params_count>(query, params_tup);
executor(new_query);
}
}

template<typename F, std::size_t ... Index>
static constexpr void ForEachTuple(F&& f, std::index_sequence<Index...>) {
(std::forward<F>(f)(std::integral_constant<std::size_t, Index>()), ...);
}

template<size_t ParamsCount, typename ParamsTup>
std::string RecombineQuery(std::string_view query, ParamsTup&& tup) {
//Compute placeholder pos, 'find_first_of' for locating quickly
std::vector<size_t> placeholder;
auto pos = query.find_first_of('?');
if (pos == std::string_view::npos) {
throw ValidationError(std::string("params_count mismath placeholder"));
}

placeholder.emplace_back(pos);
for (auto i = pos + 1; i < query.length(); ++i) {
if (query[i] == '?') {
placeholder.emplace_back(i);
}
}
//check placeholder and parameterized count
if (ParamsCount != placeholder.size()) {
throw ValidationError(std::string("params_count mismath placeholder"));
}

//replace '?' with value
std::string new_query;
new_query.reserve(query.size() + ParamsCount * 16);
new_query.append(query);
this->ForEachTuple([&new_query, &tup, &placeholder](auto index) {
auto element = std::get<index>(tup);
using type = std::remove_const_t<std::remove_reference_t<decltype(element)>>;
if constexpr (
std::is_convertible_v<type, std::string> ||
std::is_same_v<type, std::string_view>) {
new_query = new_query.replace(placeholder[index], 1, element);
}
else {
new_query = new_query.replace(placeholder[index], 1, std::to_string(element));
}
}, std::make_index_sequence<ParamsCount>());
return new_query;
}

private:
const ClientOptions options_;

5 changes: 5 additions & 0 deletions clickhouse/query.cpp
Original file line number Diff line number Diff line change
@@ -19,6 +19,11 @@ Query::Query(const std::string& query, const std::string& query_id)
{
}

Query::Query(std::string&& query, std::string&& query_id)
: query_(std::move(query))
, query_id_(std::move(query_id))
{}

Query::~Query()
{ }

1 change: 1 addition & 0 deletions clickhouse/query.h
Original file line number Diff line number Diff line change
@@ -86,6 +86,7 @@ class Query : public QueryEvents {
Query();
Query(const char* query, const char* query_id = nullptr);
Query(const std::string& query, const std::string& query_id = default_query_id);
Query(std::string&& query, std::string&& query_id = std::string(default_query_id));
~Query() override;

///
24 changes: 24 additions & 0 deletions ut/client_ut.cpp
Original file line number Diff line number Diff line change
@@ -889,6 +889,30 @@ TEST_P(ClientCase, Query_ID) {
EXPECT_EQ(5u, total_count);
}

TEST_P(ClientCase, parameterized_syntax) {
const std::string table_name = "test_clickhouse_cpp_parameterized_syntax";
client_->Execute(Query("CREATE TEMPORARY TABLE IF NOT EXISTS " + table_name + " (a Int64)"));

{
Block b;
b.AppendColumn("a", std::make_shared<ColumnInt64>(std::vector<int64_t>{1, 2, 3}));
client_->Insert(table_name, b);
}

size_t total_count = 0;
client_->Select("SELECT a FROM " + table_name + " WHERE a = ?",
[&total_count](const Block& block) {
total_count += block.GetRowCount();
}, 4);
EXPECT_EQ(0u, total_count);

client_->Select("SELECT a FROM " + table_name + " WHERE a > ?",
[&total_count](const Block& block) {
total_count += block.GetRowCount();
}, 1);
EXPECT_EQ(2u, total_count);
}

// Spontaneosly fails on INSERTint data.
TEST_P(ClientCase, DISABLED_ArrayArrayUInt64) {
// Based on https://github.com/ClickHouse/clickhouse-cpp/issues/43