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.
- package/deps/couchbase-cxx-client/core/bucket.cxx +23 -8
- package/deps/couchbase-cxx-client/core/bucket.hxx +1 -0
- package/deps/couchbase-cxx-client/core/cluster.cxx +29 -3
- package/deps/couchbase-cxx-client/core/impl/collection.cxx +2 -2
- package/deps/couchbase-cxx-client/core/impl/query_index_manager.cxx +6 -6
- package/deps/couchbase-cxx-client/core/io/http_session_manager.hxx +7 -1
- package/deps/couchbase-cxx-client/core/io/mcbp_command.hxx +10 -0
- package/deps/couchbase-cxx-client/core/io/mcbp_session.cxx +24 -1
- package/deps/couchbase-cxx-client/core/io/mcbp_session.hxx +1 -0
- package/deps/couchbase-cxx-client/core/management/design_document.hxx +1 -1
- package/deps/couchbase-cxx-client/core/meta/features.hxx +16 -1
- package/deps/couchbase-cxx-client/core/operations/document_lookup_in_all_replicas.hxx +1 -1
- package/deps/couchbase-cxx-client/core/operations/document_lookup_in_any_replica.hxx +1 -1
- package/deps/couchbase-cxx-client/core/operations/document_query.cxx +2 -2
- package/deps/couchbase-cxx-client/core/operations/document_search.cxx +10 -10
- package/deps/couchbase-cxx-client/core/operations/document_view.cxx +3 -0
- package/deps/couchbase-cxx-client/core/operations/document_view.hxx +1 -0
- package/deps/couchbase-cxx-client/core/operations/management/query_index_create.cxx +24 -13
- package/deps/couchbase-cxx-client/core/operations/management/query_index_create.hxx +1 -1
- package/deps/couchbase-cxx-client/core/origin.cxx +14 -0
- package/deps/couchbase-cxx-client/core/origin.hxx +6 -0
- package/deps/couchbase-cxx-client/core/protocol/status.cxx +2 -0
- package/deps/couchbase-cxx-client/core/protocol/status.hxx +1 -0
- package/deps/couchbase-cxx-client/core/topology/capabilities.hxx +70 -1
- package/deps/couchbase-cxx-client/core/topology/capabilities_fmt.hxx +30 -2
- package/deps/couchbase-cxx-client/core/topology/configuration.hxx +1 -34
- package/deps/couchbase-cxx-client/core/topology/configuration_fmt.hxx +2 -2
- package/deps/couchbase-cxx-client/core/topology/configuration_json.hxx +43 -20
- package/deps/couchbase-cxx-client/core/transactions/internal/exceptions_internal.hxx +5 -0
- package/deps/couchbase-cxx-client/couchbase/collection_query_index_manager.hxx +80 -11
- package/deps/couchbase-cxx-client/couchbase/fmt/key_value_status_code.hxx +3 -1
- package/deps/couchbase-cxx-client/couchbase/key_value_status_code.hxx +1 -0
- package/deps/couchbase-cxx-client/couchbase/query_index_manager.hxx +6 -8
- package/dist/binding.d.ts +24 -2
- package/dist/bindingutilities.d.ts +11 -3
- package/dist/bindingutilities.js +33 -7
- package/dist/couchbase.d.ts +1 -0
- package/dist/couchbase.js +1 -0
- package/dist/queryindexmanager.d.ts +4 -4
- package/dist/queryindexmanager.js +7 -7
- package/dist/scope.d.ts +21 -0
- package/dist/scope.js +34 -0
- package/dist/scopesearchindexmanager.d.ts +116 -0
- package/dist/scopesearchindexmanager.js +406 -0
- package/dist/sdspecs.js +10 -9
- package/dist/sdutils.d.ts +1 -0
- package/dist/sdutils.js +4 -0
- package/dist/searchexecutor.d.ts +3 -1
- package/dist/searchexecutor.js +9 -2
- package/dist/searchindexmanager.d.ts +58 -3
- package/dist/searchindexmanager.js +188 -104
- package/dist/viewexecutor.js +13 -9
- package/dist/viewindexmanager.d.ts +70 -7
- package/dist/viewindexmanager.js +236 -103
- package/dist/viewtypes.d.ts +26 -0
- package/dist/viewtypes.js +17 -1
- package/package.json +7 -7
- package/src/constants.cpp +1 -0
- 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->
|
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(
|
485
|
+
void fetch_config()
|
486
486
|
{
|
487
|
-
if (
|
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->
|
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
|
-
|
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
|
-
|
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>
|
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(
|
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>
|
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(
|
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>
|
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(
|
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;
|
@@ -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()
|
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
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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{
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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>
|
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);
|
@@ -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
|