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
data/ext/couchbase.cxx CHANGED
@@ -33,9 +33,12 @@
33
33
  #include <core/platform/terminate_handler.h>
34
34
 
35
35
  #include <core/cluster.hxx>
36
+
37
+ #include <core/agent_group.hxx>
36
38
  #include <core/design_document_namespace_fmt.hxx>
37
39
  #include <core/logger/configuration.hxx>
38
40
  #include <core/operations.hxx>
41
+
39
42
  #include <core/operations/management/analytics.hxx>
40
43
  #include <core/operations/management/bucket.hxx>
41
44
  #include <core/operations/management/cluster_developer_preview_enable.hxx>
@@ -45,6 +48,12 @@
45
48
  #include <core/operations/management/user.hxx>
46
49
  #include <core/operations/management/view.hxx>
47
50
 
51
+ #include <core/impl/subdoc/path_flags.hxx>
52
+
53
+ #include <core/range_scan_options.hxx>
54
+ #include <core/range_scan_orchestrator.hxx>
55
+ #include <core/range_scan_orchestrator_options.hxx>
56
+
48
57
  #include <core/io/dns_client.hxx>
49
58
  #include <core/io/dns_config.hxx>
50
59
  #include <core/utils/connection_string.hxx>
@@ -435,7 +444,7 @@ cb_backend_close(cb_backend_data* backend)
435
444
  static void
436
445
  cb_Backend_mark(void* /* ptr */)
437
446
  {
438
- /* no embeded ruby objects -- no mark */
447
+ /* no embedded ruby objects -- no mark */
439
448
  }
440
449
 
441
450
  static void
@@ -495,6 +504,8 @@ cb_backend_to_cluster(VALUE self)
495
504
  return backend->cluster;
496
505
  }
497
506
 
507
+ static VALUE eCouchbaseError;
508
+
498
509
  static VALUE eAmbiguousTimeout;
499
510
  static VALUE eAuthenticationFailure;
500
511
  static VALUE eBucketExists;
@@ -534,6 +545,7 @@ static VALUE eInvalidArgument;
534
545
  static VALUE eJobQueueFull;
535
546
  static VALUE eLinkNotFound;
536
547
  static VALUE eLinkExists;
548
+ static VALUE eMutationTokenOutdated;
537
549
  static VALUE eNumberTooBig;
538
550
  static VALUE eParsingFailure;
539
551
  static VALUE ePathExists;
@@ -586,7 +598,7 @@ static void
586
598
  init_exceptions(VALUE mCouchbase)
587
599
  {
588
600
  VALUE mError = rb_define_module_under(mCouchbase, "Error");
589
- VALUE eCouchbaseError = rb_define_class_under(mError, "CouchbaseError", rb_eStandardError);
601
+ eCouchbaseError = rb_define_class_under(mError, "CouchbaseError", rb_eStandardError);
590
602
 
591
603
  VALUE eTimeout = rb_define_class_under(mError, "Timeout", eCouchbaseError);
592
604
 
@@ -629,6 +641,7 @@ init_exceptions(VALUE mCouchbase)
629
641
  eJobQueueFull = rb_define_class_under(mError, "JobQueueFull", eCouchbaseError);
630
642
  eLinkNotFound = rb_define_class_under(mError, "LinkNotFound", eCouchbaseError);
631
643
  eLinkExists = rb_define_class_under(mError, "LinkExists", eCouchbaseError);
644
+ eMutationTokenOutdated = rb_define_class_under(mError, "MutationTokenOutdated", eCouchbaseError);
632
645
  eNumberTooBig = rb_define_class_under(mError, "NumberTooBig", eCouchbaseError);
633
646
  eParsingFailure = rb_define_class_under(mError, "ParsingFailure", eCouchbaseError);
634
647
  ePathExists = rb_define_class_under(mError, "PathExists", eCouchbaseError);
@@ -784,6 +797,9 @@ cb_map_error_code(std::error_code ec, const std::string& message, bool include_e
784
797
  case couchbase::errc::key_value::durable_write_re_commit_in_progress:
785
798
  return rb_exc_new_cstr(eDurableWriteReCommitInProgress, what.c_str());
786
799
 
800
+ case couchbase::errc::key_value::mutation_token_outdated:
801
+ return rb_exc_new_cstr(eMutationTokenOutdated, what.c_str());
802
+
787
803
  case couchbase::errc::key_value::path_not_found:
788
804
  return rb_exc_new_cstr(ePathNotFound, what.c_str());
789
805
 
@@ -834,6 +850,10 @@ cb_map_error_code(std::error_code ec, const std::string& message, bool include_e
834
850
 
835
851
  case couchbase::errc::key_value::cannot_revive_living_document:
836
852
  return rb_exc_new_cstr(eCannotReviveLivingDocument, what.c_str());
853
+
854
+ case couchbase::errc::key_value::range_scan_completed:
855
+ // Should not be exposed to the Ruby SDK, map it to a BackendError
856
+ return rb_exc_new_cstr(eBackendError, what.c_str());
837
857
  }
838
858
  } else if (ec.category() == couchbase::core::impl::query_category()) {
839
859
  switch (static_cast<couchbase::errc::query>(ec.value())) {
@@ -993,16 +1013,14 @@ cb_map_error_code(const couchbase::key_value_error_context& ctx, const std::stri
993
1013
  rb_hash_aset(enhanced_error_info, rb_id2sym(rb_intern("context")), cb_str_new(ctx.extended_error_info()->context()));
994
1014
  rb_hash_aset(error_context, rb_id2sym(rb_intern("extended_error_info")), enhanced_error_info);
995
1015
  }
996
- if (ctx.retry_attempts() > 0) {
997
- rb_hash_aset(error_context, rb_id2sym(rb_intern("retry_attempts")), INT2FIX(ctx.retry_attempts()));
998
- if (!ctx.retry_reasons().empty()) {
999
- VALUE retry_reasons = rb_ary_new_capa(static_cast<long>(ctx.retry_reasons().size()));
1000
- for (const auto& reason : ctx.retry_reasons()) {
1001
- auto reason_str = fmt::format("{}", reason);
1002
- rb_ary_push(retry_reasons, rb_id2sym(rb_intern(reason_str.c_str())));
1003
- }
1004
- 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())));
1005
1022
  }
1023
+ rb_hash_aset(error_context, rb_id2sym(rb_intern("retry_reasons")), retry_reasons);
1006
1024
  }
