couchbase 3.4.3 → 3.4.5

Sign up to get free protection for your applications and to get access to all the features.
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
+ }