couchbase 3.0.0.beta.1-universal-darwin-19 → 3.0.0-universal-darwin-19

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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +227 -0
  3. data/.rubocop_todo.yml +47 -0
  4. data/CONTRIBUTING.md +110 -0
  5. data/Gemfile +4 -0
  6. data/README.md +3 -3
  7. data/Rakefile +1 -1
  8. data/couchbase.gemspec +40 -39
  9. data/examples/analytics.rb +123 -108
  10. data/examples/auth.rb +33 -0
  11. data/examples/crud.rb +16 -2
  12. data/examples/managing_analytics_indexes.rb +18 -4
  13. data/examples/managing_buckets.rb +17 -3
  14. data/examples/managing_collections.rb +22 -9
  15. data/examples/managing_query_indexes.rb +38 -18
  16. data/examples/managing_search_indexes.rb +21 -6
  17. data/examples/managing_view_indexes.rb +18 -4
  18. data/examples/query.rb +17 -3
  19. data/examples/query_with_consistency.rb +30 -20
  20. data/examples/search.rb +116 -101
  21. data/examples/search_with_consistency.rb +43 -30
  22. data/examples/subdocument.rb +42 -30
  23. data/examples/view.rb +19 -10
  24. data/ext/CMakeLists.txt +40 -2
  25. data/ext/build_version.hxx.in +1 -1
  26. data/ext/couchbase/bucket.hxx +190 -38
  27. data/ext/couchbase/cluster.hxx +22 -4
  28. data/ext/couchbase/configuration.hxx +14 -14
  29. data/ext/couchbase/couchbase.cxx +108 -12
  30. data/ext/couchbase/error_map.hxx +202 -2
  31. data/ext/couchbase/errors.hxx +8 -2
  32. data/ext/couchbase/io/dns_client.hxx +6 -6
  33. data/ext/couchbase/io/http_command.hxx +2 -2
  34. data/ext/couchbase/io/http_session.hxx +7 -11
  35. data/ext/couchbase/io/http_session_manager.hxx +3 -3
  36. data/ext/couchbase/io/mcbp_command.hxx +101 -44
  37. data/ext/couchbase/io/mcbp_session.hxx +144 -49
  38. data/ext/couchbase/io/retry_action.hxx +30 -0
  39. data/ext/couchbase/io/retry_context.hxx +39 -0
  40. data/ext/couchbase/io/retry_orchestrator.hxx +96 -0
  41. data/ext/couchbase/io/retry_reason.hxx +235 -0
  42. data/ext/couchbase/io/retry_strategy.hxx +156 -0
  43. data/ext/couchbase/operations/document_decrement.hxx +2 -0
  44. data/ext/couchbase/operations/document_exists.hxx +2 -0
  45. data/ext/couchbase/operations/document_get.hxx +2 -0
  46. data/ext/couchbase/operations/document_get_and_lock.hxx +2 -0
  47. data/ext/couchbase/operations/document_get_and_touch.hxx +2 -0
  48. data/ext/couchbase/operations/document_get_projected.hxx +2 -0
  49. data/ext/couchbase/operations/document_increment.hxx +2 -0
  50. data/ext/couchbase/operations/document_insert.hxx +2 -0
  51. data/ext/couchbase/operations/document_lookup_in.hxx +2 -0
  52. data/ext/couchbase/operations/document_mutate_in.hxx +3 -0
  53. data/ext/couchbase/operations/document_query.hxx +10 -0
  54. data/ext/couchbase/operations/document_remove.hxx +2 -0
  55. data/ext/couchbase/operations/document_replace.hxx +2 -0
  56. data/ext/couchbase/operations/document_search.hxx +8 -3
  57. data/ext/couchbase/operations/document_touch.hxx +2 -0
  58. data/ext/couchbase/operations/document_unlock.hxx +2 -0
  59. data/ext/couchbase/operations/document_upsert.hxx +2 -0
  60. data/ext/couchbase/operations/query_index_create.hxx +14 -4
  61. data/ext/couchbase/operations/query_index_drop.hxx +12 -2
  62. data/ext/couchbase/operations/query_index_get_all.hxx +11 -2
  63. data/ext/couchbase/origin.hxx +47 -17
  64. data/ext/couchbase/platform/backtrace.c +189 -0
  65. data/ext/couchbase/platform/backtrace.h +54 -0
  66. data/ext/couchbase/platform/terminate_handler.cc +122 -0
  67. data/ext/couchbase/platform/terminate_handler.h +36 -0
  68. data/ext/couchbase/protocol/cmd_get_cluster_config.hxx +6 -1
  69. data/ext/couchbase/protocol/status.hxx +14 -4
  70. data/ext/couchbase/version.hxx +2 -2
  71. data/ext/extconf.rb +39 -36
  72. data/ext/test/main.cxx +64 -16
  73. data/lib/couchbase.rb +0 -1
  74. data/lib/couchbase/analytics_options.rb +2 -4
  75. data/lib/couchbase/authenticator.rb +14 -0
  76. data/lib/couchbase/binary_collection.rb +9 -9
  77. data/lib/couchbase/binary_collection_options.rb +8 -6
  78. data/lib/couchbase/bucket.rb +18 -18
  79. data/lib/couchbase/cluster.rb +121 -90
  80. data/lib/couchbase/collection.rb +36 -38
  81. data/lib/couchbase/collection_options.rb +31 -17
  82. data/lib/couchbase/common_options.rb +1 -1
  83. data/lib/couchbase/datastructures/couchbase_list.rb +16 -16
  84. data/lib/couchbase/datastructures/couchbase_map.rb +18 -18
  85. data/lib/couchbase/datastructures/couchbase_queue.rb +13 -13
  86. data/lib/couchbase/datastructures/couchbase_set.rb +8 -7
  87. data/lib/couchbase/errors.rb +10 -3
  88. data/lib/couchbase/json_transcoder.rb +2 -2
  89. data/lib/couchbase/libcouchbase.bundle +0 -0
  90. data/lib/couchbase/management/analytics_index_manager.rb +37 -37
  91. data/lib/couchbase/management/bucket_manager.rb +25 -25
  92. data/lib/couchbase/management/collection_manager.rb +3 -3
  93. data/lib/couchbase/management/query_index_manager.rb +59 -14
  94. data/lib/couchbase/management/search_index_manager.rb +15 -12
  95. data/lib/couchbase/management/user_manager.rb +1 -1
  96. data/lib/couchbase/management/view_index_manager.rb +11 -5
  97. data/lib/couchbase/mutation_state.rb +12 -0
  98. data/lib/couchbase/query_options.rb +23 -9
  99. data/lib/couchbase/scope.rb +61 -1
  100. data/lib/couchbase/search_options.rb +40 -27
  101. data/lib/couchbase/subdoc.rb +31 -28
  102. data/lib/couchbase/version.rb +2 -2
  103. data/lib/couchbase/view_options.rb +0 -1
  104. metadata +20 -7
