couchbase 3.4.4 → 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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/ext/couchbase/CMakeLists.txt +7 -0
  4. data/ext/couchbase/core/cluster.hxx +7 -0
  5. data/ext/couchbase/core/impl/create_bucket.cxx +3 -0
  6. data/ext/couchbase/core/impl/create_collection.cxx +83 -0
  7. data/ext/couchbase/core/impl/create_scope.cxx +69 -0
  8. data/ext/couchbase/core/impl/drop_collection.cxx +76 -0
  9. data/ext/couchbase/core/impl/drop_scope.cxx +68 -0
  10. data/ext/couchbase/core/impl/get_all_buckets.cxx +19 -4
  11. data/ext/couchbase/core/impl/get_all_scopes.cxx +94 -0
  12. data/ext/couchbase/core/impl/get_bucket.cxx +19 -4
  13. data/ext/couchbase/core/impl/lookup_in_all_replicas.cxx +2 -0
  14. data/ext/couchbase/core/impl/lookup_in_any_replica.cxx +2 -0
  15. data/ext/couchbase/core/impl/lookup_in_replica.cxx +8 -1
  16. data/ext/couchbase/core/impl/update_bucket.cxx +3 -0
  17. data/ext/couchbase/core/impl/update_collection.cxx +83 -0
  18. data/ext/couchbase/core/management/bucket_settings.hxx +8 -5
  19. data/ext/couchbase/core/management/bucket_settings_json.hxx +12 -2
  20. data/ext/couchbase/core/meta/features.hxx +17 -0
  21. data/ext/couchbase/core/operations/document_lookup_in.cxx +8 -1
  22. data/ext/couchbase/core/operations/management/CMakeLists.txt +1 -0
  23. data/ext/couchbase/core/operations/management/bucket_create.cxx +30 -9
  24. data/ext/couchbase/core/operations/management/bucket_update.cxx +27 -6
  25. data/ext/couchbase/core/operations/management/collection_create.cxx +5 -1
  26. data/ext/couchbase/core/operations/management/collection_create.hxx +1 -0
  27. data/ext/couchbase/core/operations/management/collection_update.cxx +87 -0
  28. data/ext/couchbase/core/operations/management/collection_update.hxx +54 -0
  29. data/ext/couchbase/core/operations/management/collections.hxx +1 -0
  30. data/ext/couchbase/core/timeout_defaults.hxx +1 -1
  31. data/ext/couchbase/core/topology/capabilities.hxx +1 -0
  32. data/ext/couchbase/core/topology/capabilities_fmt.hxx +3 -0
  33. data/ext/couchbase/core/topology/collections_manifest.hxx +2 -0
  34. data/ext/couchbase/core/topology/collections_manifest_json.hxx +3 -0
  35. data/ext/couchbase/core/topology/configuration.hxx +5 -0
  36. data/ext/couchbase/core/topology/configuration_json.hxx +2 -0
  37. data/ext/couchbase/couchbase/bucket.hxx +14 -0
  38. data/ext/couchbase/couchbase/collection_manager.hxx +160 -0
  39. data/ext/couchbase/couchbase/create_collection_options.hxx +44 -0
  40. data/ext/couchbase/couchbase/create_scope_options.hxx +41 -0
  41. data/ext/couchbase/couchbase/drop_collection_options.hxx +41 -0
  42. data/ext/couchbase/couchbase/drop_scope_options.hxx +41 -0
  43. data/ext/couchbase/couchbase/get_all_scopes_options.hxx +44 -0
  44. data/ext/couchbase/couchbase/management/bucket_settings.hxx +8 -5
  45. data/ext/couchbase/couchbase/management/collection_spec.hxx +29 -0
  46. data/ext/couchbase/couchbase/management/scope_spec.hxx +29 -0
  47. data/ext/couchbase/couchbase/update_collection_options.hxx +44 -0
  48. data/ext/couchbase/test/test_integration_management.cxx +305 -48
  49. data/ext/couchbase/test/test_integration_subdoc.cxx +81 -22
  50. data/ext/couchbase.cxx +155 -34
  51. data/ext/revisions.rb +3 -3
  52. data/lib/couchbase/collection_options.rb +1 -2
  53. data/lib/couchbase/management/bucket_manager.rb +22 -15
  54. data/lib/couchbase/management/collection_manager.rb +158 -9
  55. data/lib/couchbase/version.rb +1 -1
  56. metadata +23 -6
@@ -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,13 @@ 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
+ }
66
71
  }
67
72
 
68
73
  template<typename SubdocumentOperation>
@@ -85,6 +90,8 @@ assert_single_lookup_any_replica_success(test::utils::integration_test_guard& in
85
90
  REQUIRE_SUCCESS(resp.fields[0].ec);
86
91
  if (expected_value.has_value()) {
87
92
  REQUIRE(couchbase::core::utils::to_binary(expected_value.value()) == resp.fields[0].value);
93
+ } else {
94
+ REQUIRE(resp.fields[0].value.empty());
88
95
  }
89
96
  }
90
97
 
