couchbase 4.2.10 → 4.2.11-rc.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.
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