Skip to content
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

add parameterized syntax, just syntax-sugar #260

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
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
Expand Up @@ -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);
}
Expand Down
95 changes: 91 additions & 4 deletions clickhouse/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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_;

Expand Down
5 changes: 5 additions & 0 deletions clickhouse/query.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{ }

Expand Down
1 change: 1 addition & 0 deletions clickhouse/query.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

///
Expand Down
24 changes: 24 additions & 0 deletions ut/client_ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down