@@ -94,20 +101,25 @@ assert_single_lookup_any_replica_error(test::utils::integration_test_guard& inte
94
101
  const couchbase::core::document_id& id,
95
102
  const SubdocumentOperation& spec,
96
103
  couchbase::key_value_status_code expected_status,
97
- std::error_code expected_ec)
104
+ std::error_code expected_ec,
105
+ std::optional<std::string> expected_value = std::nullopt)
98
106
  {
99
107
  couchbase::core::operations::lookup_in_any_replica_request req{ id };
100
108
  req.specs = couchbase::lookup_in_specs{ spec }.specs();
101
109
  auto resp = test::utils::execute(integration.cluster, req);
102
- INFO(fmt::format("assert_single_lookup_all_replica_error(\"{}\", \"{}\")", id, req.specs[0].path_));
110
+ INFO(fmt::format("assert_single_lookup_any_replica_error(\"{}\", \"{}\")", id, req.specs[0].path_));
103
111
  REQUIRE_SUCCESS(resp.ctx.ec());
104
112
  REQUIRE_FALSE(resp.cas.empty());
105
113
  REQUIRE(resp.fields.size() == 1);
106
114
  REQUIRE_FALSE(resp.fields[0].exists);
107
115
  REQUIRE(resp.fields[0].path == req.specs[0].path_);
108
- REQUIRE(resp.fields[0].value.empty());
109
116
  REQUIRE(resp.fields[0].status == expected_status);
110
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
+ }
111
123
  }
112
124
 
113
125
  template<typename SubdocumentOperation>
@@ -145,7 +157,8 @@ assert_single_lookup_all_replica_error(test::utils::integration_test_guard& inte
145
157
  const couchbase::core::document_id& id,
146
158
  const SubdocumentOperation& spec,
147
159
  couchbase::key_value_status_code expected_status,
148
- std::error_code expected_ec)
160
+ std::error_code expected_ec,
161
+ std::optional<std::string> expected_value = std::nullopt)
149
162
  {
150
163
  couchbase::core::operations::lookup_in_all_replicas_request req{ id };
151
164
  req.specs = couchbase::lookup_in_specs{ spec }.specs();
@@ -161,9 +174,13 @@ assert_single_lookup_all_replica_error(test::utils::integration_test_guard& inte
161
174
  REQUIRE(resp.fields.size() == 1);
162
175
  REQUIRE_FALSE(resp.fields[0].exists);
163
176
  REQUIRE(resp.fields[0].path == req.specs[0].path_);
164
- REQUIRE(resp.fields[0].value.empty());
165
177
  REQUIRE(resp.fields[0].status == expected_status);
166
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
+ }
167
184
  }
168
185
  }
169
186
 
@@ -214,7 +231,7 @@ TEST_CASE("integration: subdoc get & exists", "[integration]")
214
231
 
215
232
  SECTION("dict exists")
216
233
  {
217
- 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");
218
235
  }
219
236
 
220
237
  SECTION("array get")
@@ -224,7 +241,7 @@ TEST_CASE("integration: subdoc get & exists", "[integration]")
224
241
 
225
242
  SECTION("array exists")
226
243
  {
227
- 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");
228
245
  }
229
246
 
230
247
  SECTION("array index get")
@@ -234,7 +251,7 @@ TEST_CASE("integration: subdoc get & exists", "[integration]")
234
251
 
235
252
  SECTION("array index exists")
236
253
  {
237
- 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");
238
255
  }
239
256
 
240
257
  SECTION("non existent path get")
@@ -252,7 +269,8 @@ TEST_CASE("integration: subdoc get & exists", "[integration]")
252
269
  id,
253
270
  couchbase::lookup_in_specs::exists("non-exist"),
254
271
  couchbase::key_value_status_code::subdoc_path_not_found,
255
- couchbase::errc::key_value::path_not_found);
272
+ std::error_code{},
273
+ "false");
256
274
  }
257
275
 
258
276
  SECTION("non existent doc")
@@ -738,7 +756,7 @@ TEST_CASE("integration: subdoc multi lookup", "[integration]")
738
756
  REQUIRE(resp.fields[0].value == couchbase::core::utils::to_binary(R"("dictval")"));
739
757
  REQUIRE(resp.fields[0].status == couchbase::key_value_status_code::success);
740
758
 
741
- REQUIRE(resp.fields[1].value.empty());
759
+ REQUIRE(resp.fields[1].value == couchbase::core::utils::to_binary("true"));
742
760
  REQUIRE(resp.fields[1].status == couchbase::key_value_status_code::success);
743
761
  REQUIRE(resp.fields[1].exists);
744
762
 
@@ -1082,7 +1100,7 @@ TEST_CASE("integration: subdoc all replica reads", "[integration]")
1082
1100
  test::utils::integration_test_guard integration;
1083
1101
 
1084
1102
  if (!integration.has_bucket_capability("subdoc.ReplicaRead")) {
1085
- SKIP("cluster does not support replica_read");
1103
+ SKIP("bucket does not support replica_read");
1086
1104
  }
1087
1105
 
1088
1106
  auto number_of_replicas = integration.number_of_replicas();
@@ -1114,7 +1132,7 @@ TEST_CASE("integration: subdoc all replica reads", "[integration]")
1114
1132
 
1115
1133
  SECTION("dict exists")
1116
1134
  {
1117
- assert_single_lookup_all_replica_success(integration, id, couchbase::lookup_in_specs::exists("dictkey"));
1135
+ assert_single_lookup_all_replica_success(integration, id, couchbase::lookup_in_specs::exists("dictkey"), "true");
1118
1136
  }
1119
1137
 
1120
1138
  SECTION("array get")
@@ -1125,7 +1143,7 @@ TEST_CASE("integration: subdoc all replica reads", "[integration]")
1125
1143
 
1126
1144
  SECTION("array exists")
1127
1145
  {
1128
- assert_single_lookup_all_replica_success(integration, id, couchbase::lookup_in_specs::exists("array"));
1146
+ assert_single_lookup_all_replica_success(integration, id, couchbase::lookup_in_specs::exists("array"), "true");
1129
1147
  }
1130
1148
 
1131
1149
  SECTION("array index get")
@@ -1135,7 +1153,7 @@ TEST_CASE("integration: subdoc all replica reads", "[integration]")
1135
1153
 
1136
1154
  SECTION("array index exists")
1137
1155
  {
1138
- assert_single_lookup_all_replica_success(integration, id, couchbase::lookup_in_specs::exists("array[0]"));
1156
+ assert_single_lookup_all_replica_success(integration, id, couchbase::lookup_in_specs::exists("array[0]"), "true");
1139
1157
  }
1140
1158
 
1141
1159
  SECTION("non existent path get")
@@ -1153,7 +1171,8 @@ TEST_CASE("integration: subdoc all replica reads", "[integration]")
1153
1171
  id,
1154
1172
  couchbase::lookup_in_specs::exists("non-exist"),
1155
1173
  couchbase::key_value_status_code::subdoc_path_not_found,
1156
- couchbase::errc::key_value::path_not_found);
1174
+ std::error_code{},
1175
+ "false");
1157
1176
  }
1158
1177
 
1159
1178
  SECTION("non existent doc")
@@ -1282,6 +1301,7 @@ TEST_CASE("integration: subdoc all replica reads", "[integration]")
1282
1301
  REQUIRE(!res.cas().empty());
1283
1302
  REQUIRE("dictval" == res.content_as<std::string>(0));
1284
1303
  REQUIRE(res.exists("array"));
1304
+ REQUIRE(res.content_as<bool>(1));
1285
1305
  REQUIRE(5 == res.content_as<int>(2));
1286
1306
  }
1287
1307
  }
@@ -1295,6 +1315,20 @@ TEST_CASE("integration: subdoc all replica reads", "[integration]")
1295
1315
  REQUIRE(ctx.ec() == couchbase::errc::key_value::document_not_found);
1296
1316
  REQUIRE(result.empty());
1297
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
+ }
1298
1332
  }
1299
1333
  }
1300
1334
 
@@ -1303,7 +1337,7 @@ TEST_CASE("integration: subdoc any replica reads", "[integration]")
1303
1337
  test::utils::integration_test_guard integration;
1304
1338
 
1305
1339
  if (!integration.has_bucket_capability("subdoc.ReplicaRead")) {
1306
- SKIP("cluster does not support replica_read");
1340
+ SKIP("bucket does not support replica_read");
1307
1341
  }
1308
1342
 
1309
1343
  auto number_of_replicas = integration.number_of_replicas();
@@ -1337,7 +1371,7 @@ TEST_CASE("integration: subdoc any replica reads", "[integration]")
1337
1371
 
1338
1372
  SECTION("dict exists")
1339
1373
  {
1340
- assert_single_lookup_any_replica_success(integration, id, couchbase::lookup_in_specs::exists("dictkey"));
1374
+ assert_single_lookup_any_replica_success(integration, id, couchbase::lookup_in_specs::exists("dictkey"), "true");
1341
1375
  }
1342
1376
 
1343
1377
  SECTION("array get")
@@ -1348,7 +1382,7 @@ TEST_CASE("integration: subdoc any replica reads", "[integration]")
1348
1382
 
1349
1383
  SECTION("array exists")
1350
1384
  {
1351
- assert_single_lookup_any_replica_success(integration, id, couchbase::lookup_in_specs::exists("array"));
1385
+ assert_single_lookup_any_replica_success(integration, id, couchbase::lookup_in_specs::exists("array"), "true");
1352
1386
  }
1353
1387
 
1354
1388
  SECTION("array index get")
@@ -1358,7 +1392,7 @@ TEST_CASE("integration: subdoc any replica reads", "[integration]")
1358
1392
 
1359
1393
  SECTION("array index exists")
1360
1394
  {
1361
- assert_single_lookup_any_replica_success(integration, id, couchbase::lookup_in_specs::exists("array[0]"));
1395
+ assert_single_lookup_any_replica_success(integration, id, couchbase::lookup_in_specs::exists("array[0]"), "true");
1362
1396
  }
1363
1397
 
1364
1398
  SECTION("non existent path get")
@@ -1376,7 +1410,8 @@ TEST_CASE("integration: subdoc any replica reads", "[integration]")
1376
1410
  id,
1377
1411
  couchbase::lookup_in_specs::exists("non-exist"),