@@ -53,7 +53,8 @@ class cluster
53
53
  {
54
54
  origin_ = origin;
55
55
  if (origin_.options().enable_dns_srv) {
56
- return do_dns_srv(std::forward<Handler>(handler));
56
+ return asio::post(asio::bind_executor(
57
+ ctx_, [this, handler = std::forward<Handler>(handler)]() mutable { return do_dns_srv(std::forward<Handler>(handler)); }));
57
58
  }
58
59
  do_open(std::forward<Handler>(handler));
59
60
  }
@@ -63,7 +64,7 @@ class cluster
63
64
  {
64
65
  asio::post(asio::bind_executor(ctx_, [this, handler = std::forward<Handler>(handler)]() {
65
66
  if (session_) {
66
- session_->stop();
67
+ session_->stop(io::retry_reason::do_not_retry);
67
68
  }
68
69
  for (auto& bucket : buckets_) {
69
70
  bucket.second->close();
@@ -103,7 +104,7 @@ class cluster
103
104
  template<class Request, class Handler>
104
105
  void execute_http(Request request, Handler&& handler)
105
106
  {
106
- auto session = session_manager_->check_out(Request::type, origin_.get_username(), origin_.get_password());
107
+ auto session = session_manager_->check_out(Request::type, origin_.credentials());
107
108
  if (!session) {
108
109
  return handler(operations::make_response(std::make_error_code(error::common_errc::service_not_available), request, {}));
109
110
  }
@@ -157,9 +158,11 @@ class cluster
157
158
  tls_.set_options(asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2 | asio::ssl::context::no_sslv3);
158
159
  if (!origin_.options().trust_certificate.empty()) {
159
160
  std::error_code ec{};
161
+ spdlog::debug(R"([{}]: use TLS certificate chain: "{}")", id_, origin_.options().trust_certificate);
160
162
  tls_.use_certificate_chain_file(origin_.options().trust_certificate, ec);
161
163
  if (ec) {
162
- spdlog::error("unable to load certificate chain \"{}\": {}", origin_.options().trust_certificate, ec.message());
164
+ spdlog::error(
165
+ "[{}]: unable to load certificate chain \"{}\": {}", id_, origin_.options().trust_certificate, ec.message());
163
166
  return handler(ec);
164
167
  }
165
168
  }
@@ -172,6 +175,21 @@ class cluster
172
175
  "(https://wiki.wireshark.org/TLS). DO NOT USE THIS BUILD IN PRODUCTION",
173
176
  TLS_KEY_LOG_FILE);
174
177
  #endif
178
+ if (origin_.credentials().uses_certificate()) {
179
+ std::error_code ec{};
180
+ spdlog::debug(R"([{}]: use TLS certificate: "{}")", id_, origin_.certificate_path());
181
+ tls_.use_certificate_file(origin_.certificate_path(), asio::ssl::context::file_format::pem, ec);
182
+ if (ec) {
183
+ spdlog::error("[{}]: unable to load certificate \"{}\": {}", id_, origin_.certificate_path(), ec.message());
184
+ return handler(ec);
185
+ }
186
+ spdlog::debug(R"([{}]: use TLS private key: "{}")", id_, origin_.key_path());
187
+ tls_.use_private_key_file(origin_.key_path(), asio::ssl::context::file_format::pem, ec);
188
+ if (ec) {
189
+ spdlog::error("[{}]: unable to load private key \"{}\": {}", id_, origin_.key_path(), ec.message());
190
+ return handler(ec);
191
+ }
192
+ }
175
193
  session_ = std::make_shared<io::mcbp_session>(id_, ctx_, tls_, origin_);
176
194
  } else {
177
195
  session_ = std::make_shared<io::mcbp_session>(id_, ctx_, origin_);
@@ -30,25 +30,25 @@ namespace couchbase
30
30
  {
31
31
  struct configuration {
32
32
  struct port_map {
33
- std::optional<std::uint16_t> key_value;
34
- std::optional<std::uint16_t> management;
35
- std::optional<std::uint16_t> analytics;
36
- std::optional<std::uint16_t> search;
37
- std::optional<std::uint16_t> views;
38
- std::optional<std::uint16_t> query;
33
+ std::optional<std::uint16_t> key_value{};
34
+ std::optional<std::uint16_t> management{};
35
+ std::optional<std::uint16_t> analytics{};
36
+ std::optional<std::uint16_t> search{};
37
+ std::optional<std::uint16_t> views{};
38
+ std::optional<std::uint16_t> query{};
39
39
  };
40
40
  struct alternate_address {
41
- std::string name;
42
- std::string hostname;
41
+ std::string name{};
42
+ std::string hostname{};
43
43
  port_map services_plain{};
44
44
  port_map services_tls{};
45
45
  };
46
46
  struct node {
47
47
  bool this_node{ false };
48
- size_t index;
49
- std::string hostname;
50
- port_map services_plain;
51
- port_map services_tls;
48
+ size_t index{};
49
+ std::string hostname{};
50
+ port_map services_plain{};
51
+ port_map services_tls{};
52
52
  std::map<std::string, alternate_address> alt{};
53
53
 
54
54
  [[nodiscard]] std::uint16_t port_or(service_type type, bool is_tls, std::uint16_t default_value) const
@@ -211,14 +211,14 @@ struct configuration {
211
211
  throw std::runtime_error("no nodes marked as this_node");
212
212
  }
213
213
 
214
- std::pair<uint16_t, size_t> map_key(const std::string& key)
214
+ std::pair<std::uint16_t, std::int16_t> map_key(const std::string& key)
215
215
  {
216
216
  if (!vbmap.has_value()) {
217
217
  throw std::runtime_error("cannot map key: partition map is not available");
218
218
  }
219
219
  uint32_t crc = utils::hash_crc32(key.data(), key.size());
220
220
  uint16_t vbucket = uint16_t(crc % vbmap->size());
221
- return std::make_pair(vbucket, static_cast<std::size_t>(vbmap->at(vbucket)[0]));
221
+ return std::make_pair(vbucket, vbmap->at(vbucket)[0]);
222
222
  }
223
223
  };
224
224
 
@@ -28,6 +28,8 @@
28
28
  #include <snappy.h>
29
29
 
30
30
  #include <version.hxx>
31
+ #include <platform/terminate_handler.h>
32
+
31
33
  #include <cluster.hxx>
32
34
  #include <operations.hxx>
33
35
 
@@ -165,6 +167,7 @@ cb_Backend_allocate(VALUE klass)
165
167
  }
166
168
 
167
169
  static VALUE eCouchbaseError;
170
+ static VALUE eTimeout;
168
171
  static VALUE eAmbiguousTimeout;
169
172
  static VALUE eAuthenticationFailure;
170
173
  static VALUE eBucketExists;
@@ -197,6 +200,7 @@ static VALUE eGroupNotFound;
197
200
  static VALUE eIndexExists;
198
201
  static VALUE eIndexFailure;
199
202
  static VALUE eIndexNotFound;
203
+ static VALUE eIndexNotReady;
200
204
  static VALUE eInternalServerFailure;
201
205
  static VALUE eInvalidArgument;
202
206
  static VALUE eJobQueueFull;
@@ -235,7 +239,9 @@ init_exceptions(VALUE mCouchbase)
235
239
  VALUE mError = rb_define_module_under(mCouchbase, "Error");
236
240
  eCouchbaseError = rb_define_class_under(mError, "CouchbaseError", rb_eStandardError);
237
241
 
238
- eAmbiguousTimeout = rb_define_class_under(mError, "AmbiguousTimeout", eCouchbaseError);
242
+ eTimeout = rb_define_class_under(mError, "Timeout", eCouchbaseError);
243
+
244
+ eAmbiguousTimeout = rb_define_class_under(mError, "AmbiguousTimeout", eTimeout);
239
245
  eAuthenticationFailure = rb_define_class_under(mError, "AuthenticationFailure", eCouchbaseError);
240
246
  eBucketExists = rb_define_class_under(mError, "BucketExists", eCouchbaseError);
241
247
  eBucketNotFlushable = rb_define_class_under(mError, "BucketNotFlushable", eCouchbaseError);
@@ -267,6 +273,7 @@ init_exceptions(VALUE mCouchbase)
267
273
  eIndexExists = rb_define_class_under(mError, "IndexExists", eCouchbaseError);
268
274
  eIndexFailure = rb_define_class_under(mError, "IndexFailure", eCouchbaseError);
269
275
  eIndexNotFound = rb_define_class_under(mError, "IndexNotFound", eCouchbaseError);
276
+ eIndexNotReady = rb_define_class_under(mError, "IndexNotReady", eCouchbaseError);
270
277
  eInternalServerFailure = rb_define_class_under(mError, "InternalServerFailure", eCouchbaseError);
271
278
  eInvalidArgument = rb_define_class_under(mError, "InvalidArgument", rb_eArgError);
272
279
  eJobQueueFull = rb_define_class_under(mError, "JobQueueFull", eCouchbaseError);
@@ -286,7 +293,7 @@ init_exceptions(VALUE mCouchbase)
286
293
  eScopeNotFound = rb_define_class_under(mError, "ScopeNotFound", eCouchbaseError);
287
294
  eServiceNotAvailable = rb_define_class_under(mError, "ServiceNotAvailable", eCouchbaseError);
288
295
  eTemporaryFailure = rb_define_class_under(mError, "TemporaryFailure", eCouchbaseError);
289
- eUnambiguousTimeout = rb_define_class_under(mError, "UnambiguousTimeout", eCouchbaseError);
296
+ eUnambiguousTimeout = rb_define_class_under(mError, "UnambiguousTimeout", eTimeout);
290
297
  eUnsupportedOperation = rb_define_class_under(mError, "UnsupportedOperation", eCouchbaseError);
291
298
  eUserNotFound = rb_define_class_under(mError, "UserNotFound", eCouchbaseError);
292
299
  eUserExists = rb_define_class_under(mError, "UserExists", eCouchbaseError);
@@ -450,6 +457,11 @@ cb__map_error_code(std::error_code ec, const std::string& message)
450
457
  case couchbase::error::query_errc::prepared_statement_failure:
451
458
  return rb_exc_new_cstr(ePreparedStatementFailure, fmt::format("{}: {}", message, ec.message()).c_str());
452
459
  }
460
+ } else if (ec.category() == couchbase::error::detail::get_search_category()) {
461
+ switch (static_cast<couchbase::error::search_errc>(ec.value())) {
462
+ case couchbase::error::search_errc::index_not_ready:
463
+ return rb_exc_new_cstr(eIndexNotReady, fmt::format("{}: {}", message, ec.message()).c_str());
464
+ }
453
465
  } else if (ec.category() == couchbase::error::detail::get_view_category()) {
454
466
  switch (static_cast<couchbase::error::view_errc>(ec.value())) {
455
467
  case couchbase::error::view_errc::view_not_found:
@@ -510,7 +522,7 @@ cb__map_error_code(std::error_code ec, const std::string& message)
510
522
  }
511
523
 
512
524
  static VALUE
513
- cb_Backend_open(VALUE self, VALUE connection_string, VALUE username, VALUE password, VALUE options)
525
+ cb_Backend_open(VALUE self, VALUE connection_string, VALUE credentials, VALUE options)
514
526
  {
515
527
  cb_backend_data* backend = nullptr;
516
528
  TypedData_Get_Struct(self, cb_backend_data, &cb_backend_type, backend);
@@ -519,26 +531,52 @@ cb_Backend_open(VALUE self, VALUE connection_string, VALUE username, VALUE passw
519
531
  rb_raise(rb_eArgError, "Cluster has been closed already");
520
532
  }
521
533
  Check_Type(connection_string, T_STRING);
522
- Check_Type(username, T_STRING);
523
- Check_Type(password, T_STRING);
534
+ Check_Type(credentials, T_HASH);
535
+
536
+ VALUE username = Qnil;
537
+ VALUE password = Qnil;
538
+
539
+ VALUE certificate_path = rb_hash_aref(credentials, rb_id2sym(rb_intern("certificate_path")));
540
+ VALUE key_path = rb_hash_aref(credentials, rb_id2sym(rb_intern("key_path")));
541
+ if (NIL_P(certificate_path) || NIL_P(key_path)) {
542
+ username = rb_hash_aref(credentials, rb_id2sym(rb_intern("username")));
543
+ password = rb_hash_aref(credentials, rb_id2sym(rb_intern("password")));
544
+ Check_Type(username, T_STRING);
545
+ Check_Type(password, T_STRING);
546
+ } else {
547
+ Check_Type(certificate_path, T_STRING);
548
+ Check_Type(key_path, T_STRING);
549
+ }
524
550
  if (!NIL_P(options)) {
525
551
  Check_Type(options, T_HASH);
526
552
  }
527
553
 
528
554
  VALUE exc = Qnil;
529
- {
555
+ do {
530
556
  std::string input(RSTRING_PTR(connection_string), static_cast<size_t>(RSTRING_LEN(connection_string)));
531
557
  auto connstr = couchbase::utils::parse_connection_string(input);
532
- std::string user(RSTRING_PTR(username), static_cast<size_t>(RSTRING_LEN(username)));
533
- std::string pass(RSTRING_PTR(password), static_cast<size_t>(RSTRING_LEN(password)));
534
- couchbase::origin origin(user, pass, std::move(connstr));
558
+ couchbase::cluster_credentials auth{};
559
+ if (NIL_P(certificate_path) || NIL_P(key_path)) {
560
+ auth.username.assign(RSTRING_PTR(username), static_cast<size_t>(RSTRING_LEN(username)));
561
+ auth.password.assign(RSTRING_PTR(password), static_cast<size_t>(RSTRING_LEN(password)));
562
+ } else {
563
+ if (!connstr.tls) {
564
+ exc = rb_exc_new_cstr(
565
+ eInvalidArgument,
566
+ fmt::format("Certificate authenticator requires TLS connection, check the schema of the connection string").c_str());
567
+ break;
568
+ }
569
+ auth.certificate_path.assign(RSTRING_PTR(certificate_path), static_cast<size_t>(RSTRING_LEN(certificate_path)));
570
+ auth.key_path.assign(RSTRING_PTR(key_path), static_cast<size_t>(RSTRING_LEN(key_path)));
571
+ }
572
+ couchbase::origin origin(auth, std::move(connstr));
535
573
  auto barrier = std::make_shared<std::promise<std::error_code>>();
536
574
  auto f = barrier->get_future();
537
575
  backend->cluster->open(origin, [barrier](std::error_code ec) mutable { barrier->set_value(ec); });
538
576
  if (auto ec = f.get()) {
539
577
  exc = cb__map_error_code(ec, fmt::format("unable open cluster at {}", origin.next_address().first));
540
578
  }
541
- }
579
+ } while (false);
542
580
  if (!NIL_P(exc)) {
543
581
  rb_exc_raise(exc);
544
582
  }
@@ -1958,6 +1996,22 @@ cb_Backend_document_query(VALUE self, VALUE statement, VALUE options)
1958
1996
  if (!NIL_P(pipeline_batch)) {
1959
1997
  req.pipeline_batch = NUM2ULONG(pipeline_batch);
1960
1998
  }
1999
+ VALUE scope_qualifier = rb_hash_aref(options, rb_id2sym(rb_intern("scope_qualifier")));
2000
+ if (!NIL_P(scope_qualifier) && TYPE(scope_qualifier) == T_STRING) {
2001
+ req.scope_qualifier.emplace(std::string(RSTRING_PTR(scope_qualifier), static_cast<std::size_t>(RSTRING_LEN(scope_qualifier))));
2002
+ } else {
2003
+ VALUE scope_name = rb_hash_aref(options, rb_id2sym(rb_intern("scope_name")));
2004
+ if (!NIL_P(scope_name) && TYPE(scope_name) == T_STRING) {
2005
+ req.scope_name.emplace(std::string(RSTRING_PTR(scope_name), static_cast<std::size_t>(RSTRING_LEN(scope_name))));
2006
+ VALUE bucket_name = rb_hash_aref(options, rb_id2sym(rb_intern("bucket_name")));
2007
+ if (NIL_P(bucket_name)) {
2008
+ exc = rb_exc_new_cstr(
2009
+ eInvalidArgument, fmt::format("bucket must be specified for query in scope \"{}\"", req.scope_name.value()).c_str());
2010
+ break;
2011
+ }
2012
+ req.bucket_name.emplace(std::string(RSTRING_PTR(bucket_name), static_cast<std::size_t>(RSTRING_LEN(bucket_name))));
2013
+ }
2014
+ }
1961
2015
  VALUE profile = rb_hash_aref(options, rb_id2sym(rb_intern("profile")));
1962
2016
  if (!NIL_P(profile)) {
1963
2017
  Check_Type(profile, T_SYMBOL);
@@ -2776,6 +2830,14 @@ cb_Backend_query_index_get_all(VALUE self, VALUE bucket_name, VALUE timeout)
2776
2830
  rb_ary_push(index_key, rb_str_new(key.data(), static_cast<long>(key.size())));
2777
2831
  }
2778
2832
  rb_hash_aset(index, rb_id2sym(rb_intern("index_key")), index_key);
2833
+ if (idx.scope_id) {
2834
+ rb_hash_aset(
2835
+ index, rb_id2sym(rb_intern("scope_id")), rb_str_new(idx.scope_id->data(), static_cast<long>(idx.scope_id->size())));
2836
+ }
2837
+ if (idx.bucket_id) {
2838
+ rb_hash_aset(
2839
+ index, rb_id2sym(rb_intern("bucket_id")), rb_str_new(idx.bucket_id->data(), static_cast<long>(idx.bucket_id->size())));
2840
+ }
2779
2841
  if (idx.condition) {
2780
2842
  rb_hash_aset(
2781
2843
  index, rb_id2sym(rb_intern("condition")), rb_str_new(idx.condition->data(), static_cast<long>(idx.condition->size())));
@@ -2840,6 +2902,14 @@ cb_Backend_query_index_create(VALUE self, VALUE bucket_name, VALUE index_name, V
2840
2902
  if (!NIL_P(condition)) {
2841
2903
  req.condition.emplace(std::string(RSTRING_PTR(condition), static_cast<std::size_t>(RSTRING_LEN(condition))));
2842
2904
  } /* else use backend default */
2905
+ VALUE scope_name = rb_hash_aref(options, rb_id2sym(rb_intern("scope_name")));
2906
+ if (TYPE(scope_name) == T_STRING) {
2907
+ req.scope_name.assign(RSTRING_PTR(scope_name), static_cast<size_t>(RSTRING_LEN(scope_name)));
2908
+ }
2909
+ VALUE collection_name = rb_hash_aref(options, rb_id2sym(rb_intern("collection_name")));
2910
+ if (TYPE(scope_name) == T_STRING) {
2911
+ req.collection_name.assign(RSTRING_PTR(collection_name), static_cast<size_t>(RSTRING_LEN(collection_name)));
2912
+ }
2843
2913
  }
2844
2914
 
2845
2915
  auto barrier = std::make_shared<std::promise<couchbase::operations::query_index_create_response>>();
@@ -2907,6 +2977,14 @@ cb_Backend_query_index_drop(VALUE self, VALUE bucket_name, VALUE index_name, VAL
2907
2977
  } else if (ignore_if_does_not_exist == Qfalse) {
2908
2978
  req.ignore_if_does_not_exist = false;
2909
2979
  } /* else use backend default */
2980
+ VALUE scope_name = rb_hash_aref(options, rb_id2sym(rb_intern("scope_name")));
2981
+ if (TYPE(scope_name) == T_STRING) {
2982
+ req.scope_name.assign(RSTRING_PTR(scope_name), static_cast<size_t>(RSTRING_LEN(scope_name)));
2983
+ }
2984
+ VALUE collection_name = rb_hash_aref(options, rb_id2sym(rb_intern("collection_name")));
2985
+ if (TYPE(scope_name) == T_STRING) {
2986
+ req.collection_name.assign(RSTRING_PTR(collection_name), static_cast<size_t>(RSTRING_LEN(collection_name)));
2987
+ }
2910
2988
  }
2911
2989
 
2912
2990
  auto barrier = std::make_shared<std::promise<couchbase::operations::query_index_drop_response>>();
@@ -2990,6 +3068,14 @@ cb_Backend_query_index_create_primary(VALUE self, VALUE bucket_name, VALUE optio
2990
3068
  if (!NIL_P(index_name)) {
2991
3069
  req.index_name.assign(RSTRING_PTR(index_name), static_cast<size_t>(RSTRING_LEN(index_name)));
2992
3070
  } /* else use backend default */
3071
+ VALUE scope_name = rb_hash_aref(options, rb_id2sym(rb_intern("scope_name")));
3072
+ if (TYPE(scope_name) == T_STRING) {
3073
+ req.scope_name.assign(RSTRING_PTR(scope_name), static_cast<size_t>(RSTRING_LEN(scope_name)));
3074
+ }
3075
+ VALUE collection_name = rb_hash_aref(options, rb_id2sym(rb_intern("collection_name")));
3076
+ if (TYPE(scope_name) == T_STRING) {
3077
+ req.collection_name.assign(RSTRING_PTR(collection_name), static_cast<size_t>(RSTRING_LEN(collection_name)));
3078
+ }
2993
3079
  }
2994
3080
 
2995
3081
  auto barrier = std::make_shared<std::promise<couchbase::operations::query_index_create_response>>();
@@ -3061,6 +3147,14 @@ cb_Backend_query_index_drop_primary(VALUE self, VALUE bucket_name, VALUE options
3061
3147
  req.is_primary = false;
3062
3148
  req.bucket_name.assign(RSTRING_PTR(index_name), static_cast<size_t>(RSTRING_LEN(index_name)));
3063
3149
  }
3150
+ VALUE scope_name = rb_hash_aref(options, rb_id2sym(rb_intern("scope_name")));
3151
+ if (TYPE(scope_name) == T_STRING) {
3152
+ req.scope_name.assign(RSTRING_PTR(scope_name), static_cast<size_t>(RSTRING_LEN(scope_name)));
3153
+ }
3154
+ VALUE collection_name = rb_hash_aref(options, rb_id2sym(rb_intern("collection_name")));
3155
+ if (TYPE(scope_name) == T_STRING) {
3156
+ req.collection_name.assign(RSTRING_PTR(collection_name), static_cast<size_t>(RSTRING_LEN(collection_name)));
3157
+ }
3064
3158
  }
3065
3159
 
3066
3160
  auto barrier = std::make_shared<std::promise<couchbase::operations::query_index_drop_response>>();
@@ -5347,7 +5441,7 @@ init_backend(VALUE mCouchbase)
5347
5441
  {
5348
5442
  VALUE cBackend = rb_define_class_under(mCouchbase, "Backend", rb_cBasicObject);
5349
5443
  rb_define_alloc_func(cBackend, cb_Backend_allocate);
5350
- rb_define_method(cBackend, "open", VALUE_FUNC(cb_Backend_open), 4);
5444
+ rb_define_method(cBackend, "open", VALUE_FUNC(cb_Backend_open), 3);
5351
5445
  rb_define_method(cBackend, "close", VALUE_FUNC(cb_Backend_close), 0);
5352
5446
  rb_define_method(cBackend, "open_bucket", VALUE_FUNC(cb_Backend_open_bucket), 2);
5353
5447
 
@@ -5435,11 +5529,13 @@ init_logger()
5435
5529
 
5436
5530
  auto env_val = spdlog::details::os::getenv("COUCHBASE_BACKEND_LOG_LEVEL");
5437
5531
  if (env_val.empty()) {
5438
- spdlog::set_level(spdlog::level::critical);
5532
+ spdlog::set_level(spdlog::level::warn);
5439
5533
  } else {
5440
5534
  auto levels = spdlog::cfg::helpers::extract_levels(env_val);
5441
5535
  spdlog::details::registry::instance().update_levels(std::move(levels));
5442
5536
  }
5537
+
5538
+ couchbase::platform::install_backtrace_terminate_handler();
5443
5539
  }
5444
5540
 
5445
5541
  extern "C" {
@@ -20,12 +20,112 @@
20
20
  namespace couchbase
21
21
  {
22
22
  struct error_map {
23
+ enum class attribute {
24
+ /**
25
+ * The operation was successful for those situations where the error code is indicating successful (i.e. subdoc operations carried
26
+ * out on a deleted document)
27
+ */
28
+ success,
29
+
30
+ /**
31
+ * This attribute means that the error is related to a constraint failure regarding the item itself, i.e. the item does not exist,
32
+ * already exists, or its current value makes the current operation impossible. Retrying the operation when the item's value or
33
+ * status has changed may succeed.
34
+ */
35
+ item_only,
36
+
37
+ /**
38
+ * This attribute means that a user's input was invalid because it violates the semantics of the operation, or exceeds some
39
+ * predefined limit.
40
+ */
41
+ invalid_input,
42
+
43
+ /**
44
+ * The client's cluster map may be outdated and requires updating. The client should obtain a newer configuration.
45
+ */
46
+ fetch_config,
47
+
48
+ /**
49
+ * The current connection is no longer valid. The client must reconnect to the server. Note that the presence of other attributes
50
+ * may indicate an alternate remedy to fixing the connection without a disconnect, but without special remedial action a disconnect
51
+ * is needed.
52
+ */
53
+ conn_state_invalidated,
54
+
55
+ /**
56
+ * The operation failed because the client failed to authenticate or is not authorized to perform this operation. Note that this
57
+ * error in itself does not mean the connection is invalid, unless conn-state-invalidated is also present.
58
+ */
59
+ auth,
60
+
61
+ /**
62
+ * This error code must be handled specially. If it is not handled, the connection must be dropped.
63
+ */
64
+ special_handling,
65
+
66
+ /**
67
+ * The operation is not supported, possibly because the of server version, bucket type, or current user.
68
+ */
69
+ support,
70
+
71
+ /**
72
+ * This error is transient. Note that this does not mean the error is retriable.
73
+ */
74
+ temp,
75
+
76
+ /**
77
+ * This is an internal error in the server.
78
+ */
79
+ internal,
80
+
81
+ /**
82
+ * The operation may be retried immediately.
83
+ */
84
+ retry_now,
85
+
86
+ /**
87
+ * The operation may be retried after some time.
88
+ */
89
+ retry_later,
90
+
91
+ /**
92
+ * The error is related to the subdocument subsystem.
93
+ */
94
+ subdoc,
95
+
96
+ /**
97
+ * The error is related to the DCP subsystem.
98
+ */
99
+ dcp,
100
+
101
+ /**
102
+ * Use retry specifications from the server.
103
+ */
104
+ auto_retry,
105
+
106
+ /**
107
+ * This attribute specifies that the requested item is currently locked.
108
+ */
109
+ item_locked,
110
+
111
+ /**
112
+ * This attribute means that the error is related to operating on a soft-deleted document.
113
+ */
114
+ item_deleted,
115
+ };
116
+
23
117
  struct error_info {
24
118
  std::uint16_t code;
25
119
  std::string name;
26
120
  std::string description;
27
- std::set<std::string> attributes;
121
+ std::set<attribute> attributes;
122
+
123
+ bool has_retry_attribute()
124
+ {
125
+ return attributes.find(attribute::retry_now) != attributes.end() || attributes.find(attribute::retry_later) != attributes.end();
126
+ }
28
127
  };
128
+
29
129
  uuid::uuid_t id;
30
130
  uint16_t version;
31
131
  uint16_t revision;
@@ -51,7 +151,44 @@ struct traits<couchbase::error_map> {
51
151
  ei.name = info.at("name").get_string();
52
152
  ei.description = info.at("desc").get_string();
53
153
  for (const auto& a : info.at("attrs").get_array()) {
54
- ei.attributes.insert(a.get_string());
154
+ const std::string& attr_val = a.get_string();
155
+ if (attr_val == "success") {
156
+ ei.attributes.insert(couchbase::error_map::attribute::success);
157
+ } else if (attr_val == "item-only") {
158
+ ei.attributes.insert(couchbase::error_map::attribute::item_only);
159
+ } else if (attr_val == "invalid-input") {
160
+ ei.attributes.insert(couchbase::error_map::attribute::invalid_input);
161
+ } else if (attr_val == "fetch-config") {
162
+ ei.attributes.insert(couchbase::error_map::attribute::fetch_config);
163
+ } else if (attr_val == "conn-state-invalidated") {
164
+ ei.attributes.insert(couchbase::error_map::attribute::conn_state_invalidated);
165
+ } else if (attr_val == "auth") {
166
+ ei.attributes.insert(couchbase::error_map::attribute::auth);
167
+ } else if (attr_val == "special-handling") {
168
+ ei.attributes.insert(couchbase::error_map::attribute::special_handling);
169
+ } else if (attr_val == "support") {
170
+ ei.attributes.insert(couchbase::error_map::attribute::support);
171
+ } else if (attr_val == "temp") {
172
+ ei.attributes.insert(couchbase::error_map::attribute::temp);
173
+ } else if (attr_val == "internal") {
174
+ ei.attributes.insert(couchbase::error_map::attribute::internal);
175
+ } else if (attr_val == "retry-now") {
176
+ ei.attributes.insert(couchbase::error_map::attribute::retry_now);
177
+ } else if (attr_val == "retry-later") {
178
+ ei.attributes.insert(couchbase::error_map::attribute::retry_later);
179
+ } else if (attr_val == "subdoc") {
180
+ ei.attributes.insert(couchbase::error_map::attribute::subdoc);
181
+ } else if (attr_val == "dcp") {
182
+ ei.attributes.insert(couchbase::error_map::attribute::dcp);
183
+ } else if (attr_val == "auto-retry") {
184
+ ei.attributes.insert(couchbase::error_map::attribute::auto_retry);
185
+ } else if (attr_val == "item-locked") {
186
+ ei.attributes.insert(couchbase::error_map::attribute::item_locked);
187
+ } else if (attr_val == "item-deleted") {
188
+ ei.attributes.insert(couchbase::error_map::attribute::item_deleted);
189
+ } else {
190
+ spdlog::warn(R"(skipping unknown attribute "{}" in error map for code={} and name="{}")", attr_val, ei.code, ei.name);
191
+ }
55
192
  }
56
193
  result.errors.emplace(ei.code, ei);
57
194
  }
@@ -59,3 +196,66 @@ struct traits<couchbase::error_map> {
59
196
  }
60
197
  };
61
198
  } // namespace tao::json
199
+
200
+ template<>
201
+ struct fmt::formatter<couchbase::error_map::attribute> : formatter<string_view> {
202
+ template<typename FormatContext>
203
+ auto format(couchbase::error_map::attribute attr, FormatContext& ctx)
204
+ {
205
+ string_view name = "unknown";
206
+ switch (attr) {
207
+ case couchbase::error_map::attribute::success:
208
+ name = "success";
209
+ break;
210
+ case couchbase::error_map::attribute::item_only:
211
+ name = "item-only";
212
+ break;
213
+ case couchbase::error_map::attribute::invalid_input:
214
+ name = "invalid-input";
215
+ break;
216
+ case couchbase::error_map::attribute::fetch_config:
217
+ name = "fetch-config";
218
+ break;
219
+ case couchbase::error_map::attribute::conn_state_invalidated:
220
+ name = "conn-state-invalidated";
221
+ break;
222
+ case couchbase::error_map::attribute::auth:
223
+ name = "auth";
224
+ break;
225
+ case couchbase::error_map::attribute::special_handling:
226
+ name = "special-handling";
227
+ break;
228
+ case couchbase::error_map::attribute::support:
229
+ name = "support";
230
+ break;
231
+ case couchbase::error_map::attribute::temp:
232
+ name = "temp";
233
+ break;
234
+ case couchbase::error_map::attribute::internal:
235
+ name = "internal";
236
+ break;
237
+ case couchbase::error_map::attribute::retry_now:
238
+ name = "retry-now";
239
+ break;
240
+ case couchbase::error_map::attribute::retry_later:
241
+ name = "retry-later";
242
+ break;
243
+ case couchbase::error_map::attribute::subdoc:
244
+ name = "subdoc";
245
+ break;
246
+ case couchbase::error_map::attribute::dcp:
247
+ name = "dcp";
248
+ break;
249
+ case couchbase::error_map::attribute::auto_retry:
250
+ name = "auto-retry";
251
+ break;
252
+ case couchbase::error_map::attribute::item_locked:
253
+ name = "item-locked";
254
+ break;
255
+ case couchbase::error_map::attribute::item_deleted:
256
+ name = "item-deleted";
257
+ break;
258
+ }
259
+ return formatter<string_view>::format(name, ctx);
260
+ }
261
+ };