couchbase 4.2.10 → 4.2.11-rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. package/deps/couchbase-cxx-client/core/bucket.cxx +23 -8
  2. package/deps/couchbase-cxx-client/core/bucket.hxx +1 -0
  3. package/deps/couchbase-cxx-client/core/cluster.cxx +29 -3
  4. package/deps/couchbase-cxx-client/core/impl/collection.cxx +2 -2
  5. package/deps/couchbase-cxx-client/core/impl/query_index_manager.cxx +6 -6
  6. package/deps/couchbase-cxx-client/core/io/http_session_manager.hxx +7 -1
  7. package/deps/couchbase-cxx-client/core/io/mcbp_command.hxx +10 -0
  8. package/deps/couchbase-cxx-client/core/io/mcbp_session.cxx +24 -1
  9. package/deps/couchbase-cxx-client/core/io/mcbp_session.hxx +1 -0
  10. package/deps/couchbase-cxx-client/core/management/design_document.hxx +1 -1
  11. package/deps/couchbase-cxx-client/core/meta/features.hxx +16 -1
  12. package/deps/couchbase-cxx-client/core/operations/document_lookup_in_all_replicas.hxx +1 -1
  13. package/deps/couchbase-cxx-client/core/operations/document_lookup_in_any_replica.hxx +1 -1
  14. package/deps/couchbase-cxx-client/core/operations/document_query.cxx +2 -2
  15. package/deps/couchbase-cxx-client/core/operations/document_search.cxx +10 -10
  16. package/deps/couchbase-cxx-client/core/operations/document_view.cxx +3 -0
  17. package/deps/couchbase-cxx-client/core/operations/document_view.hxx +1 -0
  18. package/deps/couchbase-cxx-client/core/operations/management/query_index_create.cxx +24 -13
  19. package/deps/couchbase-cxx-client/core/operations/management/query_index_create.hxx +1 -1
  20. package/deps/couchbase-cxx-client/core/origin.cxx +14 -0
  21. package/deps/couchbase-cxx-client/core/origin.hxx +6 -0
  22. package/deps/couchbase-cxx-client/core/protocol/status.cxx +2 -0
  23. package/deps/couchbase-cxx-client/core/protocol/status.hxx +1 -0
  24. package/deps/couchbase-cxx-client/core/topology/capabilities.hxx +70 -1
  25. package/deps/couchbase-cxx-client/core/topology/capabilities_fmt.hxx +30 -2
  26. package/deps/couchbase-cxx-client/core/topology/configuration.hxx +1 -34
  27. package/deps/couchbase-cxx-client/core/topology/configuration_fmt.hxx +2 -2
  28. package/deps/couchbase-cxx-client/core/topology/configuration_json.hxx +43 -20
  29. package/deps/couchbase-cxx-client/core/transactions/internal/exceptions_internal.hxx +5 -0
  30. package/deps/couchbase-cxx-client/couchbase/collection_query_index_manager.hxx +80 -11
  31. package/deps/couchbase-cxx-client/couchbase/fmt/key_value_status_code.hxx +3 -1
  32. package/deps/couchbase-cxx-client/couchbase/key_value_status_code.hxx +1 -0
  33. package/deps/couchbase-cxx-client/couchbase/query_index_manager.hxx +6 -8
  34. package/dist/binding.d.ts +24 -2
  35. package/dist/bindingutilities.d.ts +11 -3
  36. package/dist/bindingutilities.js +33 -7
  37. package/dist/couchbase.d.ts +1 -0
  38. package/dist/couchbase.js +1 -0
  39. package/dist/queryindexmanager.d.ts +4 -4
  40. package/dist/queryindexmanager.js +7 -7
  41. package/dist/scope.d.ts +21 -0
  42. package/dist/scope.js +34 -0
  43. package/dist/scopesearchindexmanager.d.ts +116 -0
  44. package/dist/scopesearchindexmanager.js +406 -0
  45. package/dist/sdspecs.js +10 -9
  46. package/dist/sdutils.d.ts +1 -0
  47. package/dist/sdutils.js +4 -0
  48. package/dist/searchexecutor.d.ts +3 -1
  49. package/dist/searchexecutor.js +9 -2
  50. package/dist/searchindexmanager.d.ts +58 -3
  51. package/dist/searchindexmanager.js +188 -104
  52. package/dist/viewexecutor.js +13 -9
  53. package/dist/viewindexmanager.d.ts +70 -7
  54. package/dist/viewindexmanager.js +236 -103
  55. package/dist/viewtypes.d.ts +26 -0
  56. package/dist/viewtypes.js +17 -1
  57. package/package.json +7 -7
  58. package/src/constants.cpp +1 -0
  59. package/src/jstocbpp_autogen.hpp +89 -6