1007
1025
  if (ctx.last_dispatched_to()) {
1008
1026
  rb_hash_aset(error_context, rb_id2sym(rb_intern("last_dispatched_to")), cb_str_new(ctx.last_dispatched_to().value()));
@@ -3409,7 +3427,7 @@ cb_Backend_document_decrement(VALUE self, VALUE bucket, VALUE scope, VALUE colle
3409
3427
  static VALUE
3410
3428
  cb_Backend_document_lookup_in(VALUE self, VALUE bucket, VALUE scope, VALUE collection, VALUE id, VALUE specs, VALUE options)
3411
3429
  {
3412
- const auto& core = cb_backend_to_cluster(self);
3430
+ const auto& cluster = cb_backend_to_cluster(self);
3413
3431
 
3414
3432
  Check_Type(bucket, T_STRING);
3415
3433
  Check_Type(scope, T_STRING);
@@ -3425,10 +3443,6 @@ cb_Backend_document_lookup_in(VALUE self, VALUE bucket, VALUE scope, VALUE colle
3425
3443
  }
3426
3444
 
3427
3445
  try {
3428
- couchbase::lookup_in_options opts;
3429
- couchbase::ruby::set_timeout(opts, options);
3430
- couchbase::ruby::set_access_deleted(opts, options);
3431
-
3432
3446
  couchbase::core::document_id doc_id{
3433
3447
  cb_string_new(bucket),
3434
3448
  cb_string_new(scope),
@@ -3436,12 +3450,15 @@ cb_Backend_document_lookup_in(VALUE self, VALUE bucket, VALUE scope, VALUE colle
3436
3450
  cb_string_new(id),
3437
3451
  };
3438
3452
 
3453
+ couchbase::core::operations::lookup_in_request req{ doc_id };
3454
+ cb_extract_timeout(req, options);
3455
+ cb_extract_option_bool(req.access_deleted, options, "access_deleted");
3456
+
3439
3457
  static VALUE xattr_property = rb_id2sym(rb_intern("xattr"));
3440
3458
  static VALUE path_property = rb_id2sym(rb_intern("path"));
3441
3459
  static VALUE opcode_property = rb_id2sym(rb_intern("opcode"));
3442
3460
 
3443
3461
  auto entries_size = static_cast<std::size_t>(RARRAY_LEN(specs));
3444
- couchbase::lookup_in_specs cxx_specs;
3445
3462
  for (std::size_t i = 0; i < entries_size; ++i) {
3446
3463
  VALUE entry = rb_ary_entry(specs, static_cast<long>(i));
3447
3464
  cb_check_type(entry, T_HASH);
@@ -3450,29 +3467,138 @@ cb_Backend_document_lookup_in(VALUE self, VALUE bucket, VALUE scope, VALUE colle
3450
3467
  bool xattr = RTEST(rb_hash_aref(entry, xattr_property));
3451
3468
  VALUE path = rb_hash_aref(entry, path_property);
3452
3469
  cb_check_type(path, T_STRING);
3470
+ auto opcode = couchbase::core::impl::subdoc::opcode{};
3453
3471
  if (ID operation_id = rb_sym2id(operation); operation_id == rb_intern("get_doc")) {
3454
- cxx_specs.push_back(couchbase::lookup_in_specs::get("").xattr(xattr));
3472
+ opcode = couchbase::core::impl::subdoc::opcode::get_doc;
3455
3473
  } else if (operation_id == rb_intern("get")) {
3456
- cxx_specs.push_back(couchbase::lookup_in_specs::get(cb_string_new(path)).xattr(xattr));
3474
+ opcode = couchbase::core::impl::subdoc::opcode::get;
3457
3475
  } else if (operation_id == rb_intern("exists")) {
3458
- cxx_specs.push_back(couchbase::lookup_in_specs::exists(cb_string_new(path)).xattr(xattr));
3476
+ opcode = couchbase::core::impl::subdoc::opcode::exists;
3459
3477
  } else if (operation_id == rb_intern("count")) {
3460
- cxx_specs.push_back(couchbase::lookup_in_specs::count(cb_string_new(path)).xattr(xattr));
3478
+ opcode = couchbase::core::impl::subdoc::opcode::get_count;
3461
3479
  } else {
3462
3480
  throw ruby_exception(eInvalidArgument, rb_sprintf("unsupported operation for subdocument lookup: %+" PRIsVALUE, operation));
3463
3481
  }
3464
3482
  cb_check_type(path, T_STRING);
3483
+
3484
+ req.specs.emplace_back(couchbase::core::impl::subdoc::command{
3485
+ opcode, cb_string_new(path), {}, couchbase::core::impl::subdoc::build_lookup_in_path_flags(xattr) });
3465
3486
  }
3466
3487
 
3467
- auto f = couchbase::cluster(core)
3468
- .bucket(cb_string_new(bucket))
3469
- .scope(cb_string_new(scope))
3470
- .collection(cb_string_new(collection))
3471
- .lookup_in(cb_string_new(id), cxx_specs, opts);
3488
+ auto barrier = std::make_shared<std::promise<couchbase::core::operations::lookup_in_response>>();
3489
+ auto f = barrier->get_future();
3490
+ cluster->execute(req, [barrier](couchbase::core::operations::lookup_in_response&& resp) { barrier->set_value(std::move(resp)); });
3491
+ auto resp = cb_wait_for_future(f);
3492
+ if (resp.ctx.ec()) {
3493
+ cb_throw_error_code(resp.ctx, "unable to perform lookup_in operation");
3494
+ }
3472
3495
 
3473
- auto [ctx, resp] = cb_wait_for_future(f);
3474
- if (ctx.ec()) {
3475
- cb_throw_error_code(ctx, "unable to lookup_in");
3496
+ static VALUE deleted_property = rb_id2sym(rb_intern("deleted"));
3497
+ static VALUE fields_property = rb_id2sym(rb_intern("fields"));
3498
+ static VALUE index_property = rb_id2sym(rb_intern("index"));
3499
+ static VALUE exists_property = rb_id2sym(rb_intern("exists"));
3500
+ static VALUE cas_property = rb_id2sym(rb_intern("cas"));
3501
+ static VALUE value_property = rb_id2sym(rb_intern("value"));
3502
+ static VALUE error_property = rb_id2sym(rb_intern("error"));
3503
+
3504
+ VALUE res = rb_hash_new();
3505
+ rb_hash_aset(res, cas_property, cb_cas_to_num(resp.cas));
3506
+ VALUE fields = rb_ary_new_capa(static_cast<long>(entries_size));
3507
+ rb_hash_aset(res, fields_property, fields);
3508
+ rb_hash_aset(res, deleted_property, resp.deleted ? Qtrue : Qfalse);
3509
+ for (std::size_t i = 0; i < entries_size; ++i) {
3510
+ auto resp_entry = resp.fields.at(i);
3511
+ VALUE entry = rb_hash_new();
3512
+ rb_hash_aset(entry, index_property, ULL2NUM(resp_entry.original_index));
3513
+ rb_hash_aset(entry, exists_property, resp_entry.exists ? Qtrue : Qfalse);
3514
+ rb_hash_aset(entry, path_property, cb_str_new(resp_entry.path));
3515
+ if (!resp_entry.value.empty()) {
3516
+ rb_hash_aset(entry, value_property, cb_str_new(resp_entry.value));
3517
+ }
3518
+ if (resp_entry.ec) {
3519
+ rb_hash_aset(entry,
3520
+ error_property,
3521
+ cb_map_error_code(resp_entry.ec,
3522
+ fmt::format("error getting result for spec at index {}, path \"{}\"", i, resp_entry.path)));
3523
+ }
3524
+ rb_ary_store(fields, static_cast<long>(i), entry);
3525
+ }
3526
+ return res;
3527
+ } catch (const std::system_error& se) {
3528
+ rb_exc_raise(cb_map_error_code(se.code(), fmt::format("failed to perform {}: {}", __func__, se.what()), false));
3529
+ } catch (const ruby_exception& e) {
3530
+ rb_exc_raise(e.exception_object());
3531
+ }
3532
+ return Qnil;
3533
+ }
3534
+
3535
+ static VALUE
3536
+ cb_Backend_document_lookup_in_any_replica(VALUE self, VALUE bucket, VALUE scope, VALUE collection, VALUE id, VALUE specs, VALUE options)
3537
+ {
3538
+ const auto& cluster = cb_backend_to_cluster(self);
3539
+
3540
+ Check_Type(bucket, T_STRING);
3541
+ Check_Type(scope, T_STRING);
3542
+ Check_Type(collection, T_STRING);
3543
+ Check_Type(id, T_STRING);
3544
+ Check_Type(specs, T_ARRAY);
3545
+ if (RARRAY_LEN(specs) <= 0) {
3546
+ rb_raise(rb_eArgError, "Array with specs cannot be empty");
3547
+ return Qnil;
3548
+ }
3549
+ if (!NIL_P(options)) {
3550
+ Check_Type(options, T_HASH);
3551
+ }
3552
+
3553
+ try {
3554
+ couchbase::core::document_id doc_id{
3555
+ cb_string_new(bucket),
3556
+ cb_string_new(scope),
3557
+ cb_string_new(collection),
3558
+ cb_string_new(id),
3559
+ };
3560
+
3561
+ couchbase::core::operations::lookup_in_any_replica_request req{ doc_id };
3562
+ cb_extract_timeout(req, options);
3563
+
3564
+ static VALUE xattr_property = rb_id2sym(rb_intern("xattr"));
3565
+ static VALUE path_property = rb_id2sym(rb_intern("path"));
3566
+ static VALUE opcode_property = rb_id2sym(rb_intern("opcode"));
3567
+
3568
+ auto entries_size = static_cast<std::size_t>(RARRAY_LEN(specs));
3569
+ for (std::size_t i = 0; i < entries_size; ++i) {
3570
+ VALUE entry = rb_ary_entry(specs, static_cast<long>(i));
3571
+ cb_check_type(entry, T_HASH);
3572
+ VALUE operation = rb_hash_aref(entry, opcode_property);
3573
+ cb_check_type(operation, T_SYMBOL);
3574
+ bool xattr = RTEST(rb_hash_aref(entry, xattr_property));
3575
+ VALUE path = rb_hash_aref(entry, path_property);
3576
+ cb_check_type(path, T_STRING);
3577
+ auto opcode = couchbase::core::impl::subdoc::opcode{};
3578
+ if (ID operation_id = rb_sym2id(operation); operation_id == rb_intern("get_doc")) {
3579
+ opcode = couchbase::core::impl::subdoc::opcode::get_doc;
3580
+ } else if (operation_id == rb_intern("get")) {
3581
+ opcode = couchbase::core::impl::subdoc::opcode::get;
3582
+ } else if (operation_id == rb_intern("exists")) {
3583
+ opcode = couchbase::core::impl::subdoc::opcode::exists;
3584
+ } else if (operation_id == rb_intern("count")) {
3585
+ opcode = couchbase::core::impl::subdoc::opcode::get_count;
3586
+ } else {
3587
+ throw ruby_exception(eInvalidArgument, rb_sprintf("unsupported operation for subdocument lookup: %+" PRIsVALUE, operation));
3588
+ }
3589
+ cb_check_type(path, T_STRING);
3590
+
3591
+ req.specs.emplace_back(couchbase::core::impl::subdoc::command{
3592
+ opcode, cb_string_new(path), {}, couchbase::core::impl::subdoc::build_lookup_in_path_flags(xattr) });
3593
+ }
3594
+
3595
+ auto barrier = std::make_shared<std::promise<couchbase::core::operations::lookup_in_any_replica_response>>();
3596
+ auto f = barrier->get_future();
3597
+ cluster->execute(
3598
+ req, [barrier](couchbase::core::operations::lookup_in_any_replica_response&& resp) { barrier->set_value(std::move(resp)); });
3599
+ auto resp = cb_wait_for_future(f);
3600
+ if (resp.ctx.ec()) {
3601
+ cb_throw_error_code(resp.ctx, "unable to perform lookup_in_any_replica operation");
3476
3602
  }
3477
3603
 
3478
3604
  static VALUE deleted_property = rb_id2sym(rb_intern("deleted"));
@@ -3481,23 +3607,153 @@ cb_Backend_document_lookup_in(VALUE self, VALUE bucket, VALUE scope, VALUE colle
3481
3607
  static VALUE exists_property = rb_id2sym(rb_intern("exists"));
3482
3608
  static VALUE cas_property = rb_id2sym(rb_intern("cas"));
3483
3609
  static VALUE value_property = rb_id2sym(rb_intern("value"));
3610
+ static VALUE error_property = rb_id2sym(rb_intern("error"));
3611
+ static VALUE is_replica_property = rb_id2sym(rb_intern("is_replica"));
3484
3612
 
3485
3613
  VALUE res = rb_hash_new();
3486
- rb_hash_aset(res, cas_property, cb_cas_to_num(resp.cas()));
3614
+ rb_hash_aset(res, cas_property, cb_cas_to_num(resp.cas));
3487
3615
  VALUE fields = rb_ary_new_capa(static_cast<long>(entries_size));
3488
3616
  rb_hash_aset(res, fields_property, fields);
3489
- rb_hash_aset(res, deleted_property, resp.is_deleted() ? Qtrue : Qfalse);
3617
+ rb_hash_aset(res, deleted_property, resp.deleted ? Qtrue : Qfalse);
3618
+ rb_hash_aset(res, is_replica_property, resp.is_replica ? Qtrue : Qfalse);
3619
+
3490
3620
  for (std::size_t i = 0; i < entries_size; ++i) {
3621
+ auto resp_entry = resp.fields.at(i);
3491
3622
  VALUE entry = rb_hash_new();
3492
- rb_hash_aset(entry, index_property, ULL2NUM(i));
3493
- rb_hash_aset(entry, exists_property, resp.exists(i) ? Qtrue : Qfalse);
3494
- rb_hash_aset(entry, path_property, rb_hash_aref(rb_ary_entry(specs, static_cast<long>(i)), path_property));
3495
- if (resp.has_value(i)) {
3496
- auto value = resp.content_as<tao::json::value>(i);
3497
- rb_hash_aset(entry, value_property, cb_str_new(couchbase::core::utils::json::generate(value)));
3623
+ rb_hash_aset(entry, index_property, ULL2NUM(resp_entry.original_index));
3624
+ rb_hash_aset(entry, exists_property, resp_entry.exists ? Qtrue : Qfalse);
3625
+ rb_hash_aset(entry, path_property, cb_str_new(resp_entry.path));
3626
+ if (!resp_entry.value.empty()) {
3627
+ rb_hash_aset(entry, value_property, cb_str_new(resp_entry.value));
3628
+ }
3629
+ if (resp_entry.ec) {
3630
+ rb_hash_aset(entry,
3631
+ error_property,
3632
+ cb_map_error_code(resp_entry.ec,
3633
+ fmt::format("error getting result for spec at index {}, path \"{}\"", i, resp_entry.path)));
3498
3634
  }
3499
3635
  rb_ary_store(fields, static_cast<long>(i), entry);
3500
3636
  }
3637
+
3638
+ return res;
3639
+ } catch (const std::system_error& se) {
3640
+ rb_exc_raise(cb_map_error_code(se.code(), fmt::format("failed to perform {}: {}", __func__, se.what()), false));
3641
+ } catch (const ruby_exception& e) {
3642
+ rb_exc_raise(e.exception_object());
3643
+ }
3644
+ return Qnil;
3645
+ }
3646
+
3647
+ static VALUE
3648
+ cb_Backend_document_lookup_in_all_replicas(VALUE self, VALUE bucket, VALUE scope, VALUE collection, VALUE id, VALUE specs, VALUE options)
3649
+ {
3650
+ const auto& cluster = cb_backend_to_cluster(self);
3651
+
3652
+ Check_Type(bucket, T_STRING);
3653
+ Check_Type(scope, T_STRING);
3654
+ Check_Type(collection, T_STRING);
3655
+ Check_Type(id, T_STRING);
3656
+ Check_Type(specs, T_ARRAY);
3657
+ if (RARRAY_LEN(specs) <= 0) {
3658
+ rb_raise(rb_eArgError, "Array with specs cannot be empty");
3659
+ return Qnil;
3660
+ }
3661
+ if (!NIL_P(options)) {
3662
+ Check_Type(options, T_HASH);
3663
+ }
3664
+
3665
+ try {
3666
+ couchbase::core::document_id doc_id{
3667
+ cb_string_new(bucket),
3668
+ cb_string_new(scope),
3669
+ cb_string_new(collection),
3670
+ cb_string_new(id),
3671
+ };
3672
+
3673
+ couchbase::core::operations::lookup_in_all_replicas_request req{ doc_id };
3674
+ cb_extract_timeout(req, options);
3675
+
3676
+ static VALUE xattr_property = rb_id2sym(rb_intern("xattr"));
3677
+ static VALUE path_property = rb_id2sym(rb_intern("path"));
3678
+ static VALUE opcode_property = rb_id2sym(rb_intern("opcode"));
3679
+
3680
+ auto entries_size = static_cast<std::size_t>(RARRAY_LEN(specs));
3681
+ for (std::size_t i = 0; i < entries_size; ++i) {
3682
+ VALUE entry = rb_ary_entry(specs, static_cast<long>(i));
3683
+ cb_check_type(entry, T_HASH);
3684
+ VALUE operation = rb_hash_aref(entry, opcode_property);
3685
+ cb_check_type(operation, T_SYMBOL);
3686
+ bool xattr = RTEST(rb_hash_aref(entry, xattr_property));
3687
+ VALUE path = rb_hash_aref(entry, path_property);
3688
+ cb_check_type(path, T_STRING);
3689
+ auto opcode = couchbase::core::impl::subdoc::opcode{};
3690
+ if (ID operation_id = rb_sym2id(operation); operation_id == rb_intern("get_doc")) {
3691
+ opcode = couchbase::core::impl::subdoc::opcode::get_doc;
3692
+ } else if (operation_id == rb_intern("get")) {
3693
+ opcode = couchbase::core::impl::subdoc::opcode::get;
3694
+ } else if (operation_id == rb_intern("exists")) {
3695
+ opcode = couchbase::core::impl::subdoc::opcode::exists;
3696
+ } else if (operation_id == rb_intern("count")) {
3697
+ opcode = couchbase::core::impl::subdoc::opcode::get_count;
3698
+ } else {
3699
+ throw ruby_exception(eInvalidArgument, rb_sprintf("unsupported operation for subdocument lookup: %+" PRIsVALUE, operation));
3700
+ }
3701
+ cb_check_type(path, T_STRING);
3702
+
3703
+ req.specs.emplace_back(couchbase::core::impl::subdoc::command{
3704
+ opcode, cb_string_new(path), {}, couchbase::core::impl::subdoc::build_lookup_in_path_flags(xattr) });
3705
+ }
3706
+
3707
+ auto barrier = std::make_shared<std::promise<couchbase::core::operations::lookup_in_all_replicas_response>>();
3708
+ auto f = barrier->get_future();
3709
+ cluster->execute(
3710
+ req, [barrier](couchbase::core::operations::lookup_in_all_replicas_response&& resp) { barrier->set_value(std::move(resp)); });
3711
+ auto resp = cb_wait_for_future(f);
3712
+ if (resp.ctx.ec()) {
3713
+ cb_throw_error_code(resp.ctx, "unable to perform lookup_in_all_replicas operation");
3714
+ }
3715
+
3716
+ static VALUE deleted_property = rb_id2sym(rb_intern("deleted"));
3717
+ static VALUE fields_property = rb_id2sym(rb_intern("fields"));
3718
+ static VALUE index_property = rb_id2sym(rb_intern("index"));
3719
+ static VALUE exists_property = rb_id2sym(rb_intern("exists"));
3720
+ static VALUE cas_property = rb_id2sym(rb_intern("cas"));
3721
+ static VALUE value_property = rb_id2sym(rb_intern("value"));
3722
+ static VALUE error_property = rb_id2sym(rb_intern("error"));
3723
+ static VALUE is_replica_property = rb_id2sym(rb_intern("is_replica"));
3724
+
3725
+ auto lookup_in_entries_size = resp.entries.size();
3726
+ VALUE res = rb_ary_new_capa(static_cast<long>(lookup_in_entries_size));
3727
+ for (std::size_t j = 0; j < lookup_in_entries_size; ++j) {
3728
+ auto lookup_in_entry = resp.entries.at(j);
3729
+ VALUE lookup_in_entry_res = rb_hash_new();
3730
+ rb_hash_aset(lookup_in_entry_res, cas_property, cb_cas_to_num(lookup_in_entry.cas));
3731
+ VALUE fields = rb_ary_new_capa(static_cast<long>(entries_size));
3732
+ rb_hash_aset(lookup_in_entry_res, fields_property, fields);
3733
+ rb_hash_aset(lookup_in_entry_res, deleted_property, lookup_in_entry.deleted ? Qtrue : Qfalse);
3734
+ rb_hash_aset(lookup_in_entry_res, is_replica_property, lookup_in_entry.is_replica ? Qtrue : Qfalse);
3735
+
3736
+ for (std::size_t i = 0; i < entries_size; ++i) {
3737
+ auto field_entry = lookup_in_entry.fields.at(i);
3738
+ VALUE entry = rb_hash_new();
3739
+ rb_hash_aset(entry, index_property, ULL2NUM(field_entry.original_index));
3740
+ rb_hash_aset(entry, exists_property, field_entry.exists ? Qtrue : Qfalse);
3741
+ rb_hash_aset(entry, path_property, cb_str_new(field_entry.path));
3742
+ if (!field_entry.value.empty()) {
3743
+ rb_hash_aset(entry, value_property, cb_str_new(field_entry.value));
3744
+ }
3745
+ if (field_entry.ec) {
3746
+ rb_hash_aset(
3747
+ entry,
3748
+ error_property,
3749
+ cb_map_error_code(field_entry.ec,
3750
+ fmt::format("error getting result for spec at index {}, path \"{}\"", i, field_entry.path)));
3751
+ }
3752
+ rb_ary_store(fields, static_cast<long>(i), entry);
3753
+ }
3754
+ rb_ary_store(res, static_cast<long>(j), lookup_in_entry_res);
3755
+ }
3756
+
3501
3757
  return res;
3502
3758
  } catch (const std::system_error& se) {
3503
3759
  rb_exc_raise(cb_map_error_code(se.code(), fmt::format("failed to perform {}: {}", __func__, se.what()), false));
@@ -3661,6 +3917,284 @@ cb_Backend_document_mutate_in(VALUE self, VALUE bucket, VALUE scope, VALUE colle
3661
3917
  return Qnil;
3662
3918
  }
3663
3919
 
3920
+ struct cb_core_scan_result_data {
3921
+ std::unique_ptr<couchbase::core::scan_result> scan_result{};
3922
+ };
3923
+
3924
+ static void
3925
+ cb_CoreScanResult_mark(void* ptr)
3926
+ {
3927
+ /* No embedded Ruby objects */
3928
+ }
3929
+
3930
+ static void
3931
+ cb_CoreScanResult_free(void* ptr)
3932
+ {
3933
+ auto* data = static_cast<cb_core_scan_result_data*>(ptr);
3934
+ if (data->scan_result != nullptr && !data->scan_result->is_cancelled()) {
3935
+ data->scan_result->cancel();
3936
+ }
3937
+ data->scan_result.reset();
3938
+ ruby_xfree(data);
3939
+ }
3940
+
3941
+ static const rb_data_type_t cb_core_scan_result_type {
3942
+ .wrap_struct_name = "Couchbase/Backend/CoreScanResult",
3943
+ .function = {
3944
+ .dmark = cb_CoreScanResult_mark,
3945
+ .dfree = cb_CoreScanResult_free,
3946
+ },
3947
+ .data = nullptr,
3948
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
3949
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
3950
+ #endif
3951
+ };
3952
+
3953
+ static VALUE
3954
+ cb_CoreScanResult_allocate(VALUE klass)
3955
+ {
3956
+ cb_core_scan_result_data* data = nullptr;
3957
+ VALUE obj = TypedData_Make_Struct(klass, cb_core_scan_result_data, &cb_core_scan_result_type, data);
3958
+ return obj;
3959
+ }
3960
+
3961
+ static VALUE
3962
+ cb_CoreScanResult_is_cancelled(VALUE self)
3963
+ {
3964
+ cb_core_scan_result_data* data = nullptr;
3965
+ TypedData_Get_Struct(self, cb_core_scan_result_data, &cb_core_scan_result_type, data);
3966
+ auto resp = data->scan_result->is_cancelled();
3967
+ if (resp) {
3968
+ return Qtrue;
3969
+ } else {
3970
+ return Qfalse;
3971
+ }
3972
+ }
3973
+
3974
+ static VALUE
3975
+ cb_CoreScanResult_cancel(VALUE self)
3976
+ {
3977
+ cb_core_scan_result_data* data = nullptr;
3978
+ TypedData_Get_Struct(self, cb_core_scan_result_data, &cb_core_scan_result_type, data);
3979
+ data->scan_result->cancel();
3980
+ return Qnil;
3981
+ }
3982
+
3983
+ static VALUE
3984
+ cb_CoreScanResult_next_item(VALUE self)
3985
+ {
3986
+ try {
3987
+ cb_core_scan_result_data* data = nullptr;
3988
+ TypedData_Get_Struct(self, cb_core_scan_result_data, &cb_core_scan_result_type, data);
3989
+ auto barrier = std::make_shared<std::promise<tl::expected<couchbase::core::range_scan_item, std::error_code>>>();
3990
+ auto f = barrier->get_future();
3991
+ data->scan_result->next([barrier](couchbase::core::range_scan_item item, std::error_code ec) {
3992
+ if (ec) {
3993
+ return barrier->set_value(tl::unexpected(ec));
3994
+ } else {
3995
+ return barrier->set_value(item);
3996
+ }
3997
+ });
3998
+ auto resp = cb_wait_for_future(f);
3999
+ if (!resp.has_value()) {
4000
+ // If the error code is range_scan_completed return nil without raising an exception (nil signifies that there
4001
+ // are no more items)
4002
+ if (resp.error() != couchbase::errc::key_value::range_scan_completed) {
4003
+ cb_throw_error_code(resp.error(), "unable to fetch next scan item");
4004
+ }
4005
+ // Release ownership of scan_result unique pointer
4006
+ return Qnil;
4007
+ }
4008
+ auto item = resp.value();
4009
+ VALUE res = rb_hash_new();
4010
+ rb_hash_aset(res, rb_id2sym(rb_intern("id")), cb_str_new(item.key));
4011
+ if (item.body.has_value()) {
4012
+ auto body = item.body.value();
4013
+ rb_hash_aset(res, rb_id2sym(rb_intern("id")), cb_str_new(item.key));
4014
+ rb_hash_aset(res, rb_id2sym(rb_intern("encoded")), cb_str_new(body.value));
4015
+ rb_hash_aset(res, rb_id2sym(rb_intern("cas")), cb_cas_to_num(body.cas));
4016
+ rb_hash_aset(res, rb_id2sym(rb_intern("flags")), UINT2NUM(body.flags));
4017
+ rb_hash_aset(res, rb_id2sym(rb_intern("expiry")), UINT2NUM(body.expiry));
4018
+ rb_hash_aset(res, rb_id2sym(rb_intern("id_only")), Qfalse);
4019
+ } else {
4020
+ rb_hash_aset(res, rb_id2sym(rb_intern("id_only")), Qtrue);
4021
+ }
4022
+ return res;
4023
+ } catch (const std::system_error& se) {
4024
+ rb_exc_raise(cb_map_error_code(se.code(), fmt::format("failed to perform {}: {}", __func__, se.what()), false));
4025
+ } catch (const ruby_exception& e) {
4026
+ rb_exc_raise(e.exception_object());
4027
+ }
4028
+ return Qnil;
4029
+ }
4030
+
4031
+ static VALUE
4032
+ cb_Backend_document_scan_create(VALUE self, VALUE bucket, VALUE scope, VALUE collection, VALUE scan_type, VALUE options)
4033
+ {
4034
+ const auto& cluster = cb_backend_to_cluster(self);
4035
+
4036
+ Check_Type(bucket, T_STRING);
4037
+ Check_Type(scope, T_STRING);
4038
+ Check_Type(collection, T_STRING);
4039
+ Check_Type(scan_type, T_HASH);
4040
+ if (!NIL_P(options)) {
4041
+ Check_Type(options, T_HASH);
4042
+ }
4043
+
4044
+ try {
4045
+ couchbase::core::range_scan_orchestrator_options orchestrator_options{};
4046
+ cb_extract_timeout(orchestrator_options, options);
4047
+ cb_extract_option_bool(orchestrator_options.ids_only, options, "ids_only");
4048
+ cb_extract_option_number(orchestrator_options.batch_item_limit, options, "batch_item_limit");
4049
+ cb_extract_option_number(orchestrator_options.batch_byte_limit, options, "batch_byte_limit");
4050
+ cb_extract_option_number(orchestrator_options.concurrency, options, "concurrency");
4051
+
4052
+ // Extracting the mutation state
4053
+ if (VALUE mutation_state = rb_hash_aref(options, rb_id2sym(rb_intern("mutation_state"))); !NIL_P(mutation_state)) {
4054
+ cb_check_type(mutation_state, T_ARRAY);
4055
+ auto state_size = static_cast<std::size_t>(RARRAY_LEN(mutation_state));
4056
+
4057
+ if (state_size > 0) {
4058
+ auto core_mut_state = couchbase::core::mutation_state{};
4059
+ core_mut_state.tokens.reserve(state_size);
4060
+ for (std::size_t i = 0; i < state_size; ++i) {
4061
+ VALUE token = rb_ary_entry(mutation_state, static_cast<long>(i));
4062
+ cb_check_type(token, T_HASH);
4063
+ VALUE bucket_name = rb_hash_aref(token, rb_id2sym(rb_intern("bucket_name")));
4064
+ cb_check_type(bucket_name, T_STRING);
4065
+ VALUE partition_id = rb_hash_aref(token, rb_id2sym(rb_intern("partition_id")));
4066
+ cb_check_type(partition_id, T_FIXNUM);
4067
+ VALUE partition_uuid = rb_hash_aref(token, rb_id2sym(rb_intern("partition_uuid")));
4068
+ switch (TYPE(partition_uuid)) {
4069
+ case T_FIXNUM:
4070
+ case T_BIGNUM:
4071
+ break;
4072
+ default:
4073
+ rb_raise(rb_eArgError, "partition_uuid must be an Integer");
4074
+ }
4075
+ VALUE sequence_number = rb_hash_aref(token, rb_id2sym(rb_intern("sequence_number")));
4076
+ switch (TYPE(sequence_number)) {
4077
+ case T_FIXNUM:
4078
+ case T_BIGNUM:
4079
+ break;
4080
+ default:
4081
+ rb_raise(rb_eArgError, "sequence_number must be an Integer");
4082
+ }
4083
+ core_mut_state.tokens.emplace_back(NUM2ULL(partition_uuid),
4084
+ NUM2ULL(sequence_number),
4085
+ gsl::narrow_cast<std::uint16_t>(NUM2UINT(partition_id)),
4086
+ cb_string_new(bucket_name));
4087
+ }
4088
+
4089
+ orchestrator_options.consistent_with = core_mut_state;
4090
+ }
4091
+ }
4092
+
4093
+ auto bucket_name = cb_string_new(bucket);
4094
+ auto scope_name = cb_string_new(scope);
4095
+ auto collection_name = cb_string_new(collection);
4096
+
4097
+ // Getting the operation agent
4098
+ auto agent_group = couchbase::core::agent_group(cluster->io_context(), couchbase::core::agent_group_config{ { cluster } });
4099
+ agent_group.open_bucket(bucket_name);
4100
+ auto agent = agent_group.get_agent(bucket_name);
4101
+ if (!agent.has_value()) {
4102
+ rb_raise(eCouchbaseError, "Cannot perform scan operation. Unable to get operation agent");
4103
+ return Qnil;
4104
+ }
4105
+
4106
+ // Getting the vbucket map
4107
+ auto barrier = std::make_shared<std::promise<tl::expected<couchbase::core::topology::configuration, std::error_code>>>();
4108
+ auto f = barrier->get_future();
4109
+ cluster->with_bucket_configuration(bucket_name,
4110
+ [barrier](std::error_code ec, const couchbase::core::topology::configuration& config) mutable {
4111
+ if (ec) {
4112
+ return barrier->set_value(tl::unexpected(ec));
4113
+ }
4114
+ barrier->set_value(config);
4115
+ });
4116
+ auto config = cb_wait_for_future(f);
4117
+ if (!config.has_value()) {
4118
+ rb_raise(eCouchbaseError, "Cannot perform scan operation. Unable to get bucket configuration");
4119
+ return Qnil;
4120
+ }
4121
+ if (!config->supports_range_scan()) {
4122
+ rb_raise(eFeatureNotAvailable, "Server does not support key-value scan operations");
4123
+ return Qnil;
4124
+ }
4125
+ auto vbucket_map = config->vbmap;
4126
+ if (!vbucket_map || vbucket_map->empty()) {
4127
+ rb_raise(eCouchbaseError, "Cannot perform scan operation. Unable to get vbucket map");
4128
+ return Qnil;
4129
+ }
4130
+
4131
+ // Constructing the scan type
4132
+ std::variant<std::monostate, couchbase::core::range_scan, couchbase::core::prefix_scan, couchbase::core::sampling_scan>
4133
+ core_scan_type{};
4134
+ ID scan_type_id = rb_sym2id(rb_hash_aref(scan_type, rb_id2sym(rb_intern("scan_type"))));
4135
+ if (scan_type_id == rb_intern("range")) {
4136
+ auto range_scan = couchbase::core::range_scan{};
4137
+
4138
+ VALUE from_hash = rb_hash_aref(scan_type, rb_id2sym(rb_intern("from")));
4139
+ VALUE to_hash = rb_hash_aref(scan_type, rb_id2sym(rb_intern("to")));
4140
+
4141
+ if (!NIL_P(from_hash)) {
4142
+ Check_Type(from_hash, T_HASH);
4143
+ range_scan.from = couchbase::core::scan_term{};
4144
+ cb_extract_option_string(range_scan.from->term, from_hash, "term");
4145
+ cb_extract_option_bool(range_scan.from->exclusive, from_hash, "exclusive");
4146
+ }
4147
+ if (!NIL_P(to_hash)) {
4148
+ Check_Type(to_hash, T_HASH);
4149
+ range_scan.to = couchbase::core::scan_term{};
4150
+ cb_extract_option_string(range_scan.to->term, to_hash, "term");
4151
+ cb_extract_option_bool(range_scan.to->exclusive, to_hash, "exclusive");
4152
+ }
4153
+ core_scan_type = range_scan;
4154
+ } else if (scan_type_id == rb_intern("prefix")) {
4155
+ auto prefix_scan = couchbase::core::prefix_scan{};
4156
+ cb_extract_option_string(prefix_scan.prefix, scan_type, "prefix");
4157
+ core_scan_type = prefix_scan;
4158
+ } else if (scan_type_id == rb_intern("sampling")) {
4159
+ auto sampling_scan = couchbase::core::sampling_scan{};
4160
+ cb_extract_option_number(sampling_scan.limit, scan_type, "limit");
4161
+ cb_extract_option_number(sampling_scan.seed, scan_type, "seed");
4162
+ core_scan_type = sampling_scan;
4163
+ } else {
4164
+ rb_raise(eInvalidArgument, "Invalid scan operation type");
4165
+ }
4166
+
4167
+ auto orchestrator = couchbase::core::range_scan_orchestrator(
4168
+ cluster->io_context(), agent.value(), vbucket_map.value(), scope_name, collection_name, core_scan_type, orchestrator_options);
4169
+
4170
+ // Start the scan
4171
+ auto resp = orchestrator.scan();
4172
+ if (!resp.has_value()) {
4173
+ cb_throw_error_code(resp.error(), "unable to start scan");
4174
+ }
4175
+
4176
+ // Wrap core scan_result inside Ruby ScanResult
4177
+ // Creating a Ruby CoreScanResult object *after* checking that no error occurred during orchestrator.scan()
4178
+ VALUE cCoreScanResult = rb_define_class_under(rb_define_module("Couchbase"), "CoreScanResult", rb_cObject);
4179
+ rb_define_alloc_func(cCoreScanResult, cb_CoreScanResult_allocate);
4180
+ rb_define_method(cCoreScanResult, "next_item", VALUE_FUNC(cb_CoreScanResult_next_item), 0);
4181
+ rb_define_method(cCoreScanResult, "cancelled?", VALUE_FUNC(cb_CoreScanResult_is_cancelled), 0);
4182
+ rb_define_method(cCoreScanResult, "cancel", VALUE_FUNC(cb_CoreScanResult_cancel), 0);
4183
+ VALUE core_scan_result_obj = rb_class_new_instance(0, NULL, cCoreScanResult);
4184
+ rb_ivar_set(core_scan_result_obj, rb_intern("@backend"), self);
4185
+ cb_core_scan_result_data* data = nullptr;
4186
+ TypedData_Get_Struct(core_scan_result_obj, cb_core_scan_result_data, &cb_core_scan_result_type, data);
4187
+ data->scan_result = std::make_unique<couchbase::core::scan_result>(resp.value());
4188
+ return core_scan_result_obj;
4189
+
4190
+ } catch (const std::system_error& se) {
4191
+ rb_exc_raise(cb_map_error_code(se.code(), fmt::format("failed to perform {}: {}", __func__, se.what()), false));
4192
+ } catch (const ruby_exception& e) {
4193
+ rb_exc_raise(e.exception_object());
4194
+ }
4195
+ return Qnil;
4196
+ }
4197
+
3664
4198
  static int
3665
4199
  cb_for_each_named_param(VALUE key, VALUE value, VALUE arg)
3666
4200
  {
@@ -3696,6 +4230,7 @@ cb_Backend_document_query(VALUE self, VALUE statement, VALUE options)
3696
4230
  cb_extract_option_bool(req.readonly, options, "readonly");
3697
4231
  cb_extract_option_bool(req.flex_index, options, "flex_index");
3698
4232
  cb_extract_option_bool(req.preserve_expiry, options, "preserve_expiry");
4233
+ cb_extract_option_bool(req.use_replica, options, "use_replica");
3699
4234
  cb_extract_option_uint64(req.scan_cap, options, "scan_cap");
3700
4235
  cb_extract_duration(req.scan_wait, options, "scan_wait");
3701
4236
  cb_extract_option_uint64(req.max_parallelism, options, "max_parallelism");
@@ -3833,18 +4368,20 @@ cb_Backend_document_query(VALUE self, VALUE statement, VALUE options)
3833
4368
  static void
3834
4369
  cb_generate_bucket_settings(VALUE bucket, couchbase::core::management::cluster::bucket_settings& entry, bool is_create)
3835
4370
  {
3836
- if (VALUE bucket_type = rb_hash_aref(bucket, rb_id2sym(rb_intern("bucket_type"))); TYPE(bucket_type) == T_SYMBOL) {
3837
- if (bucket_type == rb_id2sym(rb_intern("couchbase")) || bucket_type == rb_id2sym(rb_intern("membase"))) {
3838
- entry.bucket_type = couchbase::core::management::cluster::bucket_type::couchbase;
3839
- } else if (bucket_type == rb_id2sym(rb_intern("memcached"))) {
3840
- entry.bucket_type = couchbase::core::management::cluster::bucket_type::memcached;
3841
- } else if (bucket_type == rb_id2sym(rb_intern("ephemeral"))) {
3842
- 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
+ }
3843
4382
  } else {
3844
- 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));
3845
4384
  }
3846
- } else {
3847
- throw ruby_exception(rb_eArgError, rb_sprintf("bucket type must be a Symbol, given %+" PRIsVALUE, bucket_type));
3848
4385
  }
3849
4386
 
3850
4387
  if (VALUE name = rb_hash_aref(bucket, rb_id2sym(rb_intern("name"))); TYPE(name) == T_STRING) {
@@ -3853,10 +4390,12 @@ cb_generate_bucket_settings(VALUE bucket, couchbase::core::management::cluster::
3853
4390
  throw ruby_exception(rb_eArgError, rb_sprintf("bucket name must be a String, given %+" PRIsVALUE, name));
3854
4391
  }
3855
4392
 
3856
- if (VALUE quota = rb_hash_aref(bucket, rb_id2sym(rb_intern("ram_quota_mb"))); TYPE(quota) == T_FIXNUM) {
3857
- entry.ram_quota_mb = FIX2ULONG(quota);
3858
- } else {
3859
- 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
+ }
3860
4399
  }
3861
4400
 
3862
4401
  if (VALUE expiry = rb_hash_aref(bucket, rb_id2sym(rb_intern("max_expiry"))); !NIL_P(expiry)) {
@@ -3953,6 +4492,31 @@ cb_generate_bucket_settings(VALUE bucket, couchbase::core::management::cluster::
3953
4492
  }
3954
4493
  }
3955
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
+
3956
4520
  if (is_create) {
3957
4521
  if (VALUE conflict_resolution_type = rb_hash_aref(bucket, rb_id2sym(rb_intern("conflict_resolution_type")));
3958
4522
  !NIL_P(conflict_resolution_type)) {
@@ -4117,7 +4681,9 @@ cb_extract_bucket_settings(const couchbase::core::management::cluster::bucket_se
4117
4681
  rb_hash_aset(bucket, rb_id2sym(rb_intern("name")), cb_str_new(entry.name));
4118
4682
  rb_hash_aset(bucket, rb_id2sym(rb_intern("uuid")), cb_str_new(entry.uuid));
4119
4683
  rb_hash_aset(bucket, rb_id2sym(rb_intern("ram_quota_mb")), ULL2NUM(entry.ram_quota_mb));
4120
- 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
+ }
4121
4687
  switch (entry.compression_mode) {
4122
4688
  case couchbase::core::management::cluster::bucket_compression::off:
4123
4689
  rb_hash_aset(bucket, rb_id2sym(rb_intern("compression_mode")), rb_id2sym(rb_intern("off")));
@@ -4132,9 +4698,15 @@ cb_extract_bucket_settings(const couchbase::core::management::cluster::bucket_se
4132
4698
  rb_hash_aset(bucket, rb_id2sym(rb_intern("compression_mode")), Qnil);
4133
4699
  break;
4134
4700
  }
4135
- rb_hash_aset(bucket, rb_id2sym(rb_intern("num_replicas")), ULONG2NUM(entry.num_replicas));
4136
- rb_hash_aset(bucket, rb_id2sym(rb_intern("replica_indexes")), entry.replica_indexes ? Qtrue : Qfalse);
4137
- 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
+ }
4138
4710
  switch (entry.eviction_policy) {
4139
4711
  case couchbase::core::management::cluster::bucket_eviction_policy::full:
4140
4712
  rb_hash_aset(bucket, rb_id2sym(rb_intern("eviction_policy")), rb_id2sym(rb_intern("full")));
@@ -4183,6 +4755,18 @@ cb_extract_bucket_settings(const couchbase::core::management::cluster::bucket_se
4183
4755
  break;
4184
4756
  }
4185
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
+
4186
4770
  VALUE capabilities = rb_ary_new_capa(static_cast<long>(entry.capabilities.size()));
4187
4771
  for (const auto& capa : entry.capabilities) {
4188
4772
  rb_ary_push(capabilities, cb_str_new(capa));
@@ -4864,6 +5448,10 @@ cb_Backend_scope_get_all(VALUE self, VALUE bucket_name, VALUE options)
4864
5448
  VALUE collection = rb_hash_new();
4865
5449
  rb_hash_aset(collection, rb_id2sym(rb_intern("uid")), ULL2NUM(c.uid));
4866
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
+ }
4867
5455
  rb_ary_push(collections, collection);
4868
5456
  }
4869
5457
  rb_hash_aset(scope, rb_id2sym(rb_intern("collections")), collections);
@@ -4994,13 +5582,16 @@ cb_Backend_scope_drop(VALUE self, VALUE bucket_name, VALUE scope_name, VALUE opt
4994
5582
  }
4995
5583
 
4996
5584
  static VALUE
4997
- 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)
4998
5586
  {
4999
5587
  const auto& cluster = cb_backend_to_cluster(self);
5000
5588
 
5001
5589
  Check_Type(bucket_name, T_STRING);
5002
5590
  Check_Type(scope_name, T_STRING);
5003
5591
  Check_Type(collection_name, T_STRING);
5592
+ if (!NIL_P(settings)) {
5593
+ Check_Type(settings, T_HASH);
5594
+ }
5004
5595
  if (!NIL_P(options)) {
5005
5596
  Check_Type(options, T_HASH);
5006
5597
  }
@@ -5011,10 +5602,20 @@ cb_Backend_collection_create(VALUE self, VALUE bucket_name, VALUE scope_name, VA
5011
5602
  cb_string_new(collection_name) };
5012
5603
  cb_extract_timeout(req, options);
5013
5604
 
5014
- if (!NIL_P(max_expiry)) {
5015
- Check_Type(max_expiry, T_FIXNUM);
5016
- 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
+ }
5017
5617
  }
5618
+
5018
5619
  auto barrier = std::make_shared<std::promise<couchbase::core::operations::management::collection_create_response>>();
5019
5620
  auto f = barrier->get_future();
5020
5621
  cluster->execute(req, [barrier](couchbase::core::operations::management::collection_create_response&& resp) {
@@ -5036,6 +5637,62 @@ cb_Backend_collection_create(VALUE self, VALUE bucket_name, VALUE scope_name, VA
5036
5637
  return Qnil;
5037
5638
  }
5038
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
+
5039
5696
  static VALUE
5040
5697
  cb_Backend_collection_drop(VALUE self, VALUE bucket_name, VALUE scope_name, VALUE collection_name, VALUE options)
5041
5698
  {
@@ -8577,10 +9234,20 @@ cb_Backend_form_encode(VALUE self, VALUE data)
8577
9234
  return cb_str_new(encoded);
8578
9235
  }
8579
9236
 
9237
+ static VALUE
9238
+ cb_Backend_enable_protocol_logger_to_save_network_traffic_to_file(VALUE /* self */, VALUE path)
9239
+ {
9240
+ Check_Type(path, T_STRING);
9241
+ couchbase::core::logger::configuration configuration{};
9242
+ configuration.filename = cb_string_new(path);
9243
+ couchbase::core::logger::create_protocol_logger(configuration);
9244
+ return Qnil;
9245
+ }
9246
+
8580
9247
  static void
8581
9248
  init_backend(VALUE mCouchbase)
8582
9249
  {
8583
- VALUE cBackend = rb_define_class_under(mCouchbase, "Backend", rb_cBasicObject);
9250
+ VALUE cBackend = rb_define_class_under(mCouchbase, "Backend", rb_cObject);
8584
9251
  rb_define_alloc_func(cBackend, cb_Backend_allocate);
8585
9252
  rb_define_method(cBackend, "open", VALUE_FUNC(cb_Backend_open), 3);
8586
9253
  rb_define_method(cBackend, "close", VALUE_FUNC(cb_Backend_close), 0);
@@ -8604,7 +9271,10 @@ init_backend(VALUE mCouchbase)
8604
9271
  rb_define_method(cBackend, "document_remove", VALUE_FUNC(cb_Backend_document_remove), 5);
8605
9272
  rb_define_method(cBackend, "document_remove_multi", VALUE_FUNC(cb_Backend_document_remove_multi), 5);
8606
9273
  rb_define_method(cBackend, "document_lookup_in", VALUE_FUNC(cb_Backend_document_lookup_in), 6);
9274
+ rb_define_method(cBackend, "document_lookup_in_any_replica", VALUE_FUNC(cb_Backend_document_lookup_in_any_replica), 6);
9275
+ rb_define_method(cBackend, "document_lookup_in_all_replicas", VALUE_FUNC(cb_Backend_document_lookup_in_all_replicas), 6);
8607
9276
  rb_define_method(cBackend, "document_mutate_in", VALUE_FUNC(cb_Backend_document_mutate_in), 6);
9277
+ rb_define_method(cBackend, "document_scan_create", VALUE_FUNC(cb_Backend_document_scan_create), 5);
8608
9278
  rb_define_method(cBackend, "document_query", VALUE_FUNC(cb_Backend_document_query), 2);
8609
9279
  rb_define_method(cBackend, "document_touch", VALUE_FUNC(cb_Backend_document_touch), 6);
8610
9280
  rb_define_method(cBackend, "document_exists", VALUE_FUNC(cb_Backend_document_exists), 5);
@@ -8640,6 +9310,7 @@ init_backend(VALUE mCouchbase)
8640
9310
  rb_define_method(cBackend, "scope_create", VALUE_FUNC(cb_Backend_scope_create), 3);
8641
9311
  rb_define_method(cBackend, "scope_drop", VALUE_FUNC(cb_Backend_scope_drop), 3);
8642
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);
8643
9314
  rb_define_method(cBackend, "collection_drop", VALUE_FUNC(cb_Backend_collection_drop), 4);
8644
9315
 
8645
9316
  rb_define_method(cBackend, "query_index_get_all", VALUE_FUNC(cb_Backend_query_index_get_all), 2);
@@ -8706,6 +9377,10 @@ init_backend(VALUE mCouchbase)
8706
9377
  rb_define_singleton_method(cBackend, "query_escape", VALUE_FUNC(cb_Backend_query_escape), 1);
8707
9378
  rb_define_singleton_method(cBackend, "path_escape", VALUE_FUNC(cb_Backend_path_escape), 1);
8708
9379
  rb_define_singleton_method(cBackend, "form_encode", VALUE_FUNC(cb_Backend_form_encode), 1);
9380
+ rb_define_singleton_method(cBackend,
9381
+ "enable_protocol_logger_to_save_network_traffic_to_file",
9382
+ VALUE_FUNC(cb_Backend_enable_protocol_logger_to_save_network_traffic_to_file),
9383
+ 1);
8709
9384
  }
8710
9385
 
8711
9386
  void