1378
1412
  couchbase::key_value_status_code::subdoc_path_not_found,
1379
- couchbase::errc::key_value::path_not_found);
1413
+ std::error_code{},
1414
+ "false");
1380
1415
  }
1381
1416
 
1382
1417
  SECTION("non existent doc")
@@ -1516,6 +1551,7 @@ TEST_CASE("integration: subdoc any replica reads", "[integration]")
1516
1551
  REQUIRE(!result.cas().empty());
1517
1552
  REQUIRE("dictval" == result.content_as<std::string>(0));
1518
1553
  REQUIRE(result.exists("array"));
1554
+ REQUIRE(result.content_as<bool>(1));
1519
1555
  REQUIRE(5 == result.content_as<int>(2));
1520
1556
  }
1521
1557
 
@@ -1540,6 +1576,17 @@ TEST_CASE("integration: subdoc any replica reads", "[integration]")
1540
1576
  REQUIRE(ctx.ec() == couchbase::errc::common::invalid_argument);
1541
1577
  REQUIRE(result.cas().empty());
1542
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
+ }
1543
1590
  }
1544
1591
  }
1545
1592
 
@@ -1627,4 +1674,16 @@ TEST_CASE("integration: public API lookup in per-spec errors", "[integration]")
1627
1674
  }
1628
1675
  REQUIRE_SUCCESS(ec);
1629
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
+ }
1630
1689
  }
data/ext/couchbase.cxx CHANGED
@@ -1013,16 +1013,14 @@ cb_map_error_code(const couchbase::key_value_error_context& ctx, const std::stri
1013
1013
  rb_hash_aset(enhanced_error_info, rb_id2sym(rb_intern("context")), cb_str_new(ctx.extended_error_info()->context()));
1014
1014
  rb_hash_aset(error_context, rb_id2sym(rb_intern("extended_error_info")), enhanced_error_info);
1015
1015
  }
1016
- if (ctx.retry_attempts() > 0) {
1017
- rb_hash_aset(error_context, rb_id2sym(rb_intern("retry_attempts")), INT2FIX(ctx.retry_attempts()));
1018
- if (!ctx.retry_reasons().empty()) {
1019
- VALUE retry_reasons = rb_ary_new_capa(static_cast<long>(ctx.retry_reasons().size()));
1020
- for (const auto& reason : ctx.retry_reasons()) {
1021
- auto reason_str = fmt::format("{}", reason);
1022
- rb_ary_push(retry_reasons, rb_id2sym(rb_intern(reason_str.c_str())));
1023
- }
1024
- rb_hash_aset(error_context, rb_id2sym(rb_intern("retry_reasons")), retry_reasons);
1016
+ rb_hash_aset(error_context, rb_id2sym(rb_intern("retry_attempts")), INT2FIX(ctx.retry_attempts()));
1017
+ if (!ctx.retry_reasons().empty()) {
1018
+ VALUE retry_reasons = rb_ary_new_capa(static_cast<long>(ctx.retry_reasons().size()));
1019
+ for (const auto& reason : ctx.retry_reasons()) {
1020
+ auto reason_str = fmt::format("{}", reason);
1021
+ rb_ary_push(retry_reasons, rb_id2sym(rb_intern(reason_str.c_str())));
1025
1022
  }
1023
+ rb_hash_aset(error_context, rb_id2sym(rb_intern("retry_reasons")), retry_reasons);
1026
1024
  }
1027
1025
  if (ctx.last_dispatched_to()) {
1028
1026
  rb_hash_aset(error_context, rb_id2sym(rb_intern("last_dispatched_to")), cb_str_new(ctx.last_dispatched_to().value()));
@@ -3517,7 +3515,7 @@ cb_Backend_document_lookup_in(VALUE self, VALUE bucket, VALUE scope, VALUE colle
3517
3515
  if (!resp_entry.value.empty()) {
3518
3516
  rb_hash_aset(entry, value_property, cb_str_new(resp_entry.value));
3519
3517
  }
3520
- if (resp_entry.ec && resp_entry.ec != couchbase::errc::key_value::path_not_found) {
3518
+ if (resp_entry.ec) {
3521
3519
  rb_hash_aset(entry,
3522
3520
  error_property,
3523
3521
  cb_map_error_code(resp_entry.ec,
@@ -3628,7 +3626,7 @@ cb_Backend_document_lookup_in_any_replica(VALUE self, VALUE bucket, VALUE scope,
3628
3626
  if (!resp_entry.value.empty()) {
3629
3627
  rb_hash_aset(entry, value_property, cb_str_new(resp_entry.value));
3630
3628
  }
3631
- if (resp_entry.ec && resp_entry.ec != couchbase::errc::key_value::path_not_found) {
3629
+ if (resp_entry.ec) {
3632
3630
  rb_hash_aset(entry,
3633
3631
  error_property,
3634
3632
  cb_map_error_code(resp_entry.ec,
@@ -3744,7 +3742,7 @@ cb_Backend_document_lookup_in_all_replicas(VALUE self, VALUE bucket, VALUE scope
3744
3742
  if (!field_entry.value.empty()) {
3745
3743
  rb_hash_aset(entry, value_property, cb_str_new(field_entry.value));
3746
3744
  }
3747
- if (field_entry.ec && field_entry.ec != couchbase::errc::key_value::path_not_found) {
3745
+ if (field_entry.ec) {
3748
3746
  rb_hash_aset(
3749
3747
  entry,
3750
3748
  error_property,
@@ -4370,18 +4368,20 @@ cb_Backend_document_query(VALUE self, VALUE statement, VALUE options)
4370
4368
  static void
4371
4369
  cb_generate_bucket_settings(VALUE bucket, couchbase::core::management::cluster::bucket_settings& entry, bool is_create)
4372
4370
  {
4373
- if (VALUE bucket_type = rb_hash_aref(bucket, rb_id2sym(rb_intern("bucket_type"))); TYPE(bucket_type) == T_SYMBOL) {
4374
- if (bucket_type == rb_id2sym(rb_intern("couchbase")) || bucket_type == rb_id2sym(rb_intern("membase"))) {
4375
- entry.bucket_type = couchbase::core::management::cluster::bucket_type::couchbase;
4376
- } else if (bucket_type == rb_id2sym(rb_intern("memcached"))) {
4377
- entry.bucket_type = couchbase::core::management::cluster::bucket_type::memcached;
4378
- } else if (bucket_type == rb_id2sym(rb_intern("ephemeral"))) {
4379
- entry.bucket_type = couchbase::core::management::cluster::bucket_type::ephemeral;
4371
+ if (VALUE bucket_type = rb_hash_aref(bucket, rb_id2sym(rb_intern("bucket_type"))); !NIL_P(bucket_type)) {
4372
+ if (TYPE(bucket_type) == T_SYMBOL) {
4373
+ if (bucket_type == rb_id2sym(rb_intern("couchbase")) || bucket_type == rb_id2sym(rb_intern("membase"))) {
4374
+ entry.bucket_type = couchbase::core::management::cluster::bucket_type::couchbase;
4375
+ } else if (bucket_type == rb_id2sym(rb_intern("memcached"))) {
4376
+ entry.bucket_type = couchbase::core::management::cluster::bucket_type::memcached;
4377
+ } else if (bucket_type == rb_id2sym(rb_intern("ephemeral"))) {
4378
+ entry.bucket_type = couchbase::core::management::cluster::bucket_type::ephemeral;
4379
+ } else {
4380
+ throw ruby_exception(rb_eArgError, rb_sprintf("unknown bucket type, given %+" PRIsVALUE, bucket_type));
4381
+ }
4380
4382
  } else {
4381
- throw ruby_exception(rb_eArgError, rb_sprintf("unknown bucket type, given %+" PRIsVALUE, bucket_type));
4383
+ throw ruby_exception(rb_eArgError, rb_sprintf("bucket type must be a Symbol, given %+" PRIsVALUE, bucket_type));
4382
4384
  }
4383
- } else {
4384
- throw ruby_exception(rb_eArgError, rb_sprintf("bucket type must be a Symbol, given %+" PRIsVALUE, bucket_type));
4385
4385
  }
4386
4386
 
4387
4387
  if (VALUE name = rb_hash_aref(bucket, rb_id2sym(rb_intern("name"))); TYPE(name) == T_STRING) {
@@ -4390,10 +4390,12 @@ cb_generate_bucket_settings(VALUE bucket, couchbase::core::management::cluster::
4390
4390
  throw ruby_exception(rb_eArgError, rb_sprintf("bucket name must be a String, given %+" PRIsVALUE, name));
4391
4391
  }
4392
4392
 
4393
- if (VALUE quota = rb_hash_aref(bucket, rb_id2sym(rb_intern("ram_quota_mb"))); TYPE(quota) == T_FIXNUM) {
4394
- entry.ram_quota_mb = FIX2ULONG(quota);
4395
- } else {
4396
- throw ruby_exception(rb_eArgError, rb_sprintf("bucket RAM quota must be an Integer, given %+" PRIsVALUE, quota));
4393
+ if (VALUE quota = rb_hash_aref(bucket, rb_id2sym(rb_intern("ram_quota_mb"))); !NIL_P(quota)) {
4394
+ if (TYPE(quota) == T_FIXNUM) {
4395
+ entry.ram_quota_mb = FIX2ULONG(quota);
4396
+ } else {
4397
+ throw ruby_exception(rb_eArgError, rb_sprintf("bucket RAM quota must be an Integer, given %+" PRIsVALUE, quota));
4398
+ }
4397
4399
  }
4398
4400
 
4399
4401
  if (VALUE expiry = rb_hash_aref(bucket, rb_id2sym(rb_intern("max_expiry"))); !NIL_P(expiry)) {
@@ -4490,6 +4492,31 @@ cb_generate_bucket_settings(VALUE bucket, couchbase::core::management::cluster::
4490
4492
  }
4491
4493
  }
4492
4494
 
4495
+ if (VALUE history_retention_collection_default = rb_hash_aref(bucket, rb_id2sym(rb_intern("history_retention_collection_default")));
4496
+ !NIL_P(history_retention_collection_default)) {
4497
+ entry.history_retention_collection_default = RTEST(history_retention_collection_default);
4498
+ }
4499
+
4500
+ if (VALUE history_retention_bytes = rb_hash_aref(bucket, rb_id2sym(rb_intern("history_retention_bytes")));
4501
+ !NIL_P(history_retention_bytes)) {
4502
+ if (TYPE(history_retention_bytes) == T_FIXNUM) {
4503
+ entry.history_retention_bytes = FIX2UINT(history_retention_bytes);
4504
+ } else {
4505
+ throw ruby_exception(rb_eArgError,
4506
+ rb_sprintf("history retention bytes must be an Integer, given %+" PRIsVALUE, history_retention_bytes));
4507
+ }
4508
+ }
4509
+
4510
+ if (VALUE history_retention_duration = rb_hash_aref(bucket, rb_id2sym(rb_intern("history_retention_duration")));
4511
+ !NIL_P(history_retention_duration)) {
4512
+ if (TYPE(history_retention_duration) == T_FIXNUM) {
4513
+ entry.history_retention_duration = FIX2UINT(history_retention_duration);
4514
+ } else {
4515
+ throw ruby_exception(
4516
+ rb_eArgError, rb_sprintf("history retention duration must be an Integer, given %+" PRIsVALUE, history_retention_duration));
4517
+ }
4518
+ }
4519
+
4493
4520
  if (is_create) {
4494
4521
  if (VALUE conflict_resolution_type = rb_hash_aref(bucket, rb_id2sym(rb_intern("conflict_resolution_type")));
4495
4522
  !NIL_P(conflict_resolution_type)) {
@@ -4654,7 +4681,9 @@ cb_extract_bucket_settings(const couchbase::core::management::cluster::bucket_se
4654
4681
  rb_hash_aset(bucket, rb_id2sym(rb_intern("name")), cb_str_new(entry.name));
4655
4682
  rb_hash_aset(bucket, rb_id2sym(rb_intern("uuid")), cb_str_new(entry.uuid));
4656
4683
  rb_hash_aset(bucket, rb_id2sym(rb_intern("ram_quota_mb")), ULL2NUM(entry.ram_quota_mb));
4657
- rb_hash_aset(bucket, rb_id2sym(rb_intern("max_expiry")), ULONG2NUM(entry.max_expiry));
4684
+ if (const auto& val = entry.max_expiry; val.has_value()) {
4685
+ rb_hash_aset(bucket, rb_id2sym(rb_intern("max_expiry")), ULONG2NUM(val.value()));
4686
+ }
4658
4687
  switch (entry.compression_mode) {
4659
4688
  case couchbase::core::management::cluster::bucket_compression::off:
4660
4689
  rb_hash_aset(bucket, rb_id2sym(rb_intern("compression_mode")), rb_id2sym(rb_intern("off")));
@@ -4669,9 +4698,15 @@ cb_extract_bucket_settings(const couchbase::core::management::cluster::bucket_se
4669
4698
  rb_hash_aset(bucket, rb_id2sym(rb_intern("compression_mode")), Qnil);
4670
4699
  break;
4671
4700
  }
4672
- rb_hash_aset(bucket, rb_id2sym(rb_intern("num_replicas")), ULONG2NUM(entry.num_replicas));
4673
- rb_hash_aset(bucket, rb_id2sym(rb_intern("replica_indexes")), entry.replica_indexes ? Qtrue : Qfalse);
4674
- rb_hash_aset(bucket, rb_id2sym(rb_intern("flush_enabled")), entry.flush_enabled ? Qtrue : Qfalse);
4701
+ if (const auto& val = entry.num_replicas; val.has_value()) {
4702
+ rb_hash_aset(bucket, rb_id2sym(rb_intern("num_replicas")), ULONG2NUM(val.value()));
4703
+ }
4704
+ if (const auto& val = entry.replica_indexes; val.has_value()) {
4705
+ rb_hash_aset(bucket, rb_id2sym(rb_intern("replica_indexes")), val.value() ? Qtrue : Qfalse);
4706
+ }
4707
+ if (const auto& val = entry.flush_enabled; val.has_value()) {
4708
+ rb_hash_aset(bucket, rb_id2sym(rb_intern("flush_enabled")), val.value() ? Qtrue : Qfalse);
4709
+ }
4675
4710
  switch (entry.eviction_policy) {
4676
4711
  case couchbase::core::management::cluster::bucket_eviction_policy::full:
4677
4712
  rb_hash_aset(bucket, rb_id2sym(rb_intern("eviction_policy")), rb_id2sym(rb_intern("full")));
@@ -4720,6 +4755,18 @@ cb_extract_bucket_settings(const couchbase::core::management::cluster::bucket_se
4720
4755
  break;
4721
4756
  }
4722
4757
  }
4758
+ if (entry.history_retention_collection_default.has_value()) {
4759
+ rb_hash_aset(bucket,
4760
+ rb_id2sym(rb_intern("history_retention_collection_default")),
4761
+ entry.history_retention_collection_default.value() ? Qtrue : Qfalse);
4762
+ }
4763
+ if (const auto& val = entry.history_retention_bytes; val.has_value()) {
4764
+ rb_hash_aset(bucket, rb_id2sym(rb_intern("history_retention_bytes")), ULONG2NUM(val.value()));
4765
+ }
4766
+ if (const auto& val = entry.history_retention_duration; val.has_value()) {
4767
+ rb_hash_aset(bucket, rb_id2sym(rb_intern("history_retention_duration")), ULONG2NUM(val.value()));
4768
+ }
4769
+
4723
4770
  VALUE capabilities = rb_ary_new_capa(static_cast<long>(entry.capabilities.size()));
4724
4771
  for (const auto& capa : entry.capabilities) {
4725
4772
  rb_ary_push(capabilities, cb_str_new(capa));
@@ -5401,6 +5448,10 @@ cb_Backend_scope_get_all(VALUE self, VALUE bucket_name, VALUE options)
5401
5448
  VALUE collection = rb_hash_new();
5402
5449
  rb_hash_aset(collection, rb_id2sym(rb_intern("uid")), ULL2NUM(c.uid));
5403
5450
  rb_hash_aset(collection, rb_id2sym(rb_intern("name")), cb_str_new(c.name));
5451
+ rb_hash_aset(collection, rb_id2sym(rb_intern("max_expiry")), ULONG2NUM(c.max_expiry));
5452
+ if (c.history.has_value()) {
5453
+ rb_hash_aset(collection, rb_id2sym(rb_intern("history")), c.history.value() ? Qtrue : Qfalse);
5454
+ }
5404
5455
  rb_ary_push(collections, collection);
5405
5456
  }
5406
5457
  rb_hash_aset(scope, rb_id2sym(rb_intern("collections")), collections);
@@ -5531,13 +5582,16 @@ cb_Backend_scope_drop(VALUE self, VALUE bucket_name, VALUE scope_name, VALUE opt
5531
5582
  }
5532
5583
 
5533
5584
  static VALUE
5534
- cb_Backend_collection_create(VALUE self, VALUE bucket_name, VALUE scope_name, VALUE collection_name, VALUE max_expiry, VALUE options)
5585
+ cb_Backend_collection_create(VALUE self, VALUE bucket_name, VALUE scope_name, VALUE collection_name, VALUE settings, VALUE options)
5535
5586
  {
5536
5587
  const auto& cluster = cb_backend_to_cluster(self);
5537
5588
 
5538
5589
  Check_Type(bucket_name, T_STRING);
5539
5590
  Check_Type(scope_name, T_STRING);
5540
5591
  Check_Type(collection_name, T_STRING);
5592
+ if (!NIL_P(settings)) {
5593
+ Check_Type(settings, T_HASH);
5594
+ }
5541
5595
  if (!NIL_P(options)) {
5542
5596
  Check_Type(options, T_HASH);
5543
5597
  }
@@ -5548,10 +5602,20 @@ cb_Backend_collection_create(VALUE self, VALUE bucket_name, VALUE scope_name, VA
5548
5602
  cb_string_new(collection_name) };
5549
5603
  cb_extract_timeout(req, options);
5550
5604
 
5551
- if (!NIL_P(max_expiry)) {
5552
- Check_Type(max_expiry, T_FIXNUM);
5553
- req.max_expiry = FIX2UINT(max_expiry);
5605
+ if (!NIL_P(settings)) {
5606
+ if (VALUE max_expiry = rb_hash_aref(settings, rb_id2sym(rb_intern("max_expiry"))); !NIL_P(max_expiry)) {
5607
+ if (TYPE(max_expiry) == T_FIXNUM) {
5608
+ req.max_expiry = FIX2UINT(max_expiry);
5609
+ } else {
5610
+ throw ruby_exception(rb_eArgError,
5611
+ rb_sprintf("collection max expiry must be an Integer, given %+" PRIsVALUE, max_expiry));
5612
+ }
5613
+ }
5614
+ if (VALUE history = rb_hash_aref(settings, rb_id2sym(rb_intern("history"))); !NIL_P(history)) {
5615
+ req.history = RTEST(history);
5616
+ }
5554
5617
  }
5618
+
5555
5619
  auto barrier = std::make_shared<std::promise<couchbase::core::operations::management::collection_create_response>>();
5556
5620
  auto f = barrier->get_future();
5557
5621
  cluster->execute(req, [barrier](couchbase::core::operations::management::collection_create_response&& resp) {
@@ -5573,6 +5637,62 @@ cb_Backend_collection_create(VALUE self, VALUE bucket_name, VALUE scope_name, VA
5573
5637
  return Qnil;
5574
5638
  }
5575
5639
 
5640
+ static VALUE
5641
+ cb_Backend_collection_update(VALUE self, VALUE bucket_name, VALUE scope_name, VALUE collection_name, VALUE settings, VALUE options)
5642
+ {
5643
+ const auto& cluster = cb_backend_to_cluster(self);
5644
+
5645
+ Check_Type(bucket_name, T_STRING);
5646
+ Check_Type(scope_name, T_STRING);
5647
+ Check_Type(collection_name, T_STRING);
5648
+ if (!NIL_P(settings)) {
5649
+ Check_Type(settings, T_HASH);
5650
+ }
5651
+ if (!NIL_P(options)) {
5652
+ Check_Type(options, T_HASH);
5653
+ }
5654
+
5655
+ try {
5656
+ couchbase::core::operations::management::collection_update_request req{ cb_string_new(bucket_name),
5657
+ cb_string_new(scope_name),
5658
+ cb_string_new(collection_name) };
5659
+ cb_extract_timeout(req, options);
5660
+
5661
+ if (!NIL_P(settings)) {
5662
+ if (VALUE max_expiry = rb_hash_aref(settings, rb_id2sym(rb_intern("max_expiry"))); !NIL_P(max_expiry)) {
5663
+ if (TYPE(max_expiry) == T_FIXNUM) {
5664
+ req.max_expiry = FIX2UINT(max_expiry);
5665
+ } else {
5666
+ throw ruby_exception(rb_eArgError,
5667
+ rb_sprintf("collection max expiry must be an Integer, given %+" PRIsVALUE, max_expiry));
5668
+ }
5669
+ }
5670
+ if (VALUE history = rb_hash_aref(settings, rb_id2sym(rb_intern("history"))); !NIL_P(history)) {
5671
+ req.history = RTEST(history);
5672
+ }
5673
+ }
5674
+
5675
+ auto barrier = std::make_shared<std::promise<couchbase::core::operations::management::collection_update_response>>();
5676
+ auto f = barrier->get_future();
5677
+ cluster->execute(req, [barrier](couchbase::core::operations::management::collection_update_response&& resp) {
5678
+ barrier->set_value(std::move(resp));
5679
+ });
5680
+ auto resp = cb_wait_for_future(f);
5681
+ if (resp.ctx.ec) {
5682
+ cb_throw_error_code(
5683
+ resp.ctx,
5684
+ fmt::format(
5685
+ R"(unable update the collection "{}.{}" on the bucket "{}")", req.scope_name, req.collection_name, req.bucket_name));
5686
+ }
5687
+ return ULL2NUM(resp.uid);
5688
+ } catch (const std::system_error& se) {
5689
+ rb_exc_raise(cb_map_error_code(se.code(), fmt::format("failed to perform {}: {}", __func__, se.what()), false));
5690
+ } catch (const ruby_exception& e) {
5691
+ rb_exc_raise(e.exception_object());
5692
+ }
5693
+ return Qnil;
5694
+ }
5695
+
5576
5696
  static VALUE
5577
5697
  cb_Backend_collection_drop(VALUE self, VALUE bucket_name, VALUE scope_name, VALUE collection_name, VALUE options)
5578
5698
  {
@@ -9190,6 +9310,7 @@ init_backend(VALUE mCouchbase)
9190
9310
  rb_define_method(cBackend, "scope_create", VALUE_FUNC(cb_Backend_scope_create), 3);
9191
9311
  rb_define_method(cBackend, "scope_drop", VALUE_FUNC(cb_Backend_scope_drop), 3);
9192
9312
  rb_define_method(cBackend, "collection_create", VALUE_FUNC(cb_Backend_collection_create), 5);
9313
+ rb_define_method(cBackend, "collection_update", VALUE_FUNC(cb_Backend_collection_update), 5);
9193
9314
  rb_define_method(cBackend, "collection_drop", VALUE_FUNC(cb_Backend_collection_drop), 4);
9194
9315
 
9195
9316
  rb_define_method(cBackend, "query_index_get_all", VALUE_FUNC(cb_Backend_query_index_get_all), 2);
data/ext/revisions.rb CHANGED
@@ -1,3 +1,3 @@
1
- cmake_flags << "-DEXT_GIT_REVISION=167d2e58559c91239675f1e7f7ffa2f6176f0512"
2
- cmake_flags << "-DCOUCHBASE_CXX_CLIENT_GIT_REVISION=43cf66a592d1f8112141a73e5a563d7187ee0ee6"
3
- cmake_flags << "-DCOUCHBASE_CXX_CLIENT_GIT_DESCRIBE=1.0.0-dp.8-0-g43cf66a"
1
+ cmake_flags << "-DEXT_GIT_REVISION=276c680f5d7d8c33d270470f473a42a9d91c11c7"
2
+ cmake_flags << "-DCOUCHBASE_CXX_CLIENT_GIT_REVISION=a873bb91aa487fab8acd3b619355b911b1588fc1"
3
+ cmake_flags << "-DCOUCHBASE_CXX_CLIENT_GIT_DESCRIBE=1.0.0-dp.9-0-ga873bb9"
@@ -169,7 +169,6 @@ module Couchbase
169
169
  field = get_field_at_index(path_or_index)
170
170
 
171
171
  raise field.error unless field.error.nil?
172
- raise Error::PathNotFound, "Path is not found: #{path_or_index}" unless field.exists
173
172
 
174
173
  transcoder.decode(field.value, :json)
175
174
  end
@@ -191,7 +190,7 @@ module Couchbase
191
190
  end
192
191
  return false unless field
193
192
 
194
- raise field.error unless field.error.nil?
193
+ raise field.error unless field.error.nil? || field.error.is_a?(Error::PathNotFound)
195
194
 
196
195
  field.exists
197
196
  end