@@ -426,7 +426,7 @@ class bucket_impl
426
426
  }
427
427
  self->update_config(cfg);
428
428
  self->drain_deferred_queue();
429
- self->fetch_config({});
429
+ self->poll_config({});
430
430
  }
431
431
  asio::post(asio::bind_executor(self->ctx_, [h = std::move(h), ec, cfg = std::move(cfg)]() mutable { h(ec, cfg); }));
432
432
  });
@@ -482,15 +482,11 @@ class bucket_impl
482
482
  }
483
483
  }
484
484
 
485
- void fetch_config(std::error_code ec)
485
+ void fetch_config()
486
486
  {
487
- if (ec == asio::error::operation_aborted || closed_) {
488
- return;
489
- }
490
- if (heartbeat_timer_.expiry() > std::chrono::steady_clock::now()) {
487
+ if (closed_) {
491
488
  return;
492
489
  }
493
-
494
490
  std::optional<io::mcbp_session> session{};
495
491
  {
496
492
  std::scoped_lock lock(sessions_mutex_);
@@ -512,12 +508,25 @@ class bucket_impl
512
508
  } else {
513
509
  CB_LOG_WARNING(R"({} unable to find session with GCCCP support, retry in {})", log_prefix_, heartbeat_interval_);
514
510
  }
511
+ }
512
+
513
+ void poll_config(std::error_code ec)
514
+ {
515
+ if (ec == asio::error::operation_aborted || closed_) {
516
+ return;
517
+ }
518
+ if (heartbeat_timer_.expiry() > std::chrono::steady_clock::now()) {
519
+ return;
520
+ }
521
+
522
+ fetch_config();
523
+
515
524
  heartbeat_timer_.expires_after(heartbeat_interval_);
516
525
  return heartbeat_timer_.async_wait([self = shared_from_this()](std::error_code e) {
517
526
  if (e == asio::error::operation_aborted) {
518
527
  return;
519
528
  }
520
- self->fetch_config(e);
529
+ self->poll_config(e);
521
530
  });
522
531
  }
523
532
 
@@ -880,6 +889,12 @@ bucket::ping(std::shared_ptr<diag::ping_collector> collector, std::optional<std:
880
889
  return impl_->ping(std::move(collector), std::move(timeout));
881
890
  }
882
891
 
892
+ void
893
+ bucket::fetch_config()
894
+ {
895
+ return impl_->fetch_config();
896
+ }
897
+
883
898
  void
884
899
  bucket::update_config(topology::configuration config)
885
900
  {
@@ -177,6 +177,7 @@ class bucket
177
177
  });
178
178
  }
179
179
 
180
+ void fetch_config();
180
181
  void update_config(topology::configuration config) override;
181
182
  void bootstrap(utils::movable_function<void(std::error_code, topology::configuration)>&& handler);
182
183
  void with_configuration(utils::movable_function<void(std::error_code, topology::configuration)>&& handler);
@@ -114,6 +114,26 @@ class ping_collector_impl
114
114
  }
115
115
  };
116
116
 
117
+ template<typename Request>
118
+ constexpr bool
119
+ is_feature_supported(const Request& /* request */, const configuration_capabilities& /* capabilities */)
120
+ {
121
+ return true;
122
+ }
123
+
124
+ constexpr bool
125
+ is_feature_supported(const operations::search_request& request, const configuration_capabilities& capabilities)
126
+ {
127
+ if (request.scope_name && !capabilities.supports_scoped_search_indexes()) {
128
+ return false;
129
+ }
130
+ if (request.vector_search && !capabilities.supports_vector_search()) {
131
+ return false;
132
+ }
133
+
134
+ return true;
135
+ }
136
+
117
137
  class cluster_impl : public std::enable_shared_from_this<cluster_impl>
118
138
  {
119
139
  public:
@@ -238,10 +258,14 @@ class cluster_impl : public std::enable_shared_from_this<cluster_impl>
238
258
  auto ptr = buckets_.find(bucket_name);
239
259
  if (ptr == buckets_.end()) {
240
260
  std::vector<protocol::hello_feature> known_features;
261
+
262
+ auto origin = origin_;
241
263
  if (session_ && session_->has_config()) {
242
264
  known_features = session_->supported_features();
265
+ origin = { origin_, session_->config().value() };
243
266
  }
244
- b = std::make_shared<bucket>(id_, ctx_, tls_, tracer_, meter_, bucket_name, origin_, known_features, dns_srv_tracker_);
267
+
268
+ b = std::make_shared<bucket>(id_, ctx_, tls_, tracer_, meter_, bucket_name, origin, known_features, dns_srv_tracker_);
245
269
  buckets_.try_emplace(bucket_name, b);
246
270
  }
247
271
  }
@@ -326,6 +350,9 @@ class cluster_impl : public std::enable_shared_from_this<cluster_impl>
326
350
  if (stopped_) {
327
351
  return handler(request.make_response({ errc::network::cluster_closed }, response_type{}));
328
352
  }
353
+ if (!is_feature_supported(request, session_manager_->configuration_capabilities())) {
354
+ return handler(request.make_response({ errc::common::feature_not_available }, response_type{}));
355
+ }
329
356
  if constexpr (operations::is_compound_operation_v<Request>) {
330
357
  return request.execute(shared_from_this(), std::forward<Handler>(handler));
331
358
  } else {
@@ -355,8 +382,7 @@ class cluster_impl : public std::enable_shared_from_this<cluster_impl>
355
382
  handler(request.make_response({ ec }, {}));
356
383
  return;
357
384
  }
