couchbase 3.0.0.beta.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +227 -0
- data/.rubocop_todo.yml +47 -0
- data/CONTRIBUTING.md +110 -0
- data/Gemfile +4 -0
- data/README.md +3 -3
- data/Rakefile +1 -1
- data/couchbase.gemspec +40 -39
- data/examples/analytics.rb +123 -108
- data/examples/auth.rb +33 -0
- data/examples/crud.rb +16 -2
- data/examples/managing_analytics_indexes.rb +18 -4
- data/examples/managing_buckets.rb +17 -3
- data/examples/managing_collections.rb +22 -9
- data/examples/managing_query_indexes.rb +38 -18
- data/examples/managing_search_indexes.rb +21 -6
- data/examples/managing_view_indexes.rb +18 -4
- data/examples/query.rb +17 -3
- data/examples/query_with_consistency.rb +30 -20
- data/examples/search.rb +116 -101
- data/examples/search_with_consistency.rb +43 -30
- data/examples/subdocument.rb +42 -30
- data/examples/view.rb +19 -10
- data/ext/CMakeLists.txt +40 -2
- data/ext/build_version.hxx.in +1 -1
- data/ext/couchbase/bucket.hxx +190 -38
- data/ext/couchbase/cluster.hxx +22 -4
- data/ext/couchbase/configuration.hxx +14 -14
- data/ext/couchbase/couchbase.cxx +108 -12
- data/ext/couchbase/error_map.hxx +202 -2
- data/ext/couchbase/errors.hxx +8 -2
- data/ext/couchbase/io/dns_client.hxx +6 -6
- data/ext/couchbase/io/http_command.hxx +2 -2
- data/ext/couchbase/io/http_session.hxx +7 -11
- data/ext/couchbase/io/http_session_manager.hxx +3 -3
- data/ext/couchbase/io/mcbp_command.hxx +101 -44
- data/ext/couchbase/io/mcbp_session.hxx +144 -49
- data/ext/couchbase/io/retry_action.hxx +30 -0
- data/ext/couchbase/io/retry_context.hxx +39 -0
- data/ext/couchbase/io/retry_orchestrator.hxx +96 -0
- data/ext/couchbase/io/retry_reason.hxx +235 -0
- data/ext/couchbase/io/retry_strategy.hxx +156 -0
- data/ext/couchbase/operations/document_decrement.hxx +2 -0
- data/ext/couchbase/operations/document_exists.hxx +2 -0
- data/ext/couchbase/operations/document_get.hxx +2 -0
- data/ext/couchbase/operations/document_get_and_lock.hxx +2 -0
- data/ext/couchbase/operations/document_get_and_touch.hxx +2 -0
- data/ext/couchbase/operations/document_get_projected.hxx +2 -0
- data/ext/couchbase/operations/document_increment.hxx +2 -0
- data/ext/couchbase/operations/document_insert.hxx +2 -0
- data/ext/couchbase/operations/document_lookup_in.hxx +2 -0
- data/ext/couchbase/operations/document_mutate_in.hxx +3 -0
- data/ext/couchbase/operations/document_query.hxx +10 -0
- data/ext/couchbase/operations/document_remove.hxx +2 -0
- data/ext/couchbase/operations/document_replace.hxx +2 -0
- data/ext/couchbase/operations/document_search.hxx +8 -3
- data/ext/couchbase/operations/document_touch.hxx +2 -0
- data/ext/couchbase/operations/document_unlock.hxx +2 -0
- data/ext/couchbase/operations/document_upsert.hxx +2 -0
- data/ext/couchbase/operations/query_index_create.hxx +14 -4
- data/ext/couchbase/operations/query_index_drop.hxx +12 -2
- data/ext/couchbase/operations/query_index_get_all.hxx +11 -2
- data/ext/couchbase/origin.hxx +47 -17
- data/ext/couchbase/platform/backtrace.c +189 -0
- data/ext/couchbase/platform/backtrace.h +54 -0
- data/ext/couchbase/platform/terminate_handler.cc +122 -0
- data/ext/couchbase/platform/terminate_handler.h +36 -0
- data/ext/couchbase/protocol/cmd_get_cluster_config.hxx +6 -1
- data/ext/couchbase/protocol/status.hxx +14 -4
- data/ext/couchbase/version.hxx +2 -2
- data/ext/extconf.rb +39 -36
- data/ext/test/main.cxx +64 -16
- data/lib/couchbase.rb +0 -1
- data/lib/couchbase/analytics_options.rb +2 -4
- data/lib/couchbase/authenticator.rb +14 -0
- data/lib/couchbase/binary_collection.rb +9 -9
- data/lib/couchbase/binary_collection_options.rb +8 -6
- data/lib/couchbase/bucket.rb +18 -18
- data/lib/couchbase/cluster.rb +121 -90
- data/lib/couchbase/collection.rb +36 -38
- data/lib/couchbase/collection_options.rb +31 -17
- data/lib/couchbase/common_options.rb +1 -1
- data/lib/couchbase/datastructures/couchbase_list.rb +16 -16
- data/lib/couchbase/datastructures/couchbase_map.rb +18 -18
- data/lib/couchbase/datastructures/couchbase_queue.rb +13 -13
- data/lib/couchbase/datastructures/couchbase_set.rb +8 -7
- data/lib/couchbase/errors.rb +10 -3
- data/lib/couchbase/json_transcoder.rb +2 -2
- data/lib/couchbase/management/analytics_index_manager.rb +37 -37
- data/lib/couchbase/management/bucket_manager.rb +25 -25
- data/lib/couchbase/management/collection_manager.rb +3 -3
- data/lib/couchbase/management/query_index_manager.rb +59 -14
- data/lib/couchbase/management/search_index_manager.rb +15 -12
- data/lib/couchbase/management/user_manager.rb +1 -1
- data/lib/couchbase/management/view_index_manager.rb +11 -5
- data/lib/couchbase/mutation_state.rb +12 -0
- data/lib/couchbase/query_options.rb +23 -9
- data/lib/couchbase/scope.rb +61 -1
- data/lib/couchbase/search_options.rb +40 -27
- data/lib/couchbase/subdoc.rb +31 -28
- data/lib/couchbase/version.rb +2 -2
- data/lib/couchbase/view_options.rb +0 -1
- metadata +22 -9
data/ext/couchbase/cluster.hxx
CHANGED
@@ -53,7 +53,8 @@ class cluster
|
|
53
53
|
{
|
54
54
|
origin_ = origin;
|
55
55
|
if (origin_.options().enable_dns_srv) {
|
56
|
-
return
|
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_.
|
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(
|
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,
|
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,
|
221
|
+
return std::make_pair(vbucket, vbmap->at(vbucket)[0]);
|
222
222
|
}
|
223
223
|
};
|
224
224
|
|
data/ext/couchbase/couchbase.cxx
CHANGED
@@ -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
|
-
|
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",
|
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
|
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(
|
523
|
-
|
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
|
-
|
533
|
-
|
534
|
-
|
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),
|
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::
|
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" {
|
data/ext/couchbase/error_map.hxx
CHANGED
@@ -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<
|
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
|
-
|
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
|
+
};
|