couchbase 3.1.0 → 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
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: