couchbase 3.1.0 → 3.1.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6610fe0ac9f660835b0bfaf36f53673e35213f29440ceda426bfb6f488195b95
4
- data.tar.gz: 734c1253fae466f3698046cf37d532f4ec754d2ed35d7d5c37c48539a59ce9ee
3
+ metadata.gz: 24cc8f415bbff9e3e132ee47c16e04a2090c8a26cc9afbd21899263f735a10cd
4
+ data.tar.gz: 3e26641abb3b31354b496effc0002794c82af25a5626ccd784591f311c03c2c8
5
5
  SHA512:
6
- metadata.gz: d8ef7b9f1c6f417b4e803f12b64682980dcc9f69c8ebfc462b72dbeb60e306ee6a33176ca320cd85bd9974ff5b457d51e26ebf7dfbe2251bb6bbfd1a92b12077
7
- data.tar.gz: 8ce0c1aab3a6f48295bf5a312a4bda8bc6668b3de079b60c4549bb8c1e27662eac49284c218928d5ffb60b7af799a01cdab736ae8d484eebc17e1b23b360aa8f
6
+ metadata.gz: 7f57fb43563e563c4d0ab75330108a199ec63c36794ceb3afe0b5702cf117a2350b3e9b02027ea1129de8a3e4cc464248284a8973d6c4d442ce8f83fc69d84b0
7
+ data.tar.gz: b020a7a1049d9d63d64684e83c82c7b8c8aae90a80188e79eb09850e6c7bad242ce4642be0051ed6355b3789c5bb7a0253523218b5917bb4f78e208b3edcd30e
data/README.md CHANGED
@@ -24,7 +24,7 @@ The library tested with the MRI 2.5, 2.6, 2.7 and 3.0. Supported platforms are L
24
24
  Add this line to your application's Gemfile:
25
25
 
26
26
  ```ruby
27
- gem "couchbase", "3.1.0"
27
+ gem "couchbase", "3.1.1"
28
28
  ```
29
29
 
30
30
  And then execute:
@@ -26,5 +26,5 @@ constexpr auto BACKEND_CXX_COMPILER = "@CMAKE_CXX_COMPILER_ID@ @CMAKE_CXX_COMPIL
26
26
  constexpr auto BACKEND_C_COMPILER = "@CMAKE_C_COMPILER_ID@ @CMAKE_C_COMPILER_VERSION@";
27
27
  constexpr auto BACKEND_SYSTEM = "@CMAKE_SYSTEM@";
28
28
  constexpr auto BACKEND_SYSTEM_PROCESSOR = "@CMAKE_SYSTEM_PROCESSOR@";
29
- constexpr auto BACKEND_GIT_REVISION = "7ba4f7d8b5b0b59b9971ad765876413be3064adb";
29
+ constexpr auto BACKEND_GIT_REVISION = "a79d098eb238b3883e49e1738c35167642e8834f";
30
30
  } // namespace couchbase