358
- auto bucket_caps = config.bucket_capabilities;
359
- if (bucket_caps.find(cap) == bucket_caps.end()) {
385
+ if (!config.capabilities.has_bucket_capability(cap)) {
360
386
  handler(request.make_response({ errc::common::feature_not_available }, {}));
361
387
  return;
362
388
  }
@@ -479,7 +479,7 @@ class collection_impl : public std::enable_shared_from_this<collection_impl>
479
479
  bucket_name_,
480
480
  [core = core_, r = std::move(request), h = std::move(handler)](std::error_code ec,
481
481
  const core::topology::configuration& config) mutable {
482
- if (!config.supports_subdoc_read_replica()) {
482
+ if (!config.capabilities.supports_subdoc_read_replica()) {
483
483
  ec = errc::common::feature_not_available;
484
484
  }
485
485
 
@@ -601,7 +601,7 @@ class collection_impl : public std::enable_shared_from_this<collection_impl>
601
601
  bucket_name_,
602
602
  [core = core_, r = std::move(request), h = std::move(handler)](std::error_code ec,
603
603
  const core::topology::configuration& config) mutable {
604
- if (!config.supports_subdoc_read_replica()) {
604
+ if (!config.capabilities.supports_subdoc_read_replica()) {
605
605
  ec = errc::common::feature_not_available;
606
606
  }
607
607
  if (r->specs().size() > 16) {
@@ -215,7 +215,7 @@ class query_index_manager_impl : public std::enable_shared_from_this<query_index
215
215
  const std::string& scope_name,
216
216
  const std::string& collection_name,
217
217
  std::string index_name,
218
- std::vector<std::string> fields,
218
+ std::vector<std::string> keys,
219
219
  const create_query_index_options::built& options,
220
220
  create_query_index_handler&& handler) const
221
221
  {
@@ -225,7 +225,7 @@ class query_index_manager_impl : public std::enable_shared_from_this<query_index
225
225
  scope_name,
226
226
  collection_name,
227
227
  std::move(index_name),
228
- std::move(fields),
228
+ std::move(keys),
229
229
  {},
230
230
  false /* is_primary */,
231
231
  options.ignore_if_exists,
@@ -521,22 +521,22 @@ collection_query_index_manager::get_all_indexes(const get_all_query_indexes_opti
521
521
 
522
522
  void
523
523
  collection_query_index_manager::create_index(std::string index_name,
524
- std::vector<std::string> fields,
524
+ std::vector<std::string> keys,
525
525
  const create_query_index_options& options,
526
526
  create_query_index_handler&& handler) const
527
527
  {
528
528
  return impl_->create_index(
529
- bucket_name_, scope_name_, collection_name_, std::move(index_name), std::move(fields), options.build(), std::move(handler));
529
+ bucket_name_, scope_name_, collection_name_, std::move(index_name), std::move(keys), options.build(), std::move(handler));
530
530
  }
531
531
 
532
532
  auto
533
533
  collection_query_index_manager::create_index(std::string index_name,
534
- std::vector<std::string> fields,
534
+ std::vector<std::string> keys,
535
535
  const create_query_index_options& options) const -> std::future<manager_error_context>
536
536
  {
537
537
  auto barrier = std::make_shared<std::promise<manager_error_context>>();
538
538
  auto future = barrier->get_future();
539
- create_index(std::move(index_name), std::move(fields), options, [barrier](auto ctx) { barrier->set_value(std::move(ctx)); });
539
+ create_index(std::move(index_name), std::move(keys), options, [barrier](auto ctx) { barrier->set_value(std::move(ctx)); });
540
540
  return future;
541
541
  }
542
542
 
@@ -58,6 +58,12 @@ class http_session_manager
58
58
  meter_ = std::move(meter);
59
59
  }
60
60
 
61
+ auto configuration_capabilities() const -> configuration_capabilities
62
+ {
63
+ std::scoped_lock config_lock(config_mutex_);
64
+ return config_.capabilities;
65
+ }
66
+
61
67
  void update_config(topology::configuration config) override
62
68
  {
63
69
  std::scoped_lock config_lock(config_mutex_, sessions_mutex_);
@@ -387,7 +393,7 @@ class http_session_manager
387
393
  cluster_options options_{};
388
394
 
389
395
  topology::configuration config_{};
390
- std::mutex config_mutex_{};
396
+ mutable std::mutex config_mutex_{};
391
397
  std::map<service_type, std::list<std::shared_ptr<http_session>>> busy_sessions_{};
392
398
  std::map<service_type, std::list<std::shared_ptr<http_session>>> idle_sessions_{};
393
399
  std::size_t next_index_{ 0 };
@@ -289,6 +289,16 @@ struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Manager,
289
289
  if (status == key_value_status_code::unknown_collection) {
290
290
  return self->handle_unknown_collection();
291
291
  }
292
+ if (status == key_value_status_code::config_only) {
293
+ CB_LOG_DEBUG(
294
+ "{} server returned status 0x{:02x} ({}) meaning that the node does not serve data operations, requesting new "
295
+ "configuration and retrying",
296
+ self->session_->log_prefix(),
297
+ msg.header.status(),
298
+ status);
299
+ self->manager_->fetch_config();
300
+ return io::retry_orchestrator::maybe_retry(self->manager_, self, retry_reason::service_response_code_indicated, ec);
301
+ }
292
302
  if (error_code && error_code.value().has_retry_attribute()) {
293
303
  reason = retry_reason::key_value_error_map_retry_indicated;
294
304
  } else {
@@ -455,6 +455,16 @@ class mcbp_session_impl
455
455
  case protocol::client_opcode::get_cluster_config: {
456
456
  protocol::cmd_info info{ session_->connection_endpoints_.remote_address,
457
457
  session_->connection_endpoints_.remote.port() };
458
+ if (session_->origin_.options().dump_configuration) {
459
+ std::string_view config_text{ reinterpret_cast<const char*>(msg.body.data()), msg.body.size() };
460
+ CB_LOG_TRACE(
461
+ "{} configuration from get_cluster_config request (bootstrap, size={}, endpoint=\"{}:{}\"), {}",
462
+ session_->log_prefix_,
463
+ config_text.size(),
464
+ info.endpoint_address,
465
+ info.endpoint_port,
466
+ config_text);
467
+ }
458
468
  protocol::client_response<protocol::get_cluster_config_response_body> resp(std::move(msg), info);
459
469
  if (resp.status() == key_value_status_code::success) {
460
470
  session_->update_configuration(resp.body().config());
@@ -860,6 +870,8 @@ class mcbp_session_impl
860
870
  if (stopped_) {
861
871
  return;
862
872
  }
873
+ bootstrapped_ = false;
874
+ bootstrap_handler_ = nullptr;
863
875
  state_ = diag::endpoint_state::connecting;
864
876
  if (stream_->is_open()) {
865
877
  std::string old_id = stream_->id();
@@ -1139,6 +1151,11 @@ class mcbp_session_impl
1139
1151
  return supports_gcccp_;
1140
1152
  }
1141
1153
 
1154
+ std::optional<topology::configuration> config() const
1155
+ {
1156
+ return config_;
1157
+ }
1158
+
1142
1159
  [[nodiscard]] bool has_config() const
1143
1160
  {
1144
1161
  return configured_;
@@ -1539,7 +1556,7 @@ class mcbp_session_impl
1539
1556
  CB_LOG_TRACE("{} MCBP recv {}", self->log_prefix_, mcbp_header_view(msg.header_data()));
1540
1557
  if (self->bootstrapped_) {
1541
1558
  self->handler_->handle(std::move(msg));
1542
- } else {
1559
+ } else if (self->bootstrap_handler_) {
1543
1560
  self->bootstrap_handler_->handle(std::move(msg));
1544
1561
  }
1545
1562
  if (self->stopped_) {
@@ -1818,6 +1835,12 @@ mcbp_session::index() const
1818
1835
  return impl_->index();
1819
1836
  }
1820
1837
 
1838
+ std::optional<topology::configuration>
1839
+ mcbp_session::config() const
1840
+ {
1841
+ return impl_->config();
1842
+ }
1843
+
1821
1844
  bool
1822
1845
  mcbp_session::has_config() const
1823
1846
  {
@@ -118,6 +118,7 @@ class mcbp_session
118
118
  void stop(retry_reason reason);
119
119
  [[nodiscard]] std::size_t index() const;
120
120
  [[nodiscard]] bool has_config() const;
121
+ [[nodiscard]] std::optional<topology::configuration> config() const;
121
122
  [[nodiscard]] diag::endpoint_diag_info diag_info() const;
122
123
  void on_configuration_update(std::shared_ptr<config_listener> handler);
123
124
  void ping(std::shared_ptr<diag::ping_reporter> handler, std::optional<std::chrono::milliseconds> = {}) const;
@@ -32,7 +32,7 @@ struct design_document {
32
32
  std::optional<std::string> reduce{};
33
33
  };
34
34
 
35
- std::string rev;
35
+ std::optional<std::string> rev;
36
36
  std::string name;
37
37
  design_document_namespace ns;
38
38
  std::map<std::string, view> views;
@@ -72,7 +72,7 @@
72
72
  */
73
73
  #define COUCHBASE_CXX_CLIENT_HAS_COLLECTION_QUERY_INDEX_MANAGEMENT 1
74
74
 
75
- #define COUCHBASE_CXX_CLIENT_TRANSACTIONS_EXT_PARALLEL_UNSTAGING
75
+ #define COUCHBASE_CXX_CLIENT_TRANSACTIONS_EXT_PARALLEL_UNSTAGING 1
76
76
 
77
77
  /**
78
78
  * core cluster implementation has been hidden and not accessible through the public API
@@ -111,3 +111,18 @@
111
111
  * Scope level search index management is supported via couchbase::scope::search_indexes()
112
112
  */
113
113
  #define COUCHBASE_CXX_CLIENT_HAS_SCOPE_SEARCH_INDEX_MANAGEMENT 1
114
+
115
+ /**
116
+ * Support for couchbase::codec::raw_json_transcoder
117
+ */
118
+ #define COUCHBASE_CXX_CLIENT_HAS_RAW_JSON_TRANSCODER 1
119
+
120
+ /**
121
+ * Support for couchbase::codec::raw_string_transcoder
122
+ */
123
+ #define COUCHBASE_CXX_CLIENT_HAS_RAW_STRING_TRANSCODER 1
124
+
125
+ /**
126
+ * Transaction's transaction_operation_failed has a public getter for its final_error
127
+ */
128
+ #define COUCHBASE_CXX_CLIENT_TRANSACTIONS_CAN_FETCH_TO_RAISE 1
@@ -69,7 +69,7 @@ struct lookup_in_all_replicas_request {
69
69
  id.bucket(),
70
70
  [core, id = id, timeout = timeout, specs = specs, parent_span = parent_span, h = std::forward<Handler>(handler)](
71
71
  std::error_code ec, const topology::configuration& config) mutable {
72
- if (!config.supports_subdoc_read_replica()) {
72
+ if (!config.capabilities.supports_subdoc_read_replica()) {
73
73
  ec = errc::common::feature_not_available;
74
74
  }
75
75
 
@@ -66,7 +66,7 @@ struct lookup_in_any_replica_request {
66
66
  id.bucket(),
67
67
  [core, id = id, timeout = timeout, specs = specs, parent_span = parent_span, h = std::forward<Handler>(handler)](
68
68
  std::error_code ec, const topology::configuration& config) mutable {
69
- if (!config.supports_subdoc_read_replica()) {
69
+ if (!config.capabilities.supports_subdoc_read_replica()) {
70
70
  ec = errc::common::feature_not_available;
71
71
  }
72
72
  if (specs.size() > 16) {
@@ -47,7 +47,7 @@ query_request::encode_to(query_request::encoded_request_type& encoded, http_cont
47
47
  }
48
48
  } else {
49
49
  body["statement"] = "PREPARE " + statement;
50
- if (context.config.supports_enhanced_prepared_statements()) {
50
+ if (context.config.capabilities.supports_enhanced_prepared_statements()) {
51
51
  body["auto_execute"] = true;
52
52
  } else {
53
53
  extract_encoded_plan_ = true;
@@ -87,7 +87,7 @@ query_request::encode_to(query_request::encoded_request_type& encoded, http_cont
87
87
  break;
88
88
  }
89
89
  if (use_replica.has_value()) {
90
- if (context.config.supports_read_from_replica()) {
90
+ if (context.config.capabilities.supports_read_from_replica()) {
91
91
  if (use_replica.value()) {
92
92
  body["use_replica"] = "on";
93
93
  } else {
@@ -36,20 +36,20 @@ search_request::encode_to(search_request::encoded_request_type& encoded, http_co
36
36
  };
37
37
 
38
38
  if (show_request.has_value()) {
39
- body["showrequest"] = show_request.value() ? "true" : "false";
39
+ body["showrequest"] = show_request.value();
40
40
  }
41
41
 
42
42
  if (vector_search.has_value()) {
43
43
  body["knn"] = utils::json::parse(vector_search.value());
44
- }
45
- if (vector_query_combination.has_value()) {
46
- switch (*vector_query_combination) {
47
- case couchbase::core::vector_query_combination::combination_or:
48
- body["knn_operator"] = "or";
49
- break;
50
- case couchbase::core::vector_query_combination::combination_and:
51
- body["knn_operator"] = "and";
52
- break;
44
+ if (vector_query_combination.has_value()) {
45
+ switch (*vector_query_combination) {
46
+ case couchbase::core::vector_query_combination::combination_or:
47
+ body["knn_operator"] = "or";
48
+ break;
49
+ case couchbase::core::vector_query_combination::combination_and:
50
+ body["knn_operator"] = "and";
51
+ break;
52
+ }
53
53
  }
54
54
  }
55
55
 
@@ -77,6 +77,9 @@ document_view_request::encode_to(document_view_request::encoded_request_type& en
77
77
  if (group_level) {
78
78
  query_string.emplace_back(fmt::format("group_level={}", *group_level));
79
79
  }
80
+ if (full_set) {
81
+ query_string.emplace_back(fmt::format("full_set={}", full_set.value() ? "true" : "false"));
82
+ }
80
83
  if (order) {
81
84
  switch (*order) {
82
85
  case couchbase::core::view_sort_order::descending:
@@ -86,6 +86,7 @@ struct document_view_request {
86
86
  std::optional<std::uint32_t> group_level;
87
87
  bool debug{ false };
88
88
  std::map<std::string, std::string> raw{};
89
+ std::optional<bool> full_set;
89
90
 
90
91
  std::optional<couchbase::core::view_sort_order> order;
91
92
  std::optional<couchbase::core::view_on_error> on_error;
@@ -17,7 +17,6 @@
17
17
 
18
18
  #include "query_index_create.hxx"
19
19
 
20
- #include "core/utils/join_strings.hxx"
21
20
  #include "core/utils/json.hxx"
22
21
  #include "core/utils/keyspace.hxx"
23
22
  #include "error_utils.hxx"
@@ -49,19 +48,31 @@ query_index_create_request::encode_to(encoded_request_type& encoded, http_contex
49
48
  if (with) {
50
49
  with_clause = fmt::format("WITH {}", utils::json::generate(with));
51
50
  }
51
+ std::string encoded_keys{};
52
+ for (std::size_t i = 0; i < keys.size(); i++) {
53
+ if (i != 0) {
54
+ encoded_keys += ", ";
55
+ }
56
+ auto key = keys.at(i);
57
+
58
+ // Add backticks around the key unless they are already present
59
+ if (key.at(0) == '`' && key.at(key.size() - 1) == '`') {
60
+ encoded_keys += key;
61
+ } else {
62
+ encoded_keys += fmt::format("`{}`", key);
63
+ }
64
+ }
52
65
  std::string keyspace = utils::build_keyspace(*this);
53
- tao::json::value body{ { "statement",
54
- is_primary ? fmt::format(R"(CREATE PRIMARY INDEX {} ON {} USING GSI {})",
55
- index_name.empty() ? "" : fmt::format("`{}`", index_name),
56
- keyspace,
57
- with_clause)
58
- : fmt::format(R"(CREATE INDEX `{}` ON {}({}) {} USING GSI {})",
59
- index_name,
60
- keyspace,
61
- utils::join_strings(fields, ", "),
62
- where_clause,
63
- with_clause) },
64
- { "client_context_id", encoded.client_context_id } };
66
+ tao::json::value body{
67
+ { "statement",
68
+ is_primary ? fmt::format(R"(CREATE PRIMARY INDEX {} ON {} USING GSI {})",
69
+ index_name.empty() ? "" : fmt::format("`{}`", index_name),
70
+ keyspace,
71
+ with_clause)
72
+ : fmt::format(
73
+ R"(CREATE INDEX `{}` ON {}({}) {} USING GSI {})", index_name, keyspace, encoded_keys, where_clause, with_clause) },
74
+ { "client_context_id", encoded.client_context_id }
75
+ };
65
76
  if (query_ctx.has_value()) {
66
77
  body["query_context"] = query_ctx.value();
67
78
  }
@@ -49,7 +49,7 @@ struct query_index_create_request {
49
49
  std::string scope_name;
50
50
  std::string collection_name;
51
51
  std::string index_name{};
52
- std::vector<std::string> fields;
52
+ std::vector<std::string> keys;
53
53
  query_context query_ctx;
54
54
  bool is_primary{ false };
55
55
  bool ignore_if_exists{ false };
@@ -18,6 +18,7 @@
18
18
  #include "origin.hxx"
19
19
 
20
20
  #include "core/utils/connection_string.hxx"
21
+ #include "topology/configuration.hxx"
21
22
 
22
23
  #include <fmt/chrono.h>
23
24
  #include <fmt/core.h>
@@ -281,6 +282,19 @@ couchbase::core::origin::origin(const couchbase::core::origin& other)
281
282
  , next_node_(nodes_.begin())
282
283
  {
283
284
  }
285
+
286
+ couchbase::core::origin::origin(const origin& other, const topology::configuration& config)
287
+ : origin(other)
288
+ {
289
+ nodes_.clear();
290
+ for (const auto& node : config.nodes) {
291
+ if (auto port = options_.enable_tls ? node.services_tls.key_value : node.services_plain.key_value; port.has_value()) {
292
+ nodes_.emplace_back(node.hostname, std::to_string(port.value()));
293
+ }
294
+ }
295
+ next_node_ = nodes_.begin();
296
+ }
297
+
284
298
  couchbase::core::origin::origin(couchbase::core::cluster_credentials auth,
285
299
  const std::string& hostname,
286
300
  std::uint16_t port,
@@ -40,6 +40,11 @@ struct cluster_credentials {
40
40
  [[nodiscard]] bool uses_certificate() const;
41
41
  };
42
42
 
43
+ namespace topology
44
+ {
45
+ struct configuration;
46
+ }
47
+
43
48
  struct origin {
44
49
  using node_entry = std::pair<std::string, std::string>;
45
50
  using node_list = std::vector<node_entry>;
@@ -49,6 +54,7 @@ struct origin {
49
54
 
50
55
  origin(origin&& other) = default;
51
56
  origin(const origin& other);
57
+ origin(const origin& other, const topology::configuration& config);
52
58
  origin(cluster_credentials auth, const std::string& hostname, std::uint16_t port, cluster_options options);
53
59
  origin(cluster_credentials auth, const std::string& hostname, const std::string& port, cluster_options options);
54
60
  origin(cluster_credentials auth, const utils::connection_string& connstr);
@@ -202,6 +202,8 @@ map_status_code(protocol::client_opcode opcode, std::uint16_t status)
202
202
 
203
203
  case key_value_status_code::unknown:
204
204
  break;
205
+ case key_value_status_code::config_only:
206
+ break;
205
207
  }
206
208
  return errc::network::protocol_error;
207
209
  }
@@ -103,6 +103,7 @@ is_valid_status(std::uint16_t code)
103
103
  case key_value_status_code::range_scan_more:
104
104
  case key_value_status_code::range_scan_complete:
105
105
  case key_value_status_code::range_scan_vb_uuid_not_equal:
106
+ case key_value_status_code::config_only:
106
107
  return true;
107
108
  case key_value_status_code::unknown:
108
109
  return false;
@@ -17,6 +17,8 @@
17
17
 
18
18
  #pragma once
19
19
 
20
+ #include <set>
21
+
20
22
  namespace couchbase::core
21
23
  {
22
24
  enum class bucket_capability {
@@ -32,8 +34,15 @@ enum class bucket_capability {
32
34
  durable_write,
33
35
  tombstoned_user_xattrs,
34
36
  range_scan,
35
- replica_read,
36
37
  non_deduped_history,
38
+ subdoc_replace_body_with_xattr,
39
+ subdoc_document_macro_support,
40
+ subdoc_revive_document,
41
+ dcp_ignore_purged_tombstones,
42
+ preserve_expiry,
43
+ query_system_collection,
44
+ mobile_system_collection,
45
+ subdoc_replica_read,
37
46
  };
38
47
 
39
48
  enum class cluster_capability {
@@ -43,5 +52,65 @@ enum class cluster_capability {
43
52
  n1ql_inline_functions,
44
53
  n1ql_enhanced_prepared_statements,
45
54
  n1ql_read_from_replica,
55
+ search_vector_search,
56
+ search_scoped_search_index,
57
+ };
58
+
59
+ struct configuration_capabilities {
60
+ std::set<bucket_capability> bucket{};
61
+ std::set<cluster_capability> cluster{};
62
+
63
+ [[nodiscard]] bool has_cluster_capability(cluster_capability cap) const
64
+ {
65
+ return cluster.find(cap) != cluster.end();
66
+ }
67
+
68
+ [[nodiscard]] bool has_bucket_capability(bucket_capability cap) const
69
+ {
70
+ return bucket.find(cap) != bucket.end();
71
+ }
72
+
73
+ [[nodiscard]] bool supports_enhanced_prepared_statements() const
74
+ {
75
+ return has_cluster_capability(cluster_capability::n1ql_enhanced_prepared_statements);
76
+ }
77
+
78
+ [[nodiscard]] bool supports_read_from_replica() const
79
+ {
80
+ return has_cluster_capability(cluster_capability::n1ql_read_from_replica);
81
+ }
82
+
83
+ [[nodiscard]] bool supports_range_scan() const
84
+ {
85
+ return has_bucket_capability(bucket_capability::range_scan);
86
+ }
87
+
88
+ [[nodiscard]] bool ephemeral() const
89
+ {
90
+ // Use bucket capabilities to identify if couchapi is missing (then its ephemeral). If its null then
91
+ // we are running an old version of couchbase which doesn't have ephemeral buckets at all.
92
+ return has_bucket_capability(bucket_capability::couchapi);
93
+ }
94
+
95
+ [[nodiscard]] bool supports_subdoc_read_replica() const
96
+ {
97
+ return has_bucket_capability(bucket_capability::subdoc_replica_read);
98
+ }
99
+
100
+ [[nodiscard]] bool supports_non_deduped_history() const
101
+ {
102
+ return has_bucket_capability(bucket_capability::non_deduped_history);
103
+ }
104
+
105
+ [[nodiscard]] bool supports_scoped_search_indexes() const
106
+ {
107
+ return has_cluster_capability(cluster_capability::search_scoped_search_index);
108
+ }
109
+
110
+ [[nodiscard]] bool supports_vector_search() const
111
+ {
112
+ return has_cluster_capability(cluster_capability::search_vector_search);
113
+ }
46
114
  };
115
+
47
116
  } // namespace couchbase::core