couchbase 3.4.3 → 3.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/ext/couchbase/CMakeLists.txt +22 -1
  4. data/ext/couchbase/core/bucket.cxx +183 -152
  5. data/ext/couchbase/core/bucket.hxx +17 -4
  6. data/ext/couchbase/core/cluster.hxx +41 -13
  7. data/ext/couchbase/core/cluster_options.hxx +3 -0
  8. data/ext/couchbase/core/crud_component.cxx +51 -22
  9. data/ext/couchbase/core/error_context/key_value.cxx +2 -1
  10. data/ext/couchbase/core/error_context/key_value.hxx +10 -12
  11. data/ext/couchbase/core/impl/build_deferred_query_indexes.cxx +115 -50
  12. data/ext/couchbase/core/impl/cluster.cxx +6 -0
  13. data/ext/couchbase/core/impl/create_bucket.cxx +158 -0
  14. data/ext/couchbase/core/impl/create_collection.cxx +83 -0
  15. data/ext/couchbase/core/impl/create_query_index.cxx +172 -59
  16. data/ext/couchbase/core/impl/create_scope.cxx +69 -0
  17. data/ext/couchbase/core/impl/dns_srv_tracker.cxx +2 -1
  18. data/ext/couchbase/core/impl/drop_bucket.cxx +66 -0
  19. data/ext/couchbase/core/impl/drop_collection.cxx +76 -0
  20. data/ext/couchbase/core/impl/drop_query_index.cxx +138 -59
  21. data/ext/couchbase/core/impl/drop_scope.cxx +68 -0
  22. data/ext/couchbase/core/impl/flush_bucket.cxx +66 -0
  23. data/ext/couchbase/core/impl/get_all_buckets.cxx +178 -0
  24. data/ext/couchbase/core/impl/get_all_query_indexes.cxx +67 -37
  25. data/ext/couchbase/core/impl/get_all_scopes.cxx +94 -0
  26. data/ext/couchbase/core/impl/get_bucket.cxx +168 -0
  27. data/ext/couchbase/core/impl/internal_manager_error_context.cxx +113 -0
  28. data/ext/couchbase/core/impl/internal_manager_error_context.hxx +60 -0
  29. data/ext/couchbase/core/impl/key_value_error_category.cxx +2 -4
  30. data/ext/couchbase/core/impl/key_value_error_context.cxx +98 -0
  31. data/ext/couchbase/core/impl/lookup_in.cxx +1 -0
  32. data/ext/couchbase/core/impl/lookup_in_all_replicas.cxx +178 -0
  33. data/ext/couchbase/core/impl/lookup_in_all_replicas.hxx +80 -0
  34. data/ext/couchbase/core/impl/lookup_in_any_replica.cxx +169 -0
  35. data/ext/couchbase/core/impl/lookup_in_any_replica.hxx +75 -0
  36. data/ext/couchbase/core/impl/lookup_in_replica.cxx +104 -0
  37. data/ext/couchbase/core/impl/lookup_in_replica.hxx +67 -0
  38. data/ext/couchbase/core/impl/manager_error_context.cxx +100 -0
  39. data/ext/couchbase/core/impl/query.cxx +1 -0
  40. data/ext/couchbase/core/impl/query_error_context.cxx +75 -0
  41. data/ext/couchbase/core/impl/update_bucket.cxx +133 -0
  42. data/ext/couchbase/core/impl/update_collection.cxx +83 -0
  43. data/ext/couchbase/core/impl/watch_query_indexes.cxx +53 -29
  44. data/ext/couchbase/core/io/dns_client.cxx +111 -40
  45. data/ext/couchbase/core/io/dns_config.cxx +5 -4
  46. data/ext/couchbase/core/io/http_session.hxx +24 -1
  47. data/ext/couchbase/core/io/mcbp_command.hxx +9 -2
  48. data/ext/couchbase/core/io/mcbp_session.cxx +80 -43
  49. data/ext/couchbase/core/io/mcbp_session.hxx +4 -3
  50. data/ext/couchbase/core/logger/custom_rotating_file_sink.cxx +1 -1
  51. data/ext/couchbase/core/logger/logger.cxx +80 -20
  52. data/ext/couchbase/core/logger/logger.hxx +31 -0
  53. data/ext/couchbase/core/management/bucket_settings.hxx +8 -5
  54. data/ext/couchbase/core/management/bucket_settings_json.hxx +12 -2
  55. data/ext/couchbase/core/meta/features.hxx +42 -0
  56. data/ext/couchbase/core/operations/document_lookup_in.cxx +8 -1
  57. data/ext/couchbase/core/operations/document_lookup_in_all_replicas.hxx +192 -0
  58. data/ext/couchbase/core/operations/document_lookup_in_any_replica.hxx +188 -0
  59. data/ext/couchbase/core/operations/document_query.cxx +11 -0
  60. data/ext/couchbase/core/operations/document_query.hxx +1 -0
  61. data/ext/couchbase/core/operations/management/CMakeLists.txt +1 -0
  62. data/ext/couchbase/core/operations/management/bucket_create.cxx +30 -9
  63. data/ext/couchbase/core/operations/management/bucket_update.cxx +27 -6
  64. data/ext/couchbase/core/operations/management/collection_create.cxx +5 -1
  65. data/ext/couchbase/core/operations/management/collection_create.hxx +1 -0
  66. data/ext/couchbase/core/operations/management/collection_update.cxx +87 -0
  67. data/ext/couchbase/core/operations/management/collection_update.hxx +54 -0
  68. data/ext/couchbase/core/operations/management/collections.hxx +1 -0
  69. data/ext/couchbase/core/operations.hxx +2 -0
  70. data/ext/couchbase/core/origin.cxx +270 -0
  71. data/ext/couchbase/core/origin.hxx +2 -0
  72. data/ext/couchbase/core/protocol/client_response.hxx +1 -0
  73. data/ext/couchbase/core/protocol/cmd_hello.hxx +1 -0
  74. data/ext/couchbase/core/protocol/cmd_lookup_in_replica.cxx +107 -0
  75. data/ext/couchbase/core/protocol/cmd_lookup_in_replica.hxx +137 -0
  76. data/ext/couchbase/core/protocol/hello_feature.hxx +6 -0
  77. data/ext/couchbase/core/protocol/hello_feature_fmt.hxx +3 -0
  78. data/ext/couchbase/core/protocol/status.cxx +2 -2
  79. data/ext/couchbase/core/range_scan_options.cxx +3 -27
  80. data/ext/couchbase/core/range_scan_options.hxx +13 -17
  81. data/ext/couchbase/core/range_scan_orchestrator.cxx +388 -170
  82. data/ext/couchbase/core/range_scan_orchestrator.hxx +13 -2
  83. data/ext/couchbase/core/range_scan_orchestrator_options.hxx +5 -3
  84. data/ext/couchbase/core/scan_options.hxx +0 -19
  85. data/ext/couchbase/core/scan_result.cxx +19 -5
  86. data/ext/couchbase/core/scan_result.hxx +5 -2
  87. data/ext/couchbase/core/timeout_defaults.hxx +3 -4
  88. data/ext/couchbase/core/topology/capabilities.hxx +4 -0
  89. data/ext/couchbase/core/topology/capabilities_fmt.hxx +11 -0
  90. data/ext/couchbase/core/topology/collections_manifest.hxx +2 -0
  91. data/ext/couchbase/core/topology/collections_manifest_fmt.hxx +1 -1
  92. data/ext/couchbase/core/topology/collections_manifest_json.hxx +3 -0
  93. data/ext/couchbase/core/topology/configuration.hxx +20 -0
  94. data/ext/couchbase/core/topology/configuration_json.hxx +8 -1
  95. data/ext/couchbase/core/utils/connection_string.cxx +62 -47
  96. data/ext/couchbase/core/utils/connection_string.hxx +1 -0
  97. data/ext/couchbase/couchbase/analytics_error_context.hxx +1 -1
  98. data/ext/couchbase/couchbase/behavior_options.hxx +19 -2
  99. data/ext/couchbase/couchbase/bucket.hxx +14 -0
  100. data/ext/couchbase/couchbase/bucket_manager.hxx +135 -0
  101. data/ext/couchbase/couchbase/build_query_index_options.hxx +0 -30
  102. data/ext/couchbase/couchbase/cluster.hxx +14 -0
  103. data/ext/couchbase/couchbase/collection.hxx +111 -0
  104. data/ext/couchbase/couchbase/collection_manager.hxx +160 -0
  105. data/ext/couchbase/couchbase/collection_query_index_manager.hxx +7 -48
  106. data/ext/couchbase/couchbase/create_bucket_options.hxx +41 -0
  107. data/ext/couchbase/couchbase/create_collection_options.hxx +44 -0
  108. data/ext/couchbase/couchbase/create_primary_query_index_options.hxx +0 -29
  109. data/ext/couchbase/couchbase/create_query_index_options.hxx +0 -33
  110. data/ext/couchbase/couchbase/create_scope_options.hxx +41 -0
  111. data/ext/couchbase/couchbase/drop_bucket_options.hxx +41 -0
  112. data/ext/couchbase/couchbase/drop_collection_options.hxx +41 -0
  113. data/ext/couchbase/couchbase/drop_primary_query_index_options.hxx +0 -30
  114. data/ext/couchbase/couchbase/drop_query_index_options.hxx +0 -31
  115. data/ext/couchbase/couchbase/drop_scope_options.hxx +41 -0
  116. data/ext/couchbase/couchbase/error_codes.hxx +1 -2
  117. data/ext/couchbase/couchbase/error_context.hxx +10 -2
  118. data/ext/couchbase/couchbase/flush_bucket_options.hxx +41 -0
  119. data/ext/couchbase/{core/topology/error_map_fmt.hxx → couchbase/fmt/key_value_error_map_attribute.hxx} +21 -21
  120. data/ext/couchbase/couchbase/get_all_buckets_options.hxx +44 -0
  121. data/ext/couchbase/couchbase/get_all_query_indexes_options.hxx +0 -30
  122. data/ext/couchbase/couchbase/get_all_scopes_options.hxx +44 -0
  123. data/ext/couchbase/couchbase/get_and_lock_options.hxx +2 -2
  124. data/ext/couchbase/couchbase/get_and_touch_options.hxx +2 -2
  125. data/ext/couchbase/couchbase/get_bucket_options.hxx +43 -0
  126. data/ext/couchbase/couchbase/get_options.hxx +2 -2
  127. data/ext/couchbase/couchbase/insert_options.hxx +3 -3
  128. data/ext/couchbase/couchbase/key_value_error_context.hxx +7 -2
  129. data/ext/couchbase/couchbase/lookup_in_all_replicas_options.hxx +109 -0
  130. data/ext/couchbase/couchbase/lookup_in_any_replica_options.hxx +101 -0
  131. data/ext/couchbase/couchbase/lookup_in_options.hxx +2 -2
  132. data/ext/couchbase/couchbase/lookup_in_replica_result.hxx +74 -0
  133. data/ext/couchbase/couchbase/lookup_in_result.hxx +26 -0
  134. data/ext/couchbase/couchbase/management/bucket_settings.hxx +119 -0
  135. data/ext/couchbase/couchbase/management/collection_spec.hxx +29 -0
  136. data/ext/couchbase/couchbase/management/scope_spec.hxx +29 -0
  137. data/ext/couchbase/couchbase/manager_error_context.hxx +29 -53
  138. data/ext/couchbase/couchbase/mutate_in_options.hxx +2 -2
  139. data/ext/couchbase/couchbase/query_error_context.hxx +3 -1
  140. data/ext/couchbase/couchbase/query_index_manager.hxx +16 -83
  141. data/ext/couchbase/couchbase/query_options.hxx +18 -0
  142. data/ext/couchbase/couchbase/remove_options.hxx +2 -2
  143. data/ext/couchbase/couchbase/replace_options.hxx +3 -3
  144. data/ext/couchbase/couchbase/security_options.hxx +15 -0
  145. data/ext/couchbase/couchbase/subdocument_error_context.hxx +4 -2
  146. data/ext/couchbase/couchbase/touch_options.hxx +2 -2
  147. data/ext/couchbase/couchbase/unlock_options.hxx +2 -2
  148. data/ext/couchbase/couchbase/update_bucket_options.hxx +41 -0
  149. data/ext/couchbase/couchbase/update_collection_options.hxx +44 -0
  150. data/ext/couchbase/couchbase/upsert_options.hxx +3 -3
  151. data/ext/couchbase/couchbase/watch_query_indexes_options.hxx +0 -31
  152. data/ext/couchbase/test/CMakeLists.txt +1 -0
  153. data/ext/couchbase/test/test_integration_collections.cxx +6 -0
  154. data/ext/couchbase/test/test_integration_crud.cxx +5 -0
  155. data/ext/couchbase/test/test_integration_examples.cxx +137 -1
  156. data/ext/couchbase/test/test_integration_management.cxx +1009 -309
  157. data/ext/couchbase/test/test_integration_query.cxx +19 -7
  158. data/ext/couchbase/test/test_integration_range_scan.cxx +351 -112
  159. data/ext/couchbase/test/test_integration_search.cxx +10 -1
  160. data/ext/couchbase/test/test_integration_subdoc.cxx +721 -7
  161. data/ext/couchbase/test/test_transaction_public_async_api.cxx +13 -12
  162. data/ext/couchbase/test/test_transaction_public_blocking_api.cxx +27 -21
  163. data/ext/couchbase/test/test_unit_connection_string.cxx +29 -0
  164. data/ext/couchbase/test/test_unit_query.cxx +75 -0
  165. data/ext/couchbase.cxx +735 -60
  166. data/ext/revisions.rb +3 -3
  167. data/lib/couchbase/cluster.rb +1 -1
  168. data/lib/couchbase/collection.rb +108 -0
  169. data/lib/couchbase/collection_options.rb +100 -1
  170. data/lib/couchbase/errors.rb +5 -0
  171. data/lib/couchbase/key_value_scan.rb +125 -0
  172. data/lib/couchbase/management/bucket_manager.rb +22 -15
  173. data/lib/couchbase/management/collection_manager.rb +158 -9
  174. data/lib/couchbase/options.rb +151 -0
  175. data/lib/couchbase/scope.rb +1 -1
  176. data/lib/couchbase/utils/time.rb +14 -1
  177. data/lib/couchbase/version.rb +1 -1
  178. metadata +59 -8
  179. data/ext/couchbase/core/impl/collection_query_index_manager.cxx +0 -93
@@ -49,7 +49,8 @@ assert_single_lookup_error(test::utils::integration_test_guard& integration,
49
49
  const couchbase::core::document_id& id,
50
50
  const SubdocumentOperation& spec,
51
51
  couchbase::key_value_status_code expected_status,
52
- std::error_code expected_ec)
52
+ std::error_code expected_ec,
53
+ std::optional<std::string> expected_value = std::nullopt)
53
54
  {
54
55
  couchbase::core::operations::lookup_in_request req{ id };
55
56
  req.specs = couchbase::lookup_in_specs{ spec }.specs();
@@ -60,9 +61,127 @@ assert_single_lookup_error(test::utils::integration_test_guard& integration,
60
61
  REQUIRE(resp.fields.size() == 1);
61
62
  REQUIRE_FALSE(resp.fields[0].exists);
62
63
  REQUIRE(resp.fields[0].path == req.specs[0].path_);
63
- REQUIRE(resp.fields[0].value.empty());
64
64
  REQUIRE(resp.fields[0].status == expected_status);
65
65
  REQUIRE(resp.fields[0].ec == expected_ec);
66
+ if (expected_value.has_value()) {
67
+ REQUIRE(couchbase::core::utils::to_binary(expected_value.value()) == resp.fields[0].value);
68
+ } else {
69
+ REQUIRE(resp.fields[0].value.empty());
70
+ }
71
+ }
72
+
73
+ template<typename SubdocumentOperation>
74
+ void
75
+ assert_single_lookup_any_replica_success(test::utils::integration_test_guard& integration,
76
+ const couchbase::core::document_id& id,
77
+ const SubdocumentOperation& spec,
78
+ std::optional<std::string> expected_value = std::nullopt)
79
+ {
80
+ couchbase::core::operations::lookup_in_any_replica_request req{ id };
81
+ req.specs = couchbase::lookup_in_specs{ spec }.specs();
82
+ auto resp = test::utils::execute(integration.cluster, req);
83
+ INFO(fmt::format("assert_single_lookup_all_replica_success(\"{}\", \"{}\")", id, req.specs[0].path_));
84
+ REQUIRE_SUCCESS(resp.ctx.ec());
85
+ REQUIRE_FALSE(resp.cas.empty());
86
+ REQUIRE(resp.fields.size() == 1);
87
+ REQUIRE(resp.fields[0].exists);
88
+ REQUIRE(resp.fields[0].path == req.specs[0].path_);
89
+ REQUIRE(resp.fields[0].status == couchbase::key_value_status_code::success);
90
+ REQUIRE_SUCCESS(resp.fields[0].ec);
91
+ if (expected_value.has_value()) {
92
+ REQUIRE(couchbase::core::utils::to_binary(expected_value.value()) == resp.fields[0].value);
93
+ } else {
94
+ REQUIRE(resp.fields[0].value.empty());
95
+ }
96
+ }
97
+
98
+ template<typename SubdocumentOperation>
99
+ void
100
+ assert_single_lookup_any_replica_error(test::utils::integration_test_guard& integration,
101
+ const couchbase::core::document_id& id,
102
+ const SubdocumentOperation& spec,
103
+ couchbase::key_value_status_code expected_status,
104
+ std::error_code expected_ec,
105
+ std::optional<std::string> expected_value = std::nullopt)
106
+ {
107
+ couchbase::core::operations::lookup_in_any_replica_request req{ id };
108
+ req.specs = couchbase::lookup_in_specs{ spec }.specs();
109
+ auto resp = test::utils::execute(integration.cluster, req);
110
+ INFO(fmt::format("assert_single_lookup_any_replica_error(\"{}\", \"{}\")", id, req.specs[0].path_));
111
+ REQUIRE_SUCCESS(resp.ctx.ec());
112
+ REQUIRE_FALSE(resp.cas.empty());
113
+ REQUIRE(resp.fields.size() == 1);
114
+ REQUIRE_FALSE(resp.fields[0].exists);
115
+ REQUIRE(resp.fields[0].path == req.specs[0].path_);
116
+ REQUIRE(resp.fields[0].status == expected_status);
117
+ REQUIRE(resp.fields[0].ec == expected_ec);
118
+ if (expected_value.has_value()) {
119
+ REQUIRE(couchbase::core::utils::to_binary(expected_value.value()) == resp.fields[0].value);
120
+ } else {
121
+ REQUIRE(resp.fields[0].value.empty());
122
+ }
123
+ }
124
+
125
+ template<typename SubdocumentOperation>
126
+ void
127
+ assert_single_lookup_all_replica_success(test::utils::integration_test_guard& integration,
128
+ const couchbase::core::document_id& id,
129
+ const SubdocumentOperation& spec,
130
+ std::optional<std::string> expected_value = std::nullopt)
131
+ {
132
+ couchbase::core::operations::lookup_in_all_replicas_request req{ id };
133
+ req.specs = couchbase::lookup_in_specs{ spec }.specs();
134
+ auto response = test::utils::execute(integration.cluster, req);
135
+ INFO(fmt::format("assert_single_lookup_all_replica_success(\"{}\", \"{}\")", id, req.specs[0].path_));
136
+ REQUIRE_SUCCESS(response.ctx.ec());
137
+ REQUIRE(response.entries.size() == integration.number_of_replicas() + 1);
138
+ auto responses_from_active =
139
+ std::count_if(response.entries.begin(), response.entries.end(), [](const auto& r) { return !r.is_replica; });
140
+ REQUIRE(responses_from_active == 1);
141
+ for (auto& resp : response.entries) {
142
+ REQUIRE_FALSE(resp.cas.empty());
143
+ REQUIRE(resp.fields.size() == 1);
144
+ REQUIRE(resp.fields[0].exists);
145
+ REQUIRE(resp.fields[0].path == req.specs[0].path_);
146
+ REQUIRE(resp.fields[0].status == couchbase::key_value_status_code::success);
147
+ REQUIRE_SUCCESS(resp.fields[0].ec);
148
+ if (expected_value.has_value()) {
149
+ REQUIRE(couchbase::core::utils::to_binary(expected_value.value()) == resp.fields[0].value);
150
+ }
151
+ }
152
+ }
153
+
154
+ template<typename SubdocumentOperation>
155
+ void
156
+ assert_single_lookup_all_replica_error(test::utils::integration_test_guard& integration,
157
+ const couchbase::core::document_id& id,
158
+ const SubdocumentOperation& spec,
159
+ couchbase::key_value_status_code expected_status,
160
+ std::error_code expected_ec,
161
+ std::optional<std::string> expected_value = std::nullopt)
162
+ {
163
+ couchbase::core::operations::lookup_in_all_replicas_request req{ id };
164
+ req.specs = couchbase::lookup_in_specs{ spec }.specs();
165
+ auto response = test::utils::execute(integration.cluster, req);
166
+ INFO(fmt::format("assert_single_lookup_all_replica_error(\"{}\", \"{}\")", id, req.specs[0].path_));
167
+ REQUIRE_SUCCESS(response.ctx.ec());
168
+ REQUIRE(response.entries.size() == integration.number_of_replicas() + 1);
169
+ auto responses_from_active =
170
+ std::count_if(response.entries.begin(), response.entries.end(), [](const auto& r) { return !r.is_replica; });
171
+ REQUIRE(responses_from_active == 1);
172
+ for (auto& resp : response.entries) {
173
+ REQUIRE_FALSE(resp.cas.empty());
174
+ REQUIRE(resp.fields.size() == 1);
175
+ REQUIRE_FALSE(resp.fields[0].exists);
176
+ REQUIRE(resp.fields[0].path == req.specs[0].path_);
177
+ REQUIRE(resp.fields[0].status == expected_status);
178
+ REQUIRE(resp.fields[0].ec == expected_ec);
179
+ if (expected_value.has_value()) {
180
+ REQUIRE(couchbase::core::utils::to_binary(expected_value.value()) == resp.fields[0].value);
181
+ } else {
182
+ REQUIRE(resp.fields[0].value.empty());
183
+ }
184
+ }
66
185
  }
67
186
 
68
187
  void
@@ -112,7 +231,7 @@ TEST_CASE("integration: subdoc get & exists", "[integration]")
112
231
 
113
232
  SECTION("dict exists")
114
233
  {
115
- assert_single_lookup_success(integration, id, couchbase::lookup_in_specs::exists("dictkey"));
234
+ assert_single_lookup_success(integration, id, couchbase::lookup_in_specs::exists("dictkey"), "true");
116
235
  }
117
236
 
118
237
  SECTION("array get")
@@ -122,7 +241,7 @@ TEST_CASE("integration: subdoc get & exists", "[integration]")
122
241
 
123
242
  SECTION("array exists")
124
243
  {
125
- assert_single_lookup_success(integration, id, couchbase::lookup_in_specs::exists("array"));
244
+ assert_single_lookup_success(integration, id, couchbase::lookup_in_specs::exists("array"), "true");
126
245
  }
127
246
 
128
247
  SECTION("array index get")
@@ -132,7 +251,7 @@ TEST_CASE("integration: subdoc get & exists", "[integration]")
132
251
 
133
252
  SECTION("array index exists")
134
253
  {
135
- assert_single_lookup_success(integration, id, couchbase::lookup_in_specs::exists("array[0]"));
254
+ assert_single_lookup_success(integration, id, couchbase::lookup_in_specs::exists("array[0]"), "true");
136
255
  }
137
256
 
138
257
  SECTION("non existent path get")
@@ -150,7 +269,8 @@ TEST_CASE("integration: subdoc get & exists", "[integration]")
150
269
  id,
151
270
  couchbase::lookup_in_specs::exists("non-exist"),
152
271
  couchbase::key_value_status_code::subdoc_path_not_found,
153
- couchbase::errc::key_value::path_not_found);
272
+ std::error_code{},
273
+ "false");
154
274
  }
155
275
 
156
276
  SECTION("non existent doc")
@@ -636,7 +756,7 @@ TEST_CASE("integration: subdoc multi lookup", "[integration]")
636
756
  REQUIRE(resp.fields[0].value == couchbase::core::utils::to_binary(R"("dictval")"));
637
757
  REQUIRE(resp.fields[0].status == couchbase::key_value_status_code::success);
638
758
 
639
- REQUIRE(resp.fields[1].value.empty());
759
+ REQUIRE(resp.fields[1].value == couchbase::core::utils::to_binary("true"));
640
760
  REQUIRE(resp.fields[1].status == couchbase::key_value_status_code::success);
641
761
  REQUIRE(resp.fields[1].exists);
642
762
 
@@ -973,3 +1093,597 @@ TEST_CASE("integration: subdoc top level array", "[integration]")
973
1093
  REQUIRE(resp.fields[0].value == couchbase::core::utils::to_binary("3"));
974
1094
  }
975
1095
  }
1096
+
1097
+ TEST_CASE("integration: subdoc all replica reads", "[integration]")
1098
+ {
1099
+
1100
+ test::utils::integration_test_guard integration;
1101
+
1102
+ if (!integration.has_bucket_capability("subdoc.ReplicaRead")) {
1103
+ SKIP("bucket does not support replica_read");
1104
+ }
1105
+
1106
+ auto number_of_replicas = integration.number_of_replicas();
1107
+
1108
+ if (number_of_replicas == 0) {
1109
+ SKIP("bucket has zero replicas");
1110
+ }
1111
+ if (integration.number_of_nodes() <= number_of_replicas) {
1112
+ SKIP(fmt::format("number of nodes ({}) is less or equal to number of replicas ({})",
1113
+ integration.number_of_nodes(),
1114
+ integration.number_of_replicas()));
1115
+ }
1116
+
1117
+ auto key = test::utils::uniq_id("lookup_in_any_replica");
1118
+ couchbase::core::document_id id{ integration.ctx.bucket, "_default", "_default", key };
1119
+
1120
+ {
1121
+ auto value_json = couchbase::core::utils::to_binary(R"({"dictkey":"dictval","array":[1,2,3,4,[10,20,30,[100,200,300]]]})");
1122
+ couchbase::core::operations::insert_request req{ id, value_json };
1123
+ req.durability_level = couchbase::durability_level::majority_and_persist_to_active;
1124
+ auto resp = test::utils::execute(integration.cluster, req);
1125
+ REQUIRE_SUCCESS(resp.ctx.ec());
1126
+ }
1127
+
1128
+ SECTION("dict get")
1129
+ {
1130
+ assert_single_lookup_all_replica_success(integration, id, couchbase::lookup_in_specs::get("dictkey"), R"("dictval")");
1131
+ }
1132
+
1133
+ SECTION("dict exists")
1134
+ {
1135
+ assert_single_lookup_all_replica_success(integration, id, couchbase::lookup_in_specs::exists("dictkey"), "true");
1136
+ }
1137
+
1138
+ SECTION("array get")
1139
+ {
1140
+ assert_single_lookup_all_replica_success(
1141
+ integration, id, couchbase::lookup_in_specs::get("array"), "[1,2,3,4,[10,20,30,[100,200,300]]]");
1142
+ }
1143
+
1144
+ SECTION("array exists")
1145
+ {
1146
+ assert_single_lookup_all_replica_success(integration, id, couchbase::lookup_in_specs::exists("array"), "true");
1147
+ }
1148
+
1149
+ SECTION("array index get")
1150
+ {
1151
+ assert_single_lookup_all_replica_success(integration, id, couchbase::lookup_in_specs::get("array[0]"), "1");
1152
+ }
1153
+
1154
+ SECTION("array index exists")
1155
+ {
1156
+ assert_single_lookup_all_replica_success(integration, id, couchbase::lookup_in_specs::exists("array[0]"), "true");
1157
+ }
1158
+
1159
+ SECTION("non existent path get")
1160
+ {
1161
+ assert_single_lookup_all_replica_error(integration,
1162
+ id,
1163
+ couchbase::lookup_in_specs::get("non-exist"),
1164
+ couchbase::key_value_status_code::subdoc_path_not_found,
1165
+ couchbase::errc::key_value::path_not_found);
1166
+ }
1167
+
1168
+ SECTION("non existent path exists")
1169
+ {
1170
+ assert_single_lookup_all_replica_error(integration,
1171
+ id,
1172
+ couchbase::lookup_in_specs::exists("non-exist"),
1173
+ couchbase::key_value_status_code::subdoc_path_not_found,
1174
+ std::error_code{},
1175
+ "false");
1176
+ }
1177
+
1178
+ SECTION("non existent doc")
1179
+ {
1180
+ couchbase::core::document_id missing_id{ integration.ctx.bucket, "_default", "_default", "missing_key" };
1181
+
1182
+ SECTION("non existent doc get")
1183
+ {
1184
+ couchbase::core::operations::lookup_in_all_replicas_request req{ missing_id };
1185
+ req.specs =
1186
+ couchbase::lookup_in_specs{
1187
+ couchbase::lookup_in_specs::get("non-exist"),
1188
+ }
1189
+ .specs();
1190
+ auto resp = test::utils::execute(integration.cluster, req);
1191
+ REQUIRE(resp.ctx.ec() == couchbase::errc::key_value::document_not_found);
1192
+ REQUIRE(resp.entries.empty());
1193
+ }
1194
+
1195
+ SECTION("non existent doc exists")
1196
+ {
1197
+ couchbase::core::operations::lookup_in_all_replicas_request req{ missing_id };
1198
+ req.specs =
1199
+ couchbase::lookup_in_specs{
1200
+ couchbase::lookup_in_specs::exists("non-exist"),
1201
+ }
1202
+ .specs();
1203
+ auto resp = test::utils::execute(integration.cluster, req);
1204
+ REQUIRE(resp.ctx.ec() == couchbase::errc::key_value::document_not_found);
1205
+ REQUIRE(resp.entries.empty());
1206
+ }
1207
+ }
1208
+
1209
+ SECTION("non json")
1210
+ {
1211
+ couchbase::core::document_id non_json_id{ integration.ctx.bucket, "_default", "_default", test::utils::uniq_id("non_json") };
1212
+ auto non_json_doc = couchbase::core::utils::to_binary("string");
1213
+
1214
+ {
1215
+ couchbase::core::operations::insert_request req{ non_json_id, non_json_doc };
1216
+ auto resp = test::utils::execute(integration.cluster, req);
1217
+ REQUIRE_SUCCESS(resp.ctx.ec());
1218
+ }
1219
+
1220
+ SECTION("non json get")
1221
+ {
1222
+ if (integration.cluster_version().is_mock()) {
1223
+ SKIP("GOCAVES does not handle subdocument operations for non-JSON documents. See "
1224
+ "https://github.com/couchbaselabs/gocaves/issues/103");
1225
+ }
1226
+ assert_single_lookup_all_replica_error(integration,
1227
+ non_json_id,
1228
+ couchbase::lookup_in_specs::get("non-exist"),
1229
+ couchbase::key_value_status_code::subdoc_doc_not_json,
1230
+ couchbase::errc::key_value::document_not_json);
1231
+ }
1232
+
1233
+ SECTION("non json exists")
1234
+ {
1235
+ if (integration.cluster_version().is_mock()) {
1236
+ SKIP("GOCAVES does not handle subdocument operations for non-JSON documents. See "
1237
+ "https://github.com/couchbaselabs/gocaves/issues/103");
1238
+ }
1239
+ assert_single_lookup_all_replica_error(integration,
1240
+ non_json_id,
1241
+ couchbase::lookup_in_specs::exists("non-exist"),
1242
+ couchbase::key_value_status_code::subdoc_doc_not_json,
1243
+ couchbase::errc::key_value::document_not_json);
1244
+ }
1245
+ }
1246
+
1247
+ SECTION("invalid path")
1248
+ {
1249
+ std::vector<std::string> invalid_paths = { "invalid..path", "invalid[-2]" };
1250
+ for (const auto& path : invalid_paths) {
1251
+ if (integration.cluster_version().is_mock()) {
1252
+ assert_single_lookup_all_replica_error(integration,
1253
+ id,
1254
+ couchbase::lookup_in_specs::get(path),
1255
+ couchbase::key_value_status_code::subdoc_path_not_found,
1256
+ couchbase::errc::key_value::path_not_found);
1257
+ } else {
1258
+ assert_single_lookup_all_replica_error(integration,
1259
+ id,
1260
+ couchbase::lookup_in_specs::get(path),
1261
+ couchbase::key_value_status_code::subdoc_path_invalid,
1262
+ couchbase::errc::key_value::path_invalid);
1263
+ }
1264
+ }
1265
+ }
1266
+
1267
+ SECTION("negative paths")
1268
+ {
1269
+ assert_single_lookup_all_replica_success(integration, id, couchbase::lookup_in_specs::get("array[-1][-1][-1]"), "300");
1270
+ }
1271
+
1272
+ SECTION("nested arrays")
1273
+ {
1274
+ assert_single_lookup_all_replica_success(integration, id, couchbase::lookup_in_specs::get("array[4][3][2]"), "300");
1275
+ }
1276
+
1277
+ SECTION("path mismatch")
1278
+ {
1279
+ assert_single_lookup_all_replica_error(integration,
1280
+ id,
1281
+ couchbase::lookup_in_specs::get("array.key"),
1282
+ couchbase::key_value_status_code::subdoc_path_mismatch,
1283
+ couchbase::errc::key_value::path_mismatch);
1284
+ }
1285
+
1286
+ SECTION("public API")
1287
+ {
1288
+ auto collection = couchbase::cluster(integration.cluster).bucket(integration.ctx.bucket).scope("_default").collection("_default");
1289
+
1290
+ SECTION("lookup in all replicas")
1291
+ {
1292
+ auto specs = couchbase::lookup_in_specs{ couchbase::lookup_in_specs::get("dictkey"),
1293
+ couchbase::lookup_in_specs::exists("array"),
1294
+ couchbase::lookup_in_specs::count("array") };
1295
+ auto [ctx, result] = collection.lookup_in_all_replicas(key, specs).get();
1296
+ REQUIRE_SUCCESS(ctx.ec());
1297
+ REQUIRE(result.size() == number_of_replicas + 1);
1298
+ auto responses_from_active = std::count_if(result.begin(), result.end(), [](const auto& r) { return !r.is_replica(); });
1299
+ REQUIRE(responses_from_active == 1);
1300
+ for (auto& res : result) {
1301
+ REQUIRE(!res.cas().empty());
1302
+ REQUIRE("dictval" == res.content_as<std::string>(0));
1303
+ REQUIRE(res.exists("array"));
1304
+ REQUIRE(res.content_as<bool>(1));
1305
+ REQUIRE(5 == res.content_as<int>(2));
1306
+ }
1307
+ }
1308
+
1309
+ SECTION("missing document")
1310
+ {
1311
+ auto specs = couchbase::lookup_in_specs{
1312
+ couchbase::lookup_in_specs::get("non-exists"),
1313
+ };
1314
+ auto [ctx, result] = collection.lookup_in_all_replicas("missing-key", specs).get();
1315
+ REQUIRE(ctx.ec() == couchbase::errc::key_value::document_not_found);
1316
+ REQUIRE(result.empty());
1317
+ }
1318
+
1319
+ SECTION("non existent path exists")
1320
+ {
1321
+ auto specs = couchbase::lookup_in_specs{
1322
+ couchbase::lookup_in_specs::exists("non-exists"),
1323
+ };
1324
+ auto [ctx, result] = collection.lookup_in_all_replicas(key, specs).get();
1325
+ REQUIRE_SUCCESS(ctx.ec());
1326
+ for (auto& res : result) {
1327
+ REQUIRE(!res.cas().empty());
1328
+ REQUIRE(!res.exists(0));
1329
+ REQUIRE(!res.content_as<bool>(0));
1330
+ }
1331
+ }
1332
+ }
1333
+ }
1334
+
1335
+ TEST_CASE("integration: subdoc any replica reads", "[integration]")
1336
+ {
1337
+ test::utils::integration_test_guard integration;
1338
+
1339
+ if (!integration.has_bucket_capability("subdoc.ReplicaRead")) {
1340
+ SKIP("bucket does not support replica_read");
1341
+ }
1342
+
1343
+ auto number_of_replicas = integration.number_of_replicas();
1344
+
1345
+ if (number_of_replicas == 0) {
1346
+ SKIP("bucket has zero replicas");
1347
+ }
1348
+ if (integration.number_of_nodes() <= number_of_replicas) {
1349
+ SKIP(fmt::format("number of nodes ({}) is less or equal to number of replicas ({})",
1350
+ integration.number_of_nodes(),
1351
+ integration.number_of_replicas()));
1352
+ }
1353
+
1354
+ test::utils::open_bucket(integration.cluster, integration.ctx.bucket);
1355
+
1356
+ auto key = test::utils::uniq_id("lookup_in_any_replica");
1357
+ couchbase::core::document_id id{ integration.ctx.bucket, "_default", "_default", key };
1358
+
1359
+ {
1360
+ auto value_json = couchbase::core::utils::to_binary(R"({"dictkey":"dictval","array":[1,2,3,4,[10,20,30,[100,200,300]]]})");
1361
+ couchbase::core::operations::insert_request req{ id, value_json };
1362
+ req.durability_level = couchbase::durability_level::majority_and_persist_to_active;
1363
+ auto resp = test::utils::execute(integration.cluster, req);
1364
+ REQUIRE_SUCCESS(resp.ctx.ec());
1365
+ }
1366
+
1367
+ SECTION("dict get")
1368
+ {
1369
+ assert_single_lookup_any_replica_success(integration, id, couchbase::lookup_in_specs::get("dictkey"), R"("dictval")");
1370
+ }
1371
+
1372
+ SECTION("dict exists")
1373
+ {
1374
+ assert_single_lookup_any_replica_success(integration, id, couchbase::lookup_in_specs::exists("dictkey"), "true");
1375
+ }
1376
+
1377
+ SECTION("array get")
1378
+ {
1379
+ assert_single_lookup_any_replica_success(
1380
+ integration, id, couchbase::lookup_in_specs::get("array"), "[1,2,3,4,[10,20,30,[100,200,300]]]");
1381
+ }
1382
+
1383
+ SECTION("array exists")
1384
+ {
1385
+ assert_single_lookup_any_replica_success(integration, id, couchbase::lookup_in_specs::exists("array"), "true");
1386
+ }
1387
+
1388
+ SECTION("array index get")
1389
+ {
1390
+ assert_single_lookup_any_replica_success(integration, id, couchbase::lookup_in_specs::get("array[0]"), "1");
1391
+ }
1392
+
1393
+ SECTION("array index exists")
1394
+ {
1395
+ assert_single_lookup_any_replica_success(integration, id, couchbase::lookup_in_specs::exists("array[0]"), "true");
1396
+ }
1397
+
1398
+ SECTION("non existent path get")
1399
+ {
1400
+ assert_single_lookup_any_replica_error(integration,
1401
+ id,
1402
+ couchbase::lookup_in_specs::get("non-exist"),
1403
+ couchbase::key_value_status_code::subdoc_path_not_found,
1404
+ couchbase::errc::key_value::path_not_found);
1405
+ }
1406
+
1407
+ SECTION("non existent path exists")
1408
+ {
1409
+ assert_single_lookup_any_replica_error(integration,
1410
+ id,
1411
+ couchbase::lookup_in_specs::exists("non-exist"),
1412
+ couchbase::key_value_status_code::subdoc_path_not_found,
1413
+ std::error_code{},
1414
+ "false");
1415
+ }
1416
+
1417
+ SECTION("non existent doc")
1418
+ {
1419
+ couchbase::core::document_id missing_id{ integration.ctx.bucket, "_default", "_default", "missing_key" };
1420
+
1421
+ SECTION("non existent doc get")
1422
+ {
1423
+ couchbase::core::operations::lookup_in_any_replica_request req{ missing_id };
1424
+ req.specs =
1425
+ couchbase::lookup_in_specs{
1426
+ couchbase::lookup_in_specs::get("non-exist"),
1427
+ }
1428
+ .specs();
1429
+ auto resp = test::utils::execute(integration.cluster, req);
1430
+ REQUIRE(resp.ctx.ec() == couchbase::errc::key_value::document_irretrievable);
1431
+ REQUIRE(resp.fields.empty());
1432
+ }
1433
+
1434
+ SECTION("non existent doc exists")
1435
+ {
1436
+ couchbase::core::operations::lookup_in_any_replica_request req{ missing_id };
1437
+ req.specs =
1438
+ couchbase::lookup_in_specs{
1439
+ couchbase::lookup_in_specs::exists("non-exist"),
1440
+ }
1441
+ .specs();
1442
+ auto resp = test::utils::execute(integration.cluster, req);
1443
+ REQUIRE(resp.ctx.ec() == couchbase::errc::key_value::document_irretrievable);
1444
+ REQUIRE(resp.fields.empty());
1445
+ }
1446
+ }
1447
+
1448
+ SECTION("non json")
1449
+ {
1450
+ couchbase::core::document_id non_json_id{ integration.ctx.bucket, "_default", "_default", test::utils::uniq_id("non_json") };
1451
+ auto non_json_doc = couchbase::core::utils::to_binary("string");
1452
+
1453
+ {
1454
+ couchbase::core::operations::insert_request req{ non_json_id, non_json_doc };
1455
+ auto resp = test::utils::execute(integration.cluster, req);
1456
+ REQUIRE_SUCCESS(resp.ctx.ec());
1457
+ }
1458
+
1459
+ SECTION("non json get")
1460
+ {
1461
+ if (integration.cluster_version().is_mock()) {
1462
+ SKIP("GOCAVES does not handle subdocument operations for non-JSON documents. See "
1463
+ "https://github.com/couchbaselabs/gocaves/issues/103");
1464
+ }
1465
+ assert_single_lookup_any_replica_error(integration,
1466
+ non_json_id,
1467
+ couchbase::lookup_in_specs::get("non-exist"),
1468
+ couchbase::key_value_status_code::subdoc_doc_not_json,
1469
+ couchbase::errc::key_value::document_not_json);
1470
+ }
1471
+
1472
+ SECTION("non json exists")
1473
+ {
1474
+ if (integration.cluster_version().is_mock()) {
1475
+ SKIP("GOCAVES does not handle subdocument operations for non-JSON documents. See "
1476
+ "https://github.com/couchbaselabs/gocaves/issues/103");
1477
+ }
1478
+ assert_single_lookup_any_replica_error(integration,
1479
+ non_json_id,
1480
+ couchbase::lookup_in_specs::exists("non-exist"),
1481
+ couchbase::key_value_status_code::subdoc_doc_not_json,
1482
+ couchbase::errc::key_value::document_not_json);
1483
+ }
1484
+ }
1485
+
1486
+ SECTION("invalid path")
1487
+ {
1488
+ std::vector<std::string> invalid_paths = { "invalid..path", "invalid[-2]" };
1489
+ for (const auto& path : invalid_paths) {
1490
+ if (integration.cluster_version().is_mock()) {
1491
+ assert_single_lookup_any_replica_error(integration,
1492
+ id,
1493
+ couchbase::lookup_in_specs::get(path),
1494
+ couchbase::key_value_status_code::subdoc_path_not_found,
1495
+ couchbase::errc::key_value::path_not_found);
1496
+ } else {
1497
+ assert_single_lookup_any_replica_error(integration,
1498
+ id,
1499
+ couchbase::lookup_in_specs::get(path),
1500
+ couchbase::key_value_status_code::subdoc_path_invalid,
1501
+ couchbase::errc::key_value::path_invalid);
1502
+ }
1503
+ }
1504
+ }
1505
+
1506
+ SECTION("negative paths")
1507
+ {
1508
+ assert_single_lookup_any_replica_success(integration, id, couchbase::lookup_in_specs::get("array[-1][-1][-1]"), "300");
1509
+ }
1510
+
1511
+ SECTION("nested arrays")
1512
+ {
1513
+ assert_single_lookup_any_replica_success(integration, id, couchbase::lookup_in_specs::get("array[4][3][2]"), "300");
1514
+ }
1515
+
1516
+ SECTION("path mismatch")
1517
+ {
1518
+ assert_single_lookup_any_replica_error(integration,
1519
+ id,
1520
+ couchbase::lookup_in_specs::get("array.key"),
1521
+ couchbase::key_value_status_code::subdoc_path_mismatch,
1522
+ couchbase::errc::key_value::path_mismatch);
1523
+ }
1524
+
1525
+ SECTION("too many specs")
1526
+ {
1527
+ couchbase::core::operations::lookup_in_any_replica_request req{ id };
1528
+ auto specs = couchbase::lookup_in_specs{};
1529
+
1530
+ for (int i = 0; i < 17; i++) {
1531
+ specs.push_back(couchbase::lookup_in_specs::get("dictkey"));
1532
+ }
1533
+ req.specs = specs.specs();
1534
+
1535
+ auto resp = test::utils::execute(integration.cluster, req);
1536
+ REQUIRE(resp.ctx.ec() == couchbase::errc::common::invalid_argument);
1537
+ REQUIRE(resp.fields.empty());
1538
+ }
1539
+
1540
+ SECTION("public API")
1541
+ {
1542
+ auto collection = couchbase::cluster(integration.cluster).bucket(integration.ctx.bucket).scope("_default").collection("_default");
1543
+
1544
+ SECTION("lookup in any replica")
1545
+ {
1546
+ auto specs = couchbase::lookup_in_specs{ couchbase::lookup_in_specs::get("dictkey"),
1547
+ couchbase::lookup_in_specs::exists("array"),
1548
+ couchbase::lookup_in_specs::count("array") };
1549
+ auto [ctx, result] = collection.lookup_in_any_replica(key, specs).get();
1550
+ REQUIRE_SUCCESS(ctx.ec());
1551
+ REQUIRE(!result.cas().empty());
1552
+ REQUIRE("dictval" == result.content_as<std::string>(0));
1553
+ REQUIRE(result.exists("array"));
1554
+ REQUIRE(result.content_as<bool>(1));
1555
+ REQUIRE(5 == result.content_as<int>(2));
1556
+ }
1557
+
1558
+ SECTION("missing document")
1559
+ {
1560
+ auto specs = couchbase::lookup_in_specs{
1561
+ couchbase::lookup_in_specs::get("non-exists"),
1562
+ };
1563
+ auto [ctx, result] = collection.lookup_in_any_replica("missing-key", specs).get();
1564
+ REQUIRE(ctx.ec() == couchbase::errc::key_value::document_irretrievable);
1565
+ REQUIRE(result.cas().empty());
1566
+ }
1567
+
1568
+ SECTION("too many specs")
1569
+ {
1570
+ auto specs = couchbase::lookup_in_specs{};
1571
+
1572
+ for (int i = 0; i < 17; i++) {
1573
+ specs.push_back(couchbase::lookup_in_specs::get("dictkey"));
1574
+ }
1575
+ auto [ctx, result] = collection.lookup_in_any_replica(key, specs).get();
1576
+ REQUIRE(ctx.ec() == couchbase::errc::common::invalid_argument);
1577
+ REQUIRE(result.cas().empty());
1578
+ }
1579
+
1580
+ SECTION("non existent path exists")
1581
+ {
1582
+ auto specs = couchbase::lookup_in_specs{
1583
+ couchbase::lookup_in_specs::exists("non-exists"),
1584
+ };
1585
+ auto [ctx, result] = collection.lookup_in_any_replica(key, specs).get();
1586
+ REQUIRE_SUCCESS(ctx.ec());
1587
+ REQUIRE(!result.exists(0));
1588
+ REQUIRE(!result.content_as<bool>(0));
1589
+ }
1590
+ }
1591
+ }
1592
+
1593
+ TEST_CASE("integration: public API lookup in per-spec errors", "[integration]")
1594
+ {
1595
+ test::utils::integration_test_guard integration;
1596
+
1597
+ auto collection = couchbase::cluster(integration.cluster).bucket(integration.ctx.bucket).scope("_default").collection("_default");
1598
+
1599
+ auto key = test::utils::uniq_id("lookup_in_path_invalid");
1600
+ {
1601
+ auto value_json = couchbase::core::utils::json::parse(R"({"dictkey":"dictval","array":[1,2,3,4,[10,20,30,[100,200,300]]]})");
1602
+ auto [ctx, result] = collection.upsert(key, value_json).get();
1603
+ REQUIRE_SUCCESS(ctx.ec());
1604
+ }
1605
+
1606
+ SECTION("path invalid")
1607
+ {
1608
+ auto specs = couchbase::lookup_in_specs{
1609
+ couchbase::lookup_in_specs::get("..dictkey"),
1610
+ };
1611
+ auto [ctx, result] = collection.lookup_in(key, specs).get();
1612
+ std::error_code ec{};
1613
+ try {
1614
+ std::ignore = result.content_as<std::string>(0);
1615
+ } catch (std::system_error& exc) {
1616
+ ec = exc.code();
1617
+ }
1618
+ REQUIRE(ec == couchbase::errc::key_value::path_invalid);
1619
+
1620
+ ec.clear();
1621
+ try {
1622
+ std::ignore = result.exists(0);
1623
+ } catch (std::system_error& exc) {
1624
+ ec = exc.code();
1625
+ }
1626
+ REQUIRE(ec == couchbase::errc::key_value::path_invalid);
1627
+ }
1628
+
1629
+ SECTION("path mismatch")
1630
+ {
1631
+ auto specs = couchbase::lookup_in_specs{
1632
+ couchbase::lookup_in_specs::count("dictkey"),
1633
+ };
1634
+ auto [ctx, result] = collection.lookup_in(key, specs).get();
1635
+
1636
+ std::error_code ec{};
1637
+ try {
1638
+ std::ignore = result.content_as<std::string>(0);
1639
+ } catch (std::system_error& exc) {
1640
+ ec = exc.code();
1641
+ }
1642
+ REQUIRE(ec == couchbase::errc::key_value::path_mismatch);
1643
+
1644
+ ec.clear();
1645
+ try {
1646
+ std::ignore = result.exists(0);
1647
+ } catch (std::system_error& exc) {
1648
+ ec = exc.code();
1649
+ }
1650
+ REQUIRE(ec == couchbase::errc::key_value::path_mismatch);
1651
+ }
1652
+
1653
+ SECTION("path not found")
1654
+ {
1655
+ auto specs = couchbase::lookup_in_specs{
1656
+ couchbase::lookup_in_specs::get("dictkey2"),
1657
+ };
1658
+ auto [ctx, result] = collection.lookup_in(key, specs).get();
1659
+
1660
+ std::error_code ec{};
1661
+ try {
1662
+ std::ignore = result.content_as<std::string>(0);
1663
+ } catch (std::system_error& exc) {
1664
+ ec = exc.code();
1665
+ }
1666
+ REQUIRE(ec == couchbase::errc::key_value::path_not_found);
1667
+
1668
+ ec.clear();
1669
+ try {
1670
+ auto exists = result.exists(0);
1671
+ REQUIRE_FALSE(exists);
1672
+ } catch (std::system_error& exc) {
1673
+ ec = exc.code();
1674
+ }
1675
+ REQUIRE_SUCCESS(ec);
1676
+ }
1677
+
1678
+ SECTION("non existent path exists")
1679
+ {
1680
+ auto specs = couchbase::lookup_in_specs{
1681
+ couchbase::lookup_in_specs::exists("dictkey2"),
1682
+ };
1683
+ auto [ctx, result] = collection.lookup_in(key, specs).get();
1684
+
1685
+ REQUIRE_SUCCESS(ctx.ec());
1686
+ REQUIRE(!result.exists(0));
1687
+ REQUIRE(!result.content_as<bool>(0));
1688
+ }
1689
+ }