couchbase 3.4.3 → 3.4.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (179) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/ext/couchbase/CMakeLists.txt +22 -1
  4. data/ext/couchbase/core/bucket.cxx +183 -152
  5. data/ext/couchbase/core/bucket.hxx +17 -4
  6. data/ext/couchbase/core/cluster.hxx +41 -13
  7. data/ext/couchbase/core/cluster_options.hxx +3 -0
  8. data/ext/couchbase/core/crud_component.cxx +51 -22
  9. data/ext/couchbase/core/error_context/key_value.cxx +2 -1
  10. data/ext/couchbase/core/error_context/key_value.hxx +10 -12
  11. data/ext/couchbase/core/impl/build_deferred_query_indexes.cxx +115 -50
  12. data/ext/couchbase/core/impl/cluster.cxx +6 -0
  13. data/ext/couchbase/core/impl/create_bucket.cxx +158 -0
  14. data/ext/couchbase/core/impl/create_collection.cxx +83 -0
  15. data/ext/couchbase/core/impl/create_query_index.cxx +172 -59
  16. data/ext/couchbase/core/impl/create_scope.cxx +69 -0
  17. data/ext/couchbase/core/impl/dns_srv_tracker.cxx +2 -1
  18. data/ext/couchbase/core/impl/drop_bucket.cxx +66 -0
  19. data/ext/couchbase/core/impl/drop_collection.cxx +76 -0
  20. data/ext/couchbase/core/impl/drop_query_index.cxx +138 -59
  21. data/ext/couchbase/core/impl/drop_scope.cxx +68 -0
  22. data/ext/couchbase/core/impl/flush_bucket.cxx +66 -0
  23. data/ext/couchbase/core/impl/get_all_buckets.cxx +178 -0
  24. data/ext/couchbase/core/impl/get_all_query_indexes.cxx +67 -37
  25. data/ext/couchbase/core/impl/get_all_scopes.cxx +94 -0
  26. data/ext/couchbase/core/impl/get_bucket.cxx +168 -0
  27. data/ext/couchbase/core/impl/internal_manager_error_context.cxx +113 -0
  28. data/ext/couchbase/core/impl/internal_manager_error_context.hxx +60 -0
  29. data/ext/couchbase/core/impl/key_value_error_category.cxx +2 -4
  30. data/ext/couchbase/core/impl/key_value_error_context.cxx +98 -0
  31. data/ext/couchbase/core/impl/lookup_in.cxx +1 -0
  32. data/ext/couchbase/core/impl/lookup_in_all_replicas.cxx +178 -0
  33. data/ext/couchbase/core/impl/lookup_in_all_replicas.hxx +80 -0
  34. data/ext/couchbase/core/impl/lookup_in_any_replica.cxx +169 -0
  35. data/ext/couchbase/core/impl/lookup_in_any_replica.hxx +75 -0
  36. data/ext/couchbase/core/impl/lookup_in_replica.cxx +104 -0
  37. data/ext/couchbase/core/impl/lookup_in_replica.hxx +67 -0
  38. data/ext/couchbase/core/impl/manager_error_context.cxx +100 -0
  39. data/ext/couchbase/core/impl/query.cxx +1 -0
  40. data/ext/couchbase/core/impl/query_error_context.cxx +75 -0
  41. data/ext/couchbase/core/impl/update_bucket.cxx +133 -0
  42. data/ext/couchbase/core/impl/update_collection.cxx +83 -0
  43. data/ext/couchbase/core/impl/watch_query_indexes.cxx +53 -29
  44. data/ext/couchbase/core/io/dns_client.cxx +111 -40
  45. data/ext/couchbase/core/io/dns_config.cxx +5 -4
  46. data/ext/couchbase/core/io/http_session.hxx +24 -1
  47. data/ext/couchbase/core/io/mcbp_command.hxx +9 -2
  48. data/ext/couchbase/core/io/mcbp_session.cxx +80 -43
  49. data/ext/couchbase/core/io/mcbp_session.hxx +4 -3
  50. data/ext/couchbase/core/logger/custom_rotating_file_sink.cxx +1 -1
  51. data/ext/couchbase/core/logger/logger.cxx +80 -20
  52. data/ext/couchbase/core/logger/logger.hxx +31 -0
  53. data/ext/couchbase/core/management/bucket_settings.hxx +8 -5
  54. data/ext/couchbase/core/management/bucket_settings_json.hxx +12 -2
  55. data/ext/couchbase/core/meta/features.hxx +42 -0
  56. data/ext/couchbase/core/operations/document_lookup_in.cxx +8 -1
  57. data/ext/couchbase/core/operations/document_lookup_in_all_replicas.hxx +192 -0
  58. data/ext/couchbase/core/operations/document_lookup_in_any_replica.hxx +188 -0
  59. data/ext/couchbase/core/operations/document_query.cxx +11 -0
  60. data/ext/couchbase/core/operations/document_query.hxx +1 -0
  61. data/ext/couchbase/core/operations/management/CMakeLists.txt +1 -0
  62. data/ext/couchbase/core/operations/management/bucket_create.cxx +30 -9
  63. data/ext/couchbase/core/operations/management/bucket_update.cxx +27 -6
  64. data/ext/couchbase/core/operations/management/collection_create.cxx +5 -1
  65. data/ext/couchbase/core/operations/management/collection_create.hxx +1 -0
  66. data/ext/couchbase/core/operations/management/collection_update.cxx +87 -0
  67. data/ext/couchbase/core/operations/management/collection_update.hxx +54 -0
  68. data/ext/couchbase/core/operations/management/collections.hxx +1 -0
  69. data/ext/couchbase/core/operations.hxx +2 -0
  70. data/ext/couchbase/core/origin.cxx +270 -0
  71. data/ext/couchbase/core/origin.hxx +2 -0
  72. data/ext/couchbase/core/protocol/client_response.hxx +1 -0
  73. data/ext/couchbase/core/protocol/cmd_hello.hxx +1 -0
  74. data/ext/couchbase/core/protocol/cmd_lookup_in_replica.cxx +107 -0
  75. data/ext/couchbase/core/protocol/cmd_lookup_in_replica.hxx +137 -0
  76. data/ext/couchbase/core/protocol/hello_feature.hxx +6 -0
  77. data/ext/couchbase/core/protocol/hello_feature_fmt.hxx +3 -0
  78. data/ext/couchbase/core/protocol/status.cxx +2 -2
  79. data/ext/couchbase/core/range_scan_options.cxx +3 -27
  80. data/ext/couchbase/core/range_scan_options.hxx +13 -17
  81. data/ext/couchbase/core/range_scan_orchestrator.cxx +388 -170
  82. data/ext/couchbase/core/range_scan_orchestrator.hxx +13 -2
  83. data/ext/couchbase/core/range_scan_orchestrator_options.hxx +5 -3
  84. data/ext/couchbase/core/scan_options.hxx +0 -19
  85. data/ext/couchbase/core/scan_result.cxx +19 -5
  86. data/ext/couchbase/core/scan_result.hxx +5 -2
  87. data/ext/couchbase/core/timeout_defaults.hxx +3 -4
  88. data/ext/couchbase/core/topology/capabilities.hxx +4 -0
  89. data/ext/couchbase/core/topology/capabilities_fmt.hxx +11 -0
  90. data/ext/couchbase/core/topology/collections_manifest.hxx +2 -0
  91. data/ext/couchbase/core/topology/collections_manifest_fmt.hxx +1 -1
  92. data/ext/couchbase/core/topology/collections_manifest_json.hxx +3 -0
  93. data/ext/couchbase/core/topology/configuration.hxx +20 -0
  94. data/ext/couchbase/core/topology/configuration_json.hxx +8 -1
  95. data/ext/couchbase/core/utils/connection_string.cxx +62 -47
  96. data/ext/couchbase/core/utils/connection_string.hxx +1 -0
  97. data/ext/couchbase/couchbase/analytics_error_context.hxx +1 -1
  98. data/ext/couchbase/couchbase/behavior_options.hxx +19 -2
  99. data/ext/couchbase/couchbase/bucket.hxx +14 -0
  100. data/ext/couchbase/couchbase/bucket_manager.hxx +135 -0
  101. data/ext/couchbase/couchbase/build_query_index_options.hxx +0 -30
  102. data/ext/couchbase/couchbase/cluster.hxx +14 -0
  103. data/ext/couchbase/couchbase/collection.hxx +111 -0
  104. data/ext/couchbase/couchbase/collection_manager.hxx +160 -0
  105. data/ext/couchbase/couchbase/collection_query_index_manager.hxx +7 -48
  106. data/ext/couchbase/couchbase/create_bucket_options.hxx +41 -0
  107. data/ext/couchbase/couchbase/create_collection_options.hxx +44 -0
  108. data/ext/couchbase/couchbase/create_primary_query_index_options.hxx +0 -29
  109. data/ext/couchbase/couchbase/create_query_index_options.hxx +0 -33
  110. data/ext/couchbase/couchbase/create_scope_options.hxx +41 -0
  111. data/ext/couchbase/couchbase/drop_bucket_options.hxx +41 -0
  112. data/ext/couchbase/couchbase/drop_collection_options.hxx +41 -0
  113. data/ext/couchbase/couchbase/drop_primary_query_index_options.hxx +0 -30
  114. data/ext/couchbase/couchbase/drop_query_index_options.hxx +0 -31
  115. data/ext/couchbase/couchbase/drop_scope_options.hxx +41 -0
  116. data/ext/couchbase/couchbase/error_codes.hxx +1 -2
  117. data/ext/couchbase/couchbase/error_context.hxx +10 -2
  118. data/ext/couchbase/couchbase/flush_bucket_options.hxx +41 -0
  119. data/ext/couchbase/{core/topology/error_map_fmt.hxx → couchbase/fmt/key_value_error_map_attribute.hxx} +21 -21
  120. data/ext/couchbase/couchbase/get_all_buckets_options.hxx +44 -0
  121. data/ext/couchbase/couchbase/get_all_query_indexes_options.hxx +0 -30
  122. data/ext/couchbase/couchbase/get_all_scopes_options.hxx +44 -0
  123. data/ext/couchbase/couchbase/get_and_lock_options.hxx +2 -2
  124. data/ext/couchbase/couchbase/get_and_touch_options.hxx +2 -2
  125. data/ext/couchbase/couchbase/get_bucket_options.hxx +43 -0
  126. data/ext/couchbase/couchbase/get_options.hxx +2 -2
  127. data/ext/couchbase/couchbase/insert_options.hxx +3 -3
  128. data/ext/couchbase/couchbase/key_value_error_context.hxx +7 -2
  129. data/ext/couchbase/couchbase/lookup_in_all_replicas_options.hxx +109 -0
  130. data/ext/couchbase/couchbase/lookup_in_any_replica_options.hxx +101 -0
  131. data/ext/couchbase/couchbase/lookup_in_options.hxx +2 -2
  132. data/ext/couchbase/couchbase/lookup_in_replica_result.hxx +74 -0
  133. data/ext/couchbase/couchbase/lookup_in_result.hxx +26 -0
  134. data/ext/couchbase/couchbase/management/bucket_settings.hxx +119 -0
  135. data/ext/couchbase/couchbase/management/collection_spec.hxx +29 -0
  136. data/ext/couchbase/couchbase/management/scope_spec.hxx +29 -0
  137. data/ext/couchbase/couchbase/manager_error_context.hxx +29 -53
  138. data/ext/couchbase/couchbase/mutate_in_options.hxx +2 -2
  139. data/ext/couchbase/couchbase/query_error_context.hxx +3 -1
  140. data/ext/couchbase/couchbase/query_index_manager.hxx +16 -83
  141. data/ext/couchbase/couchbase/query_options.hxx +18 -0
  142. data/ext/couchbase/couchbase/remove_options.hxx +2 -2
  143. data/ext/couchbase/couchbase/replace_options.hxx +3 -3
  144. data/ext/couchbase/couchbase/security_options.hxx +15 -0
  145. data/ext/couchbase/couchbase/subdocument_error_context.hxx +4 -2
  146. data/ext/couchbase/couchbase/touch_options.hxx +2 -2
  147. data/ext/couchbase/couchbase/unlock_options.hxx +2 -2
  148. data/ext/couchbase/couchbase/update_bucket_options.hxx +41 -0
  149. data/ext/couchbase/couchbase/update_collection_options.hxx +44 -0
  150. data/ext/couchbase/couchbase/upsert_options.hxx +3 -3
  151. data/ext/couchbase/couchbase/watch_query_indexes_options.hxx +0 -31
  152. data/ext/couchbase/test/CMakeLists.txt +1 -0
  153. data/ext/couchbase/test/test_integration_collections.cxx +6 -0
  154. data/ext/couchbase/test/test_integration_crud.cxx +5 -0
  155. data/ext/couchbase/test/test_integration_examples.cxx +137 -1
  156. data/ext/couchbase/test/test_integration_management.cxx +1009 -309
  157. data/ext/couchbase/test/test_integration_query.cxx +19 -7
  158. data/ext/couchbase/test/test_integration_range_scan.cxx +351 -112
  159. data/ext/couchbase/test/test_integration_search.cxx +10 -1
  160. data/ext/couchbase/test/test_integration_subdoc.cxx +721 -7
  161. data/ext/couchbase/test/test_transaction_public_async_api.cxx +13 -12
  162. data/ext/couchbase/test/test_transaction_public_blocking_api.cxx +27 -21
  163. data/ext/couchbase/test/test_unit_connection_string.cxx +29 -0
  164. data/ext/couchbase/test/test_unit_query.cxx +75 -0
  165. data/ext/couchbase.cxx +735 -60
  166. data/ext/revisions.rb +3 -3
  167. data/lib/couchbase/cluster.rb +1 -1
  168. data/lib/couchbase/collection.rb +108 -0
  169. data/lib/couchbase/collection_options.rb +100 -1
  170. data/lib/couchbase/errors.rb +5 -0
  171. data/lib/couchbase/key_value_scan.rb +125 -0
  172. data/lib/couchbase/management/bucket_manager.rb +22 -15
  173. data/lib/couchbase/management/collection_manager.rb +158 -9
  174. data/lib/couchbase/options.rb +151 -0
  175. data/lib/couchbase/scope.rb +1 -1
  176. data/lib/couchbase/utils/time.rb +14 -1
  177. data/lib/couchbase/version.rb +1 -1
  178. metadata +59 -8
  179. data/ext/couchbase/core/impl/collection_query_index_manager.cxx +0 -93
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