@@ -42,6 +42,7 @@ struct cluster_options {
42
42
  bool show_queries{ false };
43
43
  bool enable_unordered_execution{ true };
44
44
  bool enable_clustermap_notification{ true };
45
+ bool enable_compression{ true };
45
46
  std::string network{ "auto" };
46
47
 
47
48
  std::chrono::milliseconds tcp_keep_alive_interval = timeout_defaults::tcp_keep_alive_interval;
@@ -2037,6 +2037,10 @@ cb_Backend_document_upsert(VALUE self, VALUE bucket, VALUE collection, VALUE id,
2037
2037
  if (!NIL_P(expiry)) {
2038
2038
  req.expiry = FIX2UINT(expiry);
2039
2039
  }
2040
+ exc = cb_extract_option_bool(req.preserve_expiry, options, "preserve_expiry");
2041
+ if (!NIL_P(exc)) {
2042
+ break;
2043
+ }
2040
2044
 
2041
2045
  auto barrier = std::make_shared<std::promise<couchbase::operations::upsert_response>>();
2042
2046
  auto f = barrier->get_future();
@@ -2083,6 +2087,11 @@ cb_Backend_document_upsert_multi(VALUE self, VALUE id_content, VALUE options)
2083
2087
  if (!NIL_P(exc)) {
2084
2088
  break;
2085
2089
  }
2090
+ bool preserve_expiry{ false };
2091
+ exc = cb_extract_option_bool(preserve_expiry, options, "preserve_expiry");
2092
+ if (!NIL_P(exc)) {
2093
+ break;
2094
+ }
2086
2095
 
2087
2096
  std::vector<std::tuple<couchbase::document_id, std::string, std::uint32_t>> tuples{};
2088
2097
  exc = cb_extract_array_of_id_content(tuples, id_content);
@@ -2105,6 +2114,7 @@ cb_Backend_document_upsert_multi(VALUE self, VALUE id_content, VALUE options)
2105
2114
  if (!NIL_P(expiry)) {
2106
2115
  req.expiry = FIX2UINT(expiry);
2107
2116
  }
2117
+ req.preserve_expiry = preserve_expiry;
2108
2118
  auto barrier = std::make_shared<std::promise<couchbase::operations::upsert_response>>();
2109
2119
  backend->cluster->execute(req, [barrier](couchbase::operations::upsert_response&& resp) mutable { barrier->set_value(resp); });
2110
2120
  barriers.emplace_back(barrier);
@@ -2276,6 +2286,10 @@ cb_Backend_document_replace(VALUE self, VALUE bucket, VALUE collection, VALUE id
2276
2286
  if (!NIL_P(expiry)) {
2277
2287
  req.expiry = FIX2UINT(expiry);
2278
2288
  }
2289
+ exc = cb_extract_option_bool(req.preserve_expiry, options, "preserve_expiry");
2290
+ if (!NIL_P(exc)) {
2291
+ break;
2292
+ }
2279
2293
  VALUE cas = Qnil;
2280
2294
  exc = cb_extract_option_bignum(cas, options, "cas");
2281
2295
  if (!NIL_P(exc)) {
@@ -2547,6 +2561,10 @@ cb_Backend_document_increment(VALUE self, VALUE bucket, VALUE collection, VALUE
2547
2561
  if (!NIL_P(expiry)) {
2548
2562
  req.expiry = FIX2UINT(expiry);
2549
2563
  }
2564
+ exc = cb_extract_option_bool(req.preserve_expiry, options, "preserve_expiry");
2565
+ if (!NIL_P(exc)) {
2566
+ break;
2567
+ }
2550
2568
 
2551
2569
  auto barrier = std::make_shared<std::promise<couchbase::operations::increment_response>>();
2552
2570
  auto f = barrier->get_future();
@@ -2622,6 +2640,10 @@ cb_Backend_document_decrement(VALUE self, VALUE bucket, VALUE collection, VALUE
2622
2640
  if (!NIL_P(expiry)) {
2623
2641
  req.expiry = FIX2UINT(expiry);
2624
2642
  }
2643
+ exc = cb_extract_option_bool(req.preserve_expiry, options, "preserve_expiry");
2644
+ if (!NIL_P(exc)) {
2645
+ break;
2646
+ }
2625
2647
 
2626
2648
  auto barrier = std::make_shared<std::promise<couchbase::operations::decrement_response>>();
2627
2649
  auto f = barrier->get_future();
@@ -2685,6 +2707,9 @@ cb_map_subdoc_opcode(couchbase::protocol::subdoc_opcode opcode)
2685
2707
  case couchbase::protocol::subdoc_opcode::set_doc:
2686
2708
  return rb_id2sym(rb_intern("set_doc"));
2687
2709
 
2710
+ case couchbase::protocol::subdoc_opcode::remove_doc:
2711
+ return rb_id2sym(rb_intern("remove_doc"));
2712
+
2688
2713
  case couchbase::protocol::subdoc_opcode::replace_body_with_xattr:
2689
2714
  return rb_id2sym(rb_intern("replace_body_with_xattr"));
2690
2715
  }
@@ -2973,6 +2998,10 @@ cb_Backend_document_mutate_in(VALUE self, VALUE bucket, VALUE collection, VALUE
2973
2998
  if (!NIL_P(expiry)) {
2974
2999
  req.expiry = FIX2UINT(expiry);
2975
3000
  }
3001
+ exc = cb_extract_option_bool(req.preserve_expiry, options, "preserve_expiry");
3002
+ if (!NIL_P(exc)) {
3003
+ break;
3004
+ }
2976
3005
  exc = cb_extract_option_bool(req.access_deleted, options, "access_deleted");
2977
3006
  if (!NIL_P(exc)) {
2978
3007
  break;
@@ -3023,6 +3052,8 @@ cb_Backend_document_mutate_in(VALUE self, VALUE bucket, VALUE collection, VALUE
3023
3052
  opcode = couchbase::protocol::subdoc_opcode::counter;
3024
3053
  } else if (operation_id == rb_intern("set_doc")) {
3025
3054
  opcode = couchbase::protocol::subdoc_opcode::set_doc;
3055
+ } else if (operation_id == rb_intern("remove_doc")) {
3056
+ opcode = couchbase::protocol::subdoc_opcode::remove_doc;
3026
3057
  } else {
3027
3058
  exc =
3028
3059
  rb_exc_new_str(eInvalidArgument, rb_sprintf("unsupported operation for subdocument mutation: %+" PRIsVALUE, operation));
@@ -139,6 +139,9 @@ class mcbp_session : public std::enable_shared_from_this<mcbp_session>
139
139
  if (session_->origin_.options().enable_clustermap_notification) {
140
140
  hello_req.body().enable_clustermap_change_notification();
141
141
  }
142
+ if (session_->origin_.options().enable_compression) {
143
+ hello_req.body().enable_compression();
144
+ }
142
145
  hello_req.opaque(session_->next_opaque());
143
146
  hello_req.body().user_agent(tao::json::to_string(user_agent));
144
147
  spdlog::debug("{} user_agent={}, requested_features=[{}]",
@@ -60,27 +60,31 @@ struct collection_create_request {
60
60
  };
61
61
 
62
62
  collection_create_response
63
- make_response(error_context::http&& ctx, collection_create_request&, collection_create_request::encoded_response_type&& encoded)
63
+ make_response(error_context::http&& ctx,
64
+ collection_create_request& /* request */,
65
+ collection_create_request::encoded_response_type&& encoded)
64
66
  {
65
67
  collection_create_response response{ ctx };
66
68
  if (!response.ctx.ec) {
67
69
  switch (encoded.status_code) {
68
- case 400:
69
- if (encoded.body.find("Collection with this name already exists") != std::string::npos) {
70
+ case 400: {
71
+ std::regex collection_exists("Collection with name .+ already exists");
72
+ if (std::regex_search(encoded.body, collection_exists)) {
70
73
  response.ctx.ec = std::make_error_code(error::management_errc::collection_exists);
71
74
  } else if (encoded.body.find("Not allowed on this version of cluster") != std::string::npos) {
72
75
  response.ctx.ec = std::make_error_code(error::common_errc::feature_not_available);
73
76
  } else {
74
77
  response.ctx.ec = std::make_error_code(error::common_errc::invalid_argument);
75
78
  }
76
- break;
77
- case 404:
78
- if (encoded.body.find("Scope with this name is not found") != std::string::npos) {
79
+ } break;
80
+ case 404: {
81
+ std::regex scope_not_found("Scope with name .+ is not found");
82
+ if (std::regex_search(encoded.body, scope_not_found)) {
79
83
  response.ctx.ec = std::make_error_code(error::common_errc::scope_not_found);
80
84
  } else {
81
85
  response.ctx.ec = std::make_error_code(error::common_errc::bucket_not_found);
82
86
  }
83
- break;
87
+ } break;
84
88
  case 200: {
85
89
  tao::json::value payload{};
86
90
  try {
@@ -17,6 +17,8 @@
17
17
 
18
18
  #pragma once
19
19
 
20
+ #include <regex>
21
+
20
22
  #include <tao/json.hpp>
21
23
 
22
24
  #include <version.hxx>
@@ -54,7 +56,7 @@ struct collection_drop_request {
54
56
  };
55
57
 
56
58
  collection_drop_response
57
- make_response(error_context::http&& ctx, collection_drop_request&, collection_drop_request::encoded_response_type&& encoded)
59
+ make_response(error_context::http&& ctx, collection_drop_request& /* request */, collection_drop_request::encoded_response_type&& encoded)
58
60
  {
59
61
  collection_drop_response response{ ctx };
60
62
  if (!response.ctx.ec) {
@@ -62,15 +64,17 @@ make_response(error_context::http&& ctx, collection_drop_request&, collection_dr
62
64
  case 400:
63
65
  response.ctx.ec = std::make_error_code(error::common_errc::unsupported_operation);
64
66
  break;
65
- case 404:
66
- if (encoded.body.find("Collection with this name is not found") != std::string::npos) {
67
+ case 404: {
68
+ std::regex scope_not_found("Scope with name .+ is not found");
69
+ std::regex collection_not_found("Collection with name .+ is not found");
70
+ if (std::regex_search(encoded.body, collection_not_found)) {
67
71
  response.ctx.ec = std::make_error_code(error::common_errc::collection_not_found);
68
- } else if (encoded.body.find("Scope with this name is not found") != std::string::npos) {
72
+ } else if (std::regex_search(encoded.body, scope_not_found)) {
69
73
  response.ctx.ec = std::make_error_code(error::common_errc::scope_not_found);
70
74
  } else {
71
75
  response.ctx.ec = std::make_error_code(error::common_errc::bucket_not_found);
72
76
  }
73
- break;
77
+ } break;
74
78
  case 200: {
75
79
  tao::json::value payload{};
76
80
  try {
@@ -45,6 +45,7 @@ struct decrement_request {
45
45
  std::optional<std::uint16_t> durability_timeout{};
46
46
  std::chrono::milliseconds timeout{ timeout_defaults::key_value_timeout };
47
47
  io::retry_context<io::retry_strategy::best_effort> retries{ false };
48
+ bool preserve_expiry{ false };
48
49
 
49
50
  [[nodiscard]] std::error_code encode_to(encoded_request_type& encoded, mcbp_context&&)
50
51
  {
@@ -62,6 +63,9 @@ struct decrement_request {
62
63
  if (durability_level != protocol::durability_level::none) {
63
64
  encoded.body().durability(durability_level, durability_timeout);
64
65
  }
66
+ if (preserve_expiry) {
67
+ encoded.body().preserve_expiry();
68
+ }
65
69
  return {};
66
70
  }
67
71
  };
@@ -47,6 +47,7 @@ struct increment_request {
47
47
  std::optional<std::uint16_t> durability_timeout{};
48
48
  std::chrono::milliseconds timeout{ timeout_defaults::key_value_timeout };
49
49
  io::retry_context<io::retry_strategy::best_effort> retries{ false };
50
+ bool preserve_expiry{ false };
50
51
 
51
52
  [[nodiscard]] std::error_code encode_to(encoded_request_type& encoded, mcbp_context&&)
52
53
  {
@@ -64,6 +65,9 @@ struct increment_request {
64
65
  if (durability_level != protocol::durability_level::none) {
65
66
  encoded.body().durability(durability_level, durability_timeout);
66
67
  }
68
+ if (preserve_expiry) {
69
+ encoded.body().preserve_expiry();
70
+ }
67
71
  return {};
68
72
  }
69
73
  };
@@ -70,6 +70,7 @@ struct mutate_in_request {
70
70
  std::optional<std::uint16_t> durability_timeout{};
71
71
  std::chrono::milliseconds timeout{ timeout_defaults::key_value_timeout };
72
72
  io::retry_context<io::retry_strategy::best_effort> retries{ false };
73
+ bool preserve_expiry{ false };
73
74
 
74
75
  [[nodiscard]] std::error_code encode_to(encoded_request_type& encoded, mcbp_context&& ctx)
75
76
  {
@@ -102,6 +103,9 @@ struct mutate_in_request {
102
103
  if (durability_level != protocol::durability_level::none) {
103
104
  encoded.body().durability(durability_level, durability_timeout);
104
105
  }
106
+ if (preserve_expiry) {
107
+ encoded.body().preserve_expiry();
108
+ }
105
109
  return {};
106
110
  }
107
111
  };
@@ -380,6 +380,7 @@ make_response(error_context::query&& ctx, query_request& request, query_request:
380
380
  bool syntax_error = false;
381
381
  bool server_timeout = false;
382
382
  bool invalid_argument = false;
383
+ bool cas_mismatch = false;
383
384
 
384
385
  if (response.payload.meta_data.errors) {
385
386
  for (const auto& error : *response.payload.meta_data.errors) {
@@ -401,6 +402,11 @@ make_response(error_context::query&& ctx, query_request& request, query_request:
401
402
  case 4090: /* IKey: "plan.build_prepared.name_not_in_encoded_plan" */
402
403
  prepared_statement_failure = true;
403
404
  break;
405
+ case 12009: /* IKey: "datastore.couchbase.DML_error" */
406
+ if (error.message.find("CAS mismatch") != std::string::npos) {
407
+ cas_mismatch = true;
408
+ }
409
+ break;
404
410
  case 12004: /* IKey: "datastore.couchbase.primary_idx_not_found" */
405
411
  case 12016: /* IKey: "datastore.couchbase.index_not_found" */
406
412
  index_not_found = true;
@@ -429,6 +435,8 @@ make_response(error_context::query&& ctx, query_request& request, query_request:
429
435
  response.ctx.ec = std::make_error_code(error::query_errc::planning_failure);
430
436
  } else if (index_not_found) {
431
437
  response.ctx.ec = std::make_error_code(error::common_errc::index_not_found);
438
+ } else if (cas_mismatch) {
439
+ response.ctx.ec = std::make_error_code(error::common_errc::cas_mismatch);
432
440
  } else {
433
441
  response.ctx.ec = std::make_error_code(error::common_errc::internal_server_failure);
434
442
  }
@@ -46,6 +46,7 @@ struct replace_request {
46
46
  std::optional<std::uint16_t> durability_timeout{};
47
47
  std::chrono::milliseconds timeout{ timeout_defaults::key_value_timeout };
48
48
  io::retry_context<io::retry_strategy::best_effort> retries{ false };
49
+ bool preserve_expiry{ false };
49
50
 
50
51
  [[nodiscard]] std::error_code encode_to(encoded_request_type& encoded, mcbp_context&&)
51
52
  {
@@ -59,6 +60,9 @@ struct replace_request {
59
60
  if (durability_level != protocol::durability_level::none) {
60
61
  encoded.body().durability(durability_level, durability_timeout);
61
62
  }
63
+ if (preserve_expiry) {
64
+ encoded.body().preserve_expiry();
65
+ }
62
66
  return {};
63
67
  }
64
68
  };
@@ -45,6 +45,7 @@ struct upsert_request {
45
45
  std::optional<std::uint16_t> durability_timeout{};
46
46
  std::chrono::milliseconds timeout{ timeout_defaults::key_value_timeout };
47
47
  io::retry_context<io::retry_strategy::best_effort> retries{ false };
48
+ bool preserve_expiry{ false };
48
49
 
49
50
  [[nodiscard]] std::error_code encode_to(encoded_request_type& encoded, mcbp_context&&)
50
51
  {
@@ -57,6 +58,9 @@ struct upsert_request {
57
58
  if (durability_level != protocol::durability_level::none) {
58
59
  encoded.body().durability(durability_level, durability_timeout);
59
60
  }
61
+ if (preserve_expiry) {
62
+ encoded.body().preserve_expiry();
63
+ }
60
64
  return {};
61
65
  }
62
66
  };
@@ -17,6 +17,8 @@
17
17
 
18
18
  #pragma once
19
19
 
20
+ #include <regex>
21
+
20
22
  #include <tao/json.hpp>
21
23
 
22
24
  #include <version.hxx>
@@ -55,22 +57,21 @@ struct scope_create_request {
55
57
  };
56
58
 
57
59
  scope_create_response
58
- make_response(error_context::http&& ctx, scope_create_request&, scope_create_request::encoded_response_type&& encoded)
60
+ make_response(error_context::http&& ctx, scope_create_request& /* request */, scope_create_request::encoded_response_type&& encoded)
59
61
  {
60
62
  scope_create_response response{ ctx };
61
63
  if (!response.ctx.ec) {
62
64
  switch (encoded.status_code) {
63
- case 400:
64
- if (encoded.body.find("Not allowed on this version") != std::string::npos) {
65
- response.ctx.ec = std::make_error_code(error::common_errc::unsupported_operation);
66
- } else if (encoded.body.find("Scope with this name already exists") != std::string::npos) {
65
+ case 400: {
66
+ std::regex scope_exists("Scope with name .+ already exists");
67
+ if (std::regex_search(encoded.body, scope_exists)) {
67
68
  response.ctx.ec = std::make_error_code(error::management_errc::scope_exists);
68
69
  } else if (encoded.body.find("Not allowed on this version of cluster") != std::string::npos) {
69
70
  response.ctx.ec = std::make_error_code(error::common_errc::feature_not_available);
70
71
  } else {
71
72
  response.ctx.ec = std::make_error_code(error::common_errc::invalid_argument);
72
73
  }
73
- break;
74
+ } break;
74
75
  case 404:
75
76
  response.ctx.ec = std::make_error_code(error::common_errc::bucket_not_found);
76
77
  break;
@@ -17,6 +17,8 @@
17
17
 
18
18
  #pragma once
19
19
 
20
+ #include <regex>
21
+
20
22
  #include <tao/json.hpp>
21
23
 
22
24
  #include <version.hxx>
@@ -53,7 +55,7 @@ struct scope_drop_request {
53
55
  };
54
56
 
55
57
  scope_drop_response
56
- make_response(error_context::http&& ctx, scope_drop_request&, scope_drop_request::encoded_response_type&& encoded)
58
+ make_response(error_context::http&& ctx, scope_drop_request& /* request */, scope_drop_request::encoded_response_type&& encoded)
57
59
  {
58
60
  scope_drop_response response{ ctx };
59
61
  if (!response.ctx.ec) {
@@ -61,13 +63,14 @@ make_response(error_context::http&& ctx, scope_drop_request&, scope_drop_request
61
63
  case 400:
62
64
  response.ctx.ec = std::make_error_code(error::common_errc::unsupported_operation);
63
65
  break;
64
- case 404:
65
- if (encoded.body.find("Scope with this name is not found") != std::string::npos) {
66
+ case 404: {
67
+ std::regex scope_not_found("Scope with name .+ is not found");
68
+ if (std::regex_search(encoded.body, scope_not_found)) {
66
69
  response.ctx.ec = std::make_error_code(error::common_errc::scope_not_found);
67
70
  } else {
68
71
  response.ctx.ec = std::make_error_code(error::common_errc::bucket_not_found);
69
72
  }
70
- break;
73
+ } break;
71
74
  case 200: {
72
75
  tao::json::value payload{};
73
76
  try {
@@ -233,6 +233,7 @@ enum class client_opcode : uint8_t {
233
233
  enum class subdoc_opcode : uint8_t {
234
234
  get_doc = 0x00,
235
235
  set_doc = 0x01,
236
+ remove_doc = 0x04,
236
237
  get = 0xc5,
237
238
  exists = 0xc6,
238
239
  dict_add = 0xc7,
@@ -345,6 +346,7 @@ is_valid_subdoc_opcode(uint8_t code)
345
346
  case subdoc_opcode::get_count:
346
347
  case subdoc_opcode::get_doc:
347
348
  case subdoc_opcode::set_doc:
349
+ case subdoc_opcode::remove_doc:
348
350
  case subdoc_opcode::replace_body_with_xattr:
349
351
  return true;
350
352
  }
@@ -626,6 +628,9 @@ struct fmt::formatter<couchbase::protocol::subdoc_opcode> : formatter<string_vie
626
628
  case couchbase::protocol::subdoc_opcode::replace_body_with_xattr:
627
629
  name = "replace_body_with_xattr (0xd3)";
628
630
  break;
631
+ case couchbase::protocol::subdoc_opcode::remove_doc:
632
+ name = "remove_doc (0x04)";
633
+ break;
629
634
  }
630
635
  return formatter<string_view>::format(name, ctx);
631
636
  }
@@ -48,7 +48,7 @@ class decrement_response_body
48
48
  bool parse(protocol::status status,
49
49
  const header_buffer& header,
50
50
  std::uint8_t framing_extras_size,
51
- std::uint16_t /* key_size */,
51
+ std::uint16_t key_size,
52
52
  std::uint8_t extras_size,
53
53
  const std::vector<uint8_t>& body,
54
54
  const cmd_info&)
@@ -66,6 +66,7 @@ class decrement_response_body
66
66
  token_.sequence_number = utils::byte_swap_64(token_.sequence_number);
67
67
  offset += 8;
68
68
  }
69
+ offset += key_size;
69
70
  memcpy(&content_, body.data() + offset, sizeof(content_));
70
71
  content_ = utils::byte_swap_64(content_);
71
72
  return true;
@@ -119,19 +120,28 @@ class decrement_request_body
119
120
  return;
120
121
  }
121
122
  auto frame_id = static_cast<uint8_t>(protocol::request_frame_info_id::durability_requirement);
123
+ auto extras_size = framing_extras_.size();
122
124
  if (timeout) {
123
- framing_extras_.resize(4);
124
- framing_extras_[0] = static_cast<std::uint8_t>((static_cast<std::uint32_t>(frame_id) << 4U) | 3U);
125
- framing_extras_[1] = static_cast<std::uint8_t>(level);
125
+ framing_extras_.resize(extras_size + 4);
126
+ framing_extras_[extras_size + 0] = static_cast<std::uint8_t>((static_cast<std::uint32_t>(frame_id) << 4U) | 3U);
127
+ framing_extras_[extras_size + 1] = static_cast<std::uint8_t>(level);
126
128
  uint16_t val = htons(*timeout);
127
- memcpy(framing_extras_.data() + 2, &val, sizeof(val));
129
+ memcpy(framing_extras_.data() + extras_size + 2, &val, sizeof(val));
128
130
  } else {
129
- framing_extras_.resize(2);
130
- framing_extras_[0] = static_cast<std::uint8_t>(static_cast<std::uint32_t>(frame_id) << 4U | 1U);
131
- framing_extras_[1] = static_cast<std::uint8_t>(level);
131
+ framing_extras_.resize(extras_size + 2);
132
+ framing_extras_[extras_size + 0] = static_cast<std::uint8_t>(static_cast<std::uint32_t>(frame_id) << 4U | 1U);
133
+ framing_extras_[extras_size + 1] = static_cast<std::uint8_t>(level);
132
134
  }
133
135
  }
134
136
 
137
+ void preserve_expiry()
138
+ {
139
+ auto frame_id = static_cast<uint8_t>(protocol::request_frame_info_id::preserve_ttl);
140
+ auto extras_size = framing_extras_.size();
141
+ framing_extras_.resize(extras_size + 1);
142
+ framing_extras_[extras_size + 0] = static_cast<std::uint8_t>(static_cast<std::uint32_t>(frame_id) << 4U | 0U);
143
+ }
144
+
135
145
  const std::string& key()
136
146
  {
137
147
  return key_;
@@ -80,7 +80,6 @@ class hello_request_body
80
80
  hello_feature::xattr,
81
81
  hello_feature::xerror,
82
82
  hello_feature::select_bucket,
83
- hello_feature::snappy,
84
83
  hello_feature::json,
85
84
  hello_feature::duplex,
86
85
  hello_feature::alt_request_support,
@@ -89,6 +88,7 @@ class hello_request_body
89
88
  hello_feature::vattr,
90
89
  hello_feature::collections,
91
90
  hello_feature::subdoc_create_as_deleted,
91
+ hello_feature::preserve_ttl,
92
92
  };
93
93
  std::vector<std::uint8_t> value_;
94
94
 
@@ -113,6 +113,11 @@ class hello_request_body
113
113
  features_.emplace_back(hello_feature::clustermap_change_notification);
114
114
  }
115
115
 
116
+ void enable_compression()
117
+ {
118
+ features_.emplace_back(hello_feature::snappy);
119
+ }
120
+
116
121
  [[nodiscard]] const std::vector<hello_feature>& features() const
117
122
  {
118
123
  return features_;
@@ -48,7 +48,7 @@ class increment_response_body
48
48
  bool parse(protocol::status status,
49
49
  const header_buffer& header,
50
50
  std::uint8_t framing_extras_size,
51
- std::uint16_t /* key_size */,
51
+ std::uint16_t key_size,
52
52
  std::uint8_t extras_size,
53
53
  const std::vector<uint8_t>& body,
54
54
  const cmd_info&)
@@ -66,6 +66,7 @@ class increment_response_body
66
66
  token_.sequence_number = utils::byte_swap_64(token_.sequence_number);
67
67
  offset += 8;
68
68
  }
69
+ offset += key_size;
69
70
  memcpy(&content_, body.data() + offset, sizeof(content_));
70
71
  content_ = utils::byte_swap_64(content_);
71
72
  return true;
@@ -119,19 +120,28 @@ class increment_request_body
119
120
  return;
120
121
  }
121
122
  auto frame_id = static_cast<uint8_t>(protocol::request_frame_info_id::durability_requirement);
123
+ auto extras_size = framing_extras_.size();
122
124
  if (timeout) {
123
- framing_extras_.resize(4);
124
- framing_extras_[0] = static_cast<std::uint8_t>((static_cast<std::uint32_t>(frame_id) << 4U) | 3U);
125
- framing_extras_[1] = static_cast<std::uint8_t>(level);
125
+ framing_extras_.resize(extras_size + 4);
126
+ framing_extras_[extras_size + 0] = static_cast<std::uint8_t>((static_cast<std::uint32_t>(frame_id) << 4U) | 3U);
127
+ framing_extras_[extras_size + 1] = static_cast<std::uint8_t>(level);
126
128
  uint16_t val = htons(*timeout);
127
- memcpy(framing_extras_.data() + 2, &val, sizeof(val));
129
+ memcpy(framing_extras_.data() + extras_size + 2, &val, sizeof(val));
128
130
  } else {
129
- framing_extras_.resize(2);
130
- framing_extras_[0] = static_cast<std::uint8_t>(static_cast<std::uint32_t>(frame_id) << 4U | 1U);
131
- framing_extras_[1] = static_cast<std::uint8_t>(level);
131
+ framing_extras_.resize(extras_size + 2);
132
+ framing_extras_[extras_size + 0] = static_cast<std::uint8_t>(static_cast<std::uint32_t>(frame_id) << 4U | 1U);
133
+ framing_extras_[extras_size + 1] = static_cast<std::uint8_t>(level);
132
134
  }
133
135
  }
134
136
 
137
+ void preserve_expiry()
138
+ {
139
+ auto frame_id = static_cast<uint8_t>(protocol::request_frame_info_id::preserve_ttl);
140
+ auto extras_size = framing_extras_.size();
141
+ framing_extras_.resize(extras_size + 1);
142
+ framing_extras_[extras_size + 0] = static_cast<std::uint8_t>(static_cast<std::uint32_t>(frame_id) << 4U | 0U);
143
+ }
144
+
135
145
  const std::string& key()
136
146
  {
137
147
  return key_;
@@ -224,7 +224,7 @@ class mutate_in_request_body
224
224
 
225
225
  void add_spec(subdoc_opcode operation, bool xattr, const std::string& path)
226
226
  {
227
- Expects(operation == protocol::subdoc_opcode::remove);
227
+ Expects(operation == protocol::subdoc_opcode::remove || operation == protocol::subdoc_opcode::remove_doc);
228
228
  add_spec(static_cast<std::uint8_t>(operation), build_path_flags(xattr, false, false), path, "");
229
229
  }
230
230
 
@@ -305,19 +305,28 @@ class mutate_in_request_body
305
305
  return;
306
306
  }
307
307
  auto frame_id = static_cast<uint8_t>(protocol::request_frame_info_id::durability_requirement);
308
+ auto extras_size = framing_extras_.size();
308
309
  if (timeout) {
309
- framing_extras_.resize(4);
310
- framing_extras_[0] = static_cast<std::uint8_t>((static_cast<std::uint32_t>(frame_id) << 4U) | 3U);
311
- framing_extras_[1] = static_cast<std::uint8_t>(level);
310
+ framing_extras_.resize(extras_size + 4);
311
+ framing_extras_[extras_size + 0] = static_cast<std::uint8_t>((static_cast<std::uint32_t>(frame_id) << 4U) | 3U);
312
+ framing_extras_[extras_size + 1] = static_cast<std::uint8_t>(level);
312
313
  uint16_t val = htons(*timeout);
313
- memcpy(framing_extras_.data() + 2, &val, sizeof(val));
314
+ memcpy(framing_extras_.data() + extras_size + 2, &val, sizeof(val));
314
315
  } else {
315
- framing_extras_.resize(2);
316
- framing_extras_[0] = static_cast<std::uint8_t>(static_cast<std::uint32_t>(frame_id) << 4U | 1U);
317
- framing_extras_[1] = static_cast<std::uint8_t>(level);
316
+ framing_extras_.resize(extras_size + 2);
317
+ framing_extras_[extras_size + 0] = static_cast<std::uint8_t>(static_cast<std::uint32_t>(frame_id) << 4U | 1U);
318
+ framing_extras_[extras_size + 1] = static_cast<std::uint8_t>(level);
318
319
  }
319
320
  }
320
321
 
322
+ void preserve_expiry()
323
+ {
324
+ auto frame_id = static_cast<uint8_t>(protocol::request_frame_info_id::preserve_ttl);
325
+ auto extras_size = framing_extras_.size();
326
+ framing_extras_.resize(extras_size + 1);
327
+ framing_extras_[extras_size + 0] = static_cast<std::uint8_t>(static_cast<std::uint32_t>(frame_id) << 4U | 0U);
328
+ }
329
+
321
330
  const std::string& key()
322
331
  {
323
332
  return key_;
@@ -97,19 +97,28 @@ class replace_request_body
97
97
  return;
98
98
  }
99
99
  auto frame_id = static_cast<uint8_t>(protocol::request_frame_info_id::durability_requirement);
100
+ auto extras_size = framing_extras_.size();
100
101
  if (timeout) {
101
- framing_extras_.resize(4);
102
- framing_extras_[0] = static_cast<std::uint8_t>((static_cast<std::uint32_t>(frame_id) << 4U) | 3U);
103
- framing_extras_[1] = static_cast<std::uint8_t>(level);
102
+ framing_extras_.resize(extras_size + 4);
103
+ framing_extras_[extras_size + 0] = static_cast<std::uint8_t>((static_cast<std::uint32_t>(frame_id) << 4U) | 3U);
104
+ framing_extras_[extras_size + 1] = static_cast<std::uint8_t>(level);
104
105
  uint16_t val = htons(*timeout);
105
- memcpy(framing_extras_.data() + 2, &val, sizeof(val));
106
+ memcpy(framing_extras_.data() + extras_size + 2, &val, sizeof(val));
106
107
  } else {
107
- framing_extras_.resize(2);
108
- framing_extras_[0] = static_cast<std::uint8_t>(static_cast<std::uint32_t>(frame_id) << 4U | 1U);
109
- framing_extras_[1] = static_cast<std::uint8_t>(level);
108
+ framing_extras_.resize(extras_size + 2);
109
+ framing_extras_[extras_size + 0] = static_cast<std::uint8_t>(static_cast<std::uint32_t>(frame_id) << 4U | 1U);
110
+ framing_extras_[extras_size + 1] = static_cast<std::uint8_t>(level);
110
111
  }
111
112
  }
112
113
 
114
+ void preserve_expiry()
115
+ {
116
+ auto frame_id = static_cast<uint8_t>(protocol::request_frame_info_id::preserve_ttl);
117
+ auto extras_size = framing_extras_.size();
118
+ framing_extras_.resize(extras_size + 1);
119
+ framing_extras_[extras_size + 0] = static_cast<std::uint8_t>(static_cast<std::uint32_t>(frame_id) << 4U | 0U);
120
+ }
121
+
113
122
  void content(const std::string& content)
114
123
  {
115
124
  content_ = { content.begin(), content.end() };
@@ -97,19 +97,28 @@ class upsert_request_body
97
97
  return;
98
98
  }
99
99
  auto frame_id = static_cast<uint8_t>(protocol::request_frame_info_id::durability_requirement);
100
+ auto extras_size = framing_extras_.size();
100
101
  if (timeout) {
101
- framing_extras_.resize(4);
102
- framing_extras_[0] = static_cast<std::uint8_t>((static_cast<std::uint32_t>(frame_id) << 4U) | 3U);
103
- framing_extras_[1] = static_cast<std::uint8_t>(level);
102
+ framing_extras_.resize(extras_size + 4);
103
+ framing_extras_[extras_size + 0] = static_cast<std::uint8_t>((static_cast<std::uint32_t>(frame_id) << 4U) | 3U);
104
+ framing_extras_[extras_size + 1] = static_cast<std::uint8_t>(level);
104
105
  uint16_t val = htons(*timeout);
105
- memcpy(framing_extras_.data() + 2, &val, sizeof(val));
106
+ memcpy(framing_extras_.data() + extras_size + 2, &val, sizeof(val));
106
107
  } else {
107
- framing_extras_.resize(2);
108
- framing_extras_[0] = static_cast<std::uint8_t>(static_cast<std::uint32_t>(frame_id) << 4U | 1U);
109
- framing_extras_[1] = static_cast<std::uint8_t>(level);
108
+ framing_extras_.resize(extras_size + 2);
109
+ framing_extras_[extras_size + 0] = static_cast<std::uint8_t>(static_cast<std::uint32_t>(frame_id) << 4U | 1U);
110
+ framing_extras_[extras_size + 1] = static_cast<std::uint8_t>(level);
110
111
  }
111
112
  }
112
113
 
114
+ void preserve_expiry()
115
+ {
116
+ auto frame_id = static_cast<uint8_t>(protocol::request_frame_info_id::preserve_ttl);
117
+ auto extras_size = framing_extras_.size();
118
+ framing_extras_.resize(extras_size + 1);
119
+ framing_extras_[extras_size + 0] = static_cast<std::uint8_t>(static_cast<std::uint32_t>(frame_id) << 4U | 0U);
120
+ }
121
+
113
122
  void content(const std::string& content)
114
123
  {
115
124
  content_ = { content.begin(), content.end() };
@@ -90,7 +90,7 @@ enum class request_frame_info_id : uint8_t {
90
90
  * If the request modifies an existing document the expiry time from the existing document should be used instead of the TTL provided.
91
91
  * If document don't exist the provided TTL should be used. The frame info contains no value (length = 0).
92
92
  */
93
- preserve_ttl = 0x1f,
93
+ preserve_ttl = 0x05,
94
94
  };
95
95
 
96
96
  constexpr inline bool
@@ -348,6 +348,15 @@ extract_options(connection_string& connstr)
348
348
  } else if (param.second == "false" || param.second == "no" || param.second == "off") {
349
349
  connstr.options.enable_unordered_execution = false;
350
350
  }
351
+ } else if (param.first == "enable_compression") {
352
+ /**
353
+ * Announce support of compression (snappy) to server
354
+ */
355
+ if (param.second == "true" || param.second == "yes" || param.second == "on") {
356
+ connstr.options.enable_compression = true;
357
+ } else if (param.second == "false" || param.second == "no" || param.second == "off") {
358
+ connstr.options.enable_compression = false;
359
+ }
351
360
  } else {
352
361
  spdlog::warn(R"(unknown parameter "{}" in connection string (value "{}"))", param.first, param.second);
353
362
  }
@@ -24,7 +24,7 @@
24
24
  namespace couchbase
25
25
  {
26
26
  constexpr auto BACKEND_VERSION_MAJOR = 1;
27
- constexpr auto BACKEND_VERSION_MINOR = 4;
27
+ constexpr auto BACKEND_VERSION_MINOR = 5;
28
28
  constexpr auto BACKEND_VERSION_PATCH = 0;
29
29
 
30
30
  inline const std::string&
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  require "couchbase"
16
+ require "digest/sha2"
16
17
 
17
18
  module ActiveSupport
18
19
  module Cache
@@ -507,10 +507,13 @@ module Couchbase
507
507
  attr_accessor :expiry # @return [Integer, #in_seconds, nil]
508
508
  attr_accessor :transcoder # @return [JsonTranscoder, #encode(Object)]
509
509
  attr_accessor :durability_level # @return [Symbol]
510
+ attr_accessor :preserve_expiry # @return [Boolean]
510
511
 
511
512
  # Creates an instance of options for {Collection#upsert}
512
513
  #
513
514
  # @param [Integer, #in_seconds, Time, nil] expiry expiration time to associate with the document
515
+ # @param [Boolean] preserve_expiry if true and the document exists, the server will preserve current expiration
516
+ # for the document, otherwise will use {expiry} from the operation.
514
517
  # @param [JsonTranscoder, #encode(Object)] transcoder used for encoding
515
518
  # @param [Symbol] durability_level level of durability
516
519
  # +:none+::
@@ -533,6 +536,7 @@ module Couchbase
533
536
  #
534
537
  # @yieldparam [Upsert]
535
538
  def initialize(expiry: nil,
539
+ preserve_expiry: false,
536
540
  transcoder: JsonTranscoder.new,
537
541
  durability_level: :none,
538
542
  timeout: nil,
@@ -541,6 +545,7 @@ module Couchbase
541
545
  parent_span: nil)
542
546
  super(timeout: timeout, retry_strategy: retry_strategy, client_context: client_context, parent_span: parent_span)
543
547
  @expiry = Utils::Time.extract_expiry_time(expiry)
548
+ @preserve_expiry = preserve_expiry
544
549
  @transcoder = transcoder
545
550
  @durability_level = durability_level
546
551
  yield self if block_given?
@@ -550,6 +555,7 @@ module Couchbase
550
555
  {
551
556
  timeout: @timeout.respond_to?(:in_milliseconds) ? @timeout.public_send(:in_milliseconds) : @timeout,
552
557
  expiry: @expiry,
558
+ preserve_expiry: @preserve_expiry,
553
559
  durability_level: @durability_level,
554
560
  }
555
561
  end
@@ -560,10 +566,13 @@ module Couchbase
560
566
  attr_accessor :expiry # @return [Integer, #in_seconds, nil]
561
567
  attr_accessor :transcoder # @return [JsonTranscoder, #encode(Object)]
562
568
  attr_accessor :durability_level # @return [Symbol]
569
+ attr_accessor :preserve_expiry # @return [Boolean]
563
570
 
564
571
  # Creates an instance of options for {Collection#upsert}
565
572
  #
566
573
  # @param [Integer, #in_seconds, Time, nil] expiry expiration time to associate with the document
574
+ # @param [Boolean] preserve_expiry if true and the document exists, the server will preserve current expiration
575
+ # for the document, otherwise will use {expiry} from the operation.
567
576
  # @param [JsonTranscoder, #encode(Object)] transcoder used for encoding
568
577
  # @param [Symbol] durability_level level of durability
569
578
  # +:none+::
@@ -586,6 +595,7 @@ module Couchbase
586
595
  #
587
596
  # @yieldparam [Upsert]
588
597
  def initialize(expiry: nil,
598
+ preserve_expiry: false,
589
599
  transcoder: JsonTranscoder.new,
590
600
  durability_level: :none,
591
601
  timeout: nil,
@@ -594,6 +604,7 @@ module Couchbase
594
604
  parent_span: nil)
595
605
  super(timeout: timeout, retry_strategy: retry_strategy, client_context: client_context, parent_span: parent_span)
596
606
  @expiry = Utils::Time.extract_expiry_time(expiry)
607
+ @preserve_expiry = preserve_expiry
597
608
  @transcoder = transcoder
598
609
  @durability_level = durability_level
599
610
  yield self if block_given?
@@ -603,6 +614,7 @@ module Couchbase
603
614
  {
604
615
  timeout: @timeout.respond_to?(:in_milliseconds) ? @timeout.public_send(:in_milliseconds) : @timeout,
605
616
  expiry: @expiry,
617
+ preserve_expiry: @preserve_expiry,
606
618
  durability_level: @durability_level,
607
619
  }
608
620
  end
@@ -614,10 +626,13 @@ module Couchbase
614
626
  attr_accessor :transcoder # @return [JsonTranscoder, #encode(Object)]
615
627
  attr_accessor :cas # @return [Integer, nil]
616
628
  attr_accessor :durability_level # @return [Symbol]
629
+ attr_accessor :preserve_expiry # @return [Boolean]
617
630
 
618
631
  # Creates an instance of options for {Collection#replace}
619
632
  #
620
633
  # @param [Integer, #in_seconds, nil] expiry expiration time to associate with the document
634
+ # @param [Boolean] preserve_expiry if true and the document exists, the server will preserve current expiration
635
+ # for the document, otherwise will use {expiry} from the operation.
621
636
  # @param [JsonTranscoder, #encode(Object)] transcoder used for encoding
622
637
  # @param [Integer, nil] cas a CAS value that will be taken into account on the server side for optimistic concurrency
623
638
  # @param [Symbol] durability_level level of durability
@@ -641,6 +656,7 @@ module Couchbase
641
656
  #
642
657
  # @yieldparam [Replace]
643
658
  def initialize(expiry: nil,
659
+ preserve_expiry: false,
644
660
  transcoder: JsonTranscoder.new,
645
661
  cas: nil,
646
662
  durability_level: :none,
@@ -650,6 +666,7 @@ module Couchbase
650
666
  parent_span: nil)
651
667
  super(timeout: timeout, retry_strategy: retry_strategy, client_context: client_context, parent_span: parent_span)
652
668
  @expiry = Utils::Time.extract_expiry_time(expiry)
669
+ @preserve_expiry = preserve_expiry
653
670
  @transcoder = transcoder
654
671
  @cas = cas
655
672
  @durability_level = durability_level
@@ -660,6 +677,7 @@ module Couchbase
660
677
  {
661
678
  timeout: @timeout.respond_to?(:in_milliseconds) ? @timeout.public_send(:in_milliseconds) : @timeout,
662
679
  expiry: @expiry,
680
+ preserve_expiry: @preserve_expiry,
663
681
  durability_level: @durability_level,
664
682
  cas: @cas,
665
683
  }
@@ -673,10 +691,13 @@ module Couchbase
673
691
  attr_accessor :cas # @return [Integer, nil]
674
692
  attr_accessor :durability_level # @return [Symbol]
675
693
  attr_accessor :transcoder # @return [JsonTranscoder, #encode(Object)]
694
+ attr_accessor :preserve_expiry # @return [Boolean]
676
695
 
677
696
  # Creates an instance of options for {Collection#mutate_in}
678
697
  #
679
698
  # @param [Integer, #in_seconds, Time, nil] expiry expiration time to associate with the document
699
+ # @param [Boolean] preserve_expiry if true and the document exists, the server will preserve current expiration
700
+ # for the document, otherwise will use {expiry} from the operation.
680
701
  # @param [Symbol] store_semantics describes how the outer document store semantics on subdoc should act
681
702
  # +:replace+:: replace the document, fail if it does not exist. This is the default
682
703
  # +:upsert+:: replace the document or create if it does not exist
@@ -706,6 +727,7 @@ module Couchbase
706
727
  #
707
728
  # @yieldparam [MutateIn]
708
729
  def initialize(expiry: nil,
730
+ preserve_expiry: false,
709
731
  store_semantics: :replace,
710
732
  cas: nil,
711
733
  access_deleted: false,
@@ -718,6 +740,7 @@ module Couchbase
718
740
  parent_span: nil)
719
741
  super(timeout: timeout, retry_strategy: retry_strategy, client_context: client_context, parent_span: parent_span)
720
742
  @expiry = Utils::Time.extract_expiry_time(expiry)
743
+ @preserve_expiry = preserve_expiry
721
744
  @store_semantics = store_semantics
722
745
  @cas = cas
723
746
  @access_deleted = access_deleted
@@ -732,6 +755,7 @@ module Couchbase
732
755
  {
733
756
  timeout: @timeout.respond_to?(:in_milliseconds) ? @timeout.public_send(:in_milliseconds) : @timeout,
734
757
  expiry: @expiry,
758
+ preserve_expiry: @preserve_expiry,
735
759
  durability_level: @durability_level,
736
760
  cas: @cas,
737
761
  store_semantics: @store_semantics,
@@ -862,12 +886,15 @@ module Couchbase
862
886
  attr_accessor :initial # @return [Integer]
863
887
  attr_accessor :expiry # @return [Integer, #in_seconds]
864
888
  attr_accessor :durability_level # @return [Symbol]
889
+ attr_accessor :preserve_expiry # @return [Boolean]
865
890
 
866
891
  # Creates an instance of options for {BinaryCollection#increment}
867
892
  #
868
893
  # @param [Integer] delta the delta for the operation
869
894
  # @param [Integer] initial if present, holds the initial value
870
895
  # @param [Integer, #in_seconds, Time, nil] expiry if set, holds the expiration for the operation
896
+ # @param [Boolean] preserve_expiry if true and the document exists, the server will preserve current expiration
897
+ # for the document, otherwise will use {expiry} from the operation.
871
898
  # @param [Symbol] durability_level level of durability
872
899
  # +:none+::
873
900
  # no enhanced durability required for the mutation
@@ -891,6 +918,7 @@ module Couchbase
891
918
  def initialize(delta: 1,
892
919
  initial: nil,
893
920
  expiry: nil,
921
+ preserve_expiry: false,
894
922
  durability_level: :none,
895
923
  timeout: nil,
896
924
  retry_strategy: nil,
@@ -902,6 +930,7 @@ module Couchbase
902
930
  @delta = delta
903
931
  @initial = initial
904
932
  @expiry = Utils::Time.extract_expiry_time(expiry)
933
+ @preserve_expiry = preserve_expiry
905
934
  @durability_level = durability_level
906
935
  yield self if block_given?
907
936
  end
@@ -920,6 +949,7 @@ module Couchbase
920
949
  delta: @delta,
921
950
  initial_value: @initial,
922
951
  expiry: @expiry,
952
+ preserve_expiry: @preserve_expiry,
923
953
  durability_level: @durability_level,
924
954
  }
925
955
  end
@@ -931,12 +961,15 @@ module Couchbase
931
961
  attr_accessor :initial # @return [Integer]
932
962
  attr_accessor :expiry # @return [Integer, #in_seconds]
933
963
  attr_accessor :durability_level # @return [Symbol]
964
+ attr_accessor :preserve_expiry # @return [Boolean]
934
965
 
935
966
  # Creates an instance of options for {BinaryCollection#decrement}
936
967
  #
937
968
  # @param [Integer] delta the delta for the operation
938
969
  # @param [Integer] initial if present, holds the initial value
939
970
  # @param [Integer, #in_seconds, Time, nil] expiry if set, holds the expiration for the operation
971
+ # @param [Boolean] preserve_expiry if true and the document exists, the server will preserve current expiration
972
+ # for the document, otherwise will use {expiry} from the operation.
940
973
  # @param [Symbol] durability_level level of durability
941
974
  # +:none+::
942
975
  # no enhanced durability required for the mutation
@@ -960,6 +993,7 @@ module Couchbase
960
993
  def initialize(delta: 1,
961
994
  initial: nil,
962
995
  expiry: nil,
996
+ preserve_expiry: false,
963
997
  durability_level: :none,
964
998
  timeout: nil,
965
999
  retry_strategy: nil,
@@ -971,6 +1005,7 @@ module Couchbase
971
1005
  @delta = delta
972
1006
  @initial = initial
973
1007
  @expiry = Utils::Time.extract_expiry_time(expiry)
1008
+ @preserve_expiry = preserve_expiry
974
1009
  @durability_level = durability_level
975
1010
  yield self if block_given?
976
1011
  end
@@ -989,6 +1024,7 @@ module Couchbase
989
1024
  delta: @delta,
990
1025
  initial_value: @initial,
991
1026
  expiry: @expiry,
1027
+ preserve_expiry: @preserve_expiry,
992
1028
  durability_level: @durability_level,
993
1029
  }
994
1030
  end
@@ -134,7 +134,7 @@ module Couchbase
134
134
  #
135
135
  # @return [MutateInSpec]
136
136
  def self.remove(path)
137
- new(:remove, path, nil)
137
+ new(path.empty? ? :remove_doc : :remove, path, nil)
138
138
  end
139
139
 
140
140
  # Creates a command with the intention of upserting a value in a JSON object.
@@ -17,21 +17,21 @@ module Couchbase
17
17
  #
18
18
  # @example Display version and all dependencies in command line
19
19
  # # ruby -rcouchbase -e 'pp Couchbase::VERSION'
20
- # {:sdk=>"3.0.1",
21
- # :backend=>"1.1.0",
22
- # :build_timestamp=>"2020-10-05 11:19:50",
23
- # :revision=>"b59cb40f11ec2dba992eda285eae5cd7238a59c3",
20
+ # {:sdk=>"3.1.0",
21
+ # :backend=>"1.4.0",
22
+ # :build_timestamp=>"2021-03-24 11:25:34",
23
+ # :revision=>"7ba4f7d8b5b0b59b9971ad765876413be3064adb",
24
24
  # :platform=>"Linux-4.15.0-66-generic",
25
25
  # :cpu=>"x86_64",
26
26
  # :cc=>"GNU 9.3.1",
27
27
  # :cxx=>"GNU 9.3.1",
28
- # :ruby=>"2.7.0",
28
+ # :ruby=>"3.0.0",
29
29
  # :spdlog=>"1.8.1",
30
30
  # :asio=>"1.18.0",
31
31
  # :snappy=>"1.1.8",
32
32
  # :http_parser=>"2.9.4",
33
- # :openssl_headers=>"OpenSSL 1.1.1c FIPS 28 May 2019",
34
- # :openssl_runtime=>"OpenSSL 1.1.1c FIPS 28 May 2019"}
33
+ # :openssl_headers=>"OpenSSL 1.1.1g FIPS 21 Apr 2020",
34
+ # :openssl_runtime=>"OpenSSL 1.1.1g FIPS 21 Apr 2020"}
35
35
  VERSION = {} unless defined?(VERSION) # rubocop:disable Style/MutableConstant
36
- VERSION.update(:sdk => "3.1.0".freeze)
36
+ VERSION.update(:sdk => "3.1.1".freeze)
37
37
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: couchbase
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergey Avseyev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-03-24 00:00:00.000000000 Z
11
+ date: 2021-04-08 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Modern SDK for Couchbase Server
14
14
  email:
@@ -1375,9 +1375,9 @@ metadata:
1375
1375
  homepage_uri: https://docs.couchbase.com/ruby-sdk/3.0/hello-world/start-using-sdk.html
1376
1376
  bug_tracker_uri: https://couchbase.com/issues/browse/RCBC
1377
1377
  mailing_list_uri: https://forums.couchbase.com/c/ruby-sdk
1378
- source_code_uri: https://github.com/couchbase/couchbase-ruby-client/tree/3.1.0
1379
- changelog_uri: https://github.com/couchbase/couchbase-ruby-client/releases/tag/3.1.0
1380
- documentation_uri: https://docs.couchbase.com/sdk-api/couchbase-ruby-client-3.1.0/index.html
1378
+ source_code_uri: https://github.com/couchbase/couchbase-ruby-client/tree/3.1.1
1379
+ changelog_uri: https://github.com/couchbase/couchbase-ruby-client/releases/tag/3.1.1
1380
+ documentation_uri: https://docs.couchbase.com/sdk-api/couchbase-ruby-client-3.1.1/index.html
1381
1381
  github_repo: ssh://github.com/couchbase/couchbase-ruby-client
1382
1382
  post_install_message:
1383
1383
  rdoc_options: