Skip to content

Commit a86acb9

Browse files
authored
CXXCBC-684: Allow setting both named and positional parameters for queries (#759)
1 parent 0d2750b commit a86acb9

File tree

7 files changed

+88
-30
lines changed

7 files changed

+88
-30
lines changed

core/meta/features.hxx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,9 @@
223223
* bucket_settings in both the public and core management APIs have a num_vbuckets attribute
224224
*/
225225
#define COUCHBASE_CXX_CLIENT_HAS_BUCKET_SETTINGS_NUM_VBUCKETS 1
226+
227+
/**
228+
* couchbase::query_options and couchbase::analytics_options allow setting both positional and named
229+
* parameters.
230+
*/
231+
#define COUCHBASE_CXX_CLIENT_QUERY_SUPPORTS_BOTH_POSITIONAL_NAMED_PARAMETERS 1

core/operations/document_analytics.cxx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,16 @@ analytics_request::encode_to(analytics_request::encoded_request_type& encoded,
3535
tao::json::value body{ { "statement", statement },
3636
{ "client_context_id", encoded.client_context_id },
3737
{ "timeout", fmt::format("{}ms", encoded.timeout.count()) } };
38-
if (positional_parameters.empty()) {
39-
for (const auto& [name, value] : named_parameters) {
40-
Expects(name.empty() == false);
41-
std::string key = name;
42-
if (key[0] != '$') {
43-
key.insert(key.begin(), '$');
44-
}
45-
body[key] = utils::json::parse(value);
38+
39+
for (const auto& [name, value] : named_parameters) {
40+
Expects(name.empty() == false);
41+
std::string key = name;
42+
if (key[0] != '$') {
43+
key.insert(key.begin(), '$');
4644
}
47-
} else {
45+
body[key] = utils::json::parse(value);
46+
}
47+
if (!positional_parameters.empty()) {
4848
std::vector<tao::json::value> parameters;
4949
parameters.reserve(positional_parameters.size());
5050
for (const auto& value : positional_parameters) {

core/operations/document_query.cxx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,16 @@ query_request::encode_to(query_request::encoded_request_type& encoded,
6464
timeout_for_service -= std::chrono::milliseconds(500);
6565
}
6666
body["timeout"] = fmt::format("{}ms", timeout_for_service.count());
67-
if (positional_parameters.empty()) {
68-
for (const auto& [name, value] : named_parameters) {
69-
Expects(name.empty() == false);
70-
std::string key = name;
71-
if (key[0] != '$') {
72-
key.insert(key.begin(), '$');
73-
}
74-
body[key] = utils::json::parse(value);
67+
68+
for (const auto& [name, value] : named_parameters) {
69+
Expects(name.empty() == false);
70+
std::string key = name;
71+
if (key[0] != '$') {
72+
key.insert(key.begin(), '$');
7573
}
76-
} else {
74+
body[key] = utils::json::parse(value);
75+
}
76+
if (!positional_parameters.empty()) {
7777
std::vector<tao::json::value> parameters;
7878
parameters.reserve(positional_parameters.size());
7979
for (const auto& value : positional_parameters) {

couchbase/analytics_options.hxx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,8 @@ struct analytics_options : public common_options<analytics_options> {
246246
}
247247

248248
/**
249-
* Set list of positional parameters for a query.
249+
* Set list of positional parameters for a query. Any existing positional parameters will be
250+
* overridden.
250251
*
251252
* @tparam Parameters types for the parameters
252253
* @param parameters the sequence of positional parameters. Each entry will be encoded into JSON.
@@ -260,14 +261,13 @@ struct analytics_options : public common_options<analytics_options> {
260261
std::enable_if_t<codec::is_serializer_v<Serializer>, bool> = true>
261262
auto positional_parameters(const Parameters&... parameters) -> analytics_options&
262263
{
263-
named_parameters_.clear();
264264
positional_parameters_.clear();
265265
encode_positional_parameters<Serializer>(parameters...);
266266
return self();
267267
}
268268

269269
/**
270-
* Set list of named parameters for a query.
270+
* Set list of named parameters for a query. Any existing named parameters will be overridden.
271271
*
272272
* @tparam Parameters types for the parameter pairs
273273
* @param parameters the sequence of name-value pairs. Each value will be encoded into JSON.
@@ -282,7 +282,6 @@ struct analytics_options : public common_options<analytics_options> {
282282
auto named_parameters(const Parameters&... parameters) -> analytics_options&
283283
{
284284
named_parameters_.clear();
285-
positional_parameters_.clear();
286285
encode_named_parameters<Serializer>(parameters...);
287286
return self();
288287
}
@@ -325,7 +324,6 @@ struct analytics_options : public common_options<analytics_options> {
325324
*/
326325
auto encoded_positional_parameters(std::vector<codec::binary> parameters) -> analytics_options&
327326
{
328-
named_parameters_.clear();
329327
positional_parameters_ = std::move(parameters);
330328
return self();
331329
}
@@ -348,7 +346,6 @@ struct analytics_options : public common_options<analytics_options> {
348346
-> analytics_options&
349347
{
350348
named_parameters_ = std::move(parameters);
351-
positional_parameters_.clear();
352349
return self();
353350
}
354351

couchbase/query_options.hxx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,8 @@ struct query_options : public common_options<query_options> {
450450
}
451451

452452
/**
453-
* Set list of positional parameters for a query.
453+
* Set list of positional parameters for a query. Any existing positional parameters will be
454+
* overridden.
454455
*
455456
* @tparam Parameters types for the parameters
456457
* @param parameters the sequence of positional parameters. Each entry will be encoded into JSON.
@@ -464,14 +465,13 @@ struct query_options : public common_options<query_options> {
464465
std::enable_if_t<codec::is_serializer_v<Serializer>, bool> = true>
465466
auto positional_parameters(const Parameters&... parameters) -> query_options&
466467
{
467-
named_parameters_.clear();
468468
positional_parameters_.clear();
469469
encode_positional_parameters<Serializer>(parameters...);
470470
return self();
471471
}
472472

473473
/**
474-
* Set list of named parameters for a query.
474+
* Set list of named parameters for a query. Any existing named parameters will be overridden.
475475
*
476476
* @tparam Parameters types for the parameter pairs
477477
* @param parameters the sequence of name-value pairs. Each value will be encoded into JSON.
@@ -486,7 +486,6 @@ struct query_options : public common_options<query_options> {
486486
auto named_parameters(const Parameters&... parameters) -> query_options&
487487
{
488488
named_parameters_.clear();
489-
positional_parameters_.clear();
490489
encode_named_parameters<Serializer>(parameters...);
491490
return self();
492491
}
@@ -529,7 +528,6 @@ struct query_options : public common_options<query_options> {
529528
*/
530529
auto encoded_positional_parameters(std::vector<codec::binary> parameters) -> query_options&
531530
{
532-
named_parameters_.clear();
533531
positional_parameters_ = std::move(parameters);
534532
return self();
535533
}
@@ -552,7 +550,6 @@ struct query_options : public common_options<query_options> {
552550
-> query_options&
553551
{
554552
named_parameters_ = std::move(parameters);
555-
positional_parameters_.clear();
556553
return self();
557554
}
558555

test/test_integration_analytics.cxx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,3 +633,32 @@ TEST_CASE("integration: public API analytics scope query")
633633
test::utils::execute(integration.cluster, req);
634634
}
635635
}
636+
637+
TEST_CASE("integration: public API analytics query using both named and positional parameters",
638+
"[integration]")
639+
{
640+
test::utils::integration_test_guard integration;
641+
642+
if (!integration.cluster_version().supports_analytics()) {
643+
SKIP("cluster does not support analytics");
644+
}
645+
646+
auto cluster = integration.public_cluster();
647+
648+
auto opts = couchbase::analytics_options().positional_parameters(20, "foo").named_parameters(
649+
std::pair{ "x", 10 });
650+
651+
auto [err, res] = cluster.analytics_query("SELECT $x fieldA, $1 fieldB, $2 fieldC", opts).get();
652+
if (err) {
653+
fmt::println("{}", err.ctx().to_json());
654+
}
655+
REQUIRE_SUCCESS(err.ec());
656+
657+
auto rows = res.rows_as<couchbase::codec::tao_json_serializer, tao::json::value>();
658+
REQUIRE(rows.size() == 1);
659+
660+
auto row = rows[0].get_object();
661+
REQUIRE(row["fieldA"].as<std::uint32_t>() == 10);
662+
REQUIRE(row["fieldB"].as<std::uint32_t>() == 20);
663+
REQUIRE(row["fieldC"].as<std::string>() == "foo");
664+
}

test/test_integration_query.cxx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,3 +722,32 @@ TEST_CASE("integration: query from scope with public API", "[integration]")
722722
REQUIRE(rows[0][collection_name] == value);
723723
}
724724
}
725+
726+
TEST_CASE("integration: public API query using both named and positional parameters",
727+
"[integration]")
728+
{
729+
test::utils::integration_test_guard integration;
730+
731+
if (!integration.cluster_version().supports_query()) {
732+
SKIP("cluster does not support query");
733+
}
734+
735+
auto cluster = integration.public_cluster();
736+
737+
auto opts = couchbase::query_options().positional_parameters(20, "foo").named_parameters(
738+
std::pair{ "x", 10 });
739+
740+
auto [err, res] = cluster.query("SELECT $x fieldA, $1 fieldB, $2 fieldC", opts).get();
741+
if (err) {
742+
fmt::println("{}", err.ctx().to_json());
743+
}
744+
REQUIRE_SUCCESS(err.ec());
745+
746+
auto rows = res.rows_as<couchbase::codec::tao_json_serializer, tao::json::value>();
747+
REQUIRE(rows.size() == 1);
748+
749+
auto row = rows[0].get_object();
750+
REQUIRE(row["fieldA"].as<std::uint32_t>() == 10);
751+
REQUIRE(row["fieldB"].as<std::uint32_t>() == 20);
752+
REQUIRE(row["fieldC"].as<std::string>() == "foo");
753+
}

0 commit comments

Comments
 (0)