couchbase 4.2.5-dev.3 → 4.2.6-dev
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +81 -9
- package/deps/couchbase-cxx-client/CMakeLists.txt +9 -1
- package/deps/couchbase-cxx-client/bin/api.rb +234 -0
- package/deps/couchbase-cxx-client/bin/create-search-index +18 -135
- package/deps/couchbase-cxx-client/bin/init-cluster +17 -139
- package/deps/couchbase-cxx-client/bin/load-sample-buckets +54 -0
- package/deps/couchbase-cxx-client/core/cluster.hxx +33 -12
- package/deps/couchbase-cxx-client/core/cluster_options.hxx +3 -0
- package/deps/couchbase-cxx-client/core/crud_component.cxx +51 -22
- package/deps/couchbase-cxx-client/core/impl/build_deferred_query_indexes.cxx +115 -50
- package/deps/couchbase-cxx-client/core/impl/cluster.cxx +6 -0
- package/deps/couchbase-cxx-client/core/impl/create_bucket.cxx +155 -0
- package/deps/couchbase-cxx-client/core/impl/create_query_index.cxx +172 -59
- package/deps/couchbase-cxx-client/core/impl/dns_srv_tracker.cxx +2 -1
- package/deps/couchbase-cxx-client/core/impl/drop_bucket.cxx +66 -0
- package/deps/couchbase-cxx-client/core/impl/drop_query_index.cxx +138 -59
- package/deps/couchbase-cxx-client/core/impl/flush_bucket.cxx +66 -0
- package/deps/couchbase-cxx-client/core/impl/get_all_buckets.cxx +163 -0
- package/deps/couchbase-cxx-client/core/impl/get_all_query_indexes.cxx +67 -37
- package/deps/couchbase-cxx-client/core/impl/get_bucket.cxx +153 -0
- package/deps/couchbase-cxx-client/core/impl/internal_manager_error_context.cxx +113 -0
- package/deps/couchbase-cxx-client/core/impl/internal_manager_error_context.hxx +60 -0
- package/deps/couchbase-cxx-client/core/impl/key_value_error_category.cxx +2 -4
- package/deps/couchbase-cxx-client/core/impl/manager_error_context.cxx +100 -0
- package/deps/couchbase-cxx-client/core/impl/query.cxx +1 -0
- package/deps/couchbase-cxx-client/core/impl/update_bucket.cxx +130 -0
- package/deps/couchbase-cxx-client/core/impl/watch_query_indexes.cxx +53 -29
- package/deps/couchbase-cxx-client/core/io/dns_client.cxx +71 -38
- package/deps/couchbase-cxx-client/core/io/dns_config.cxx +5 -4
- package/deps/couchbase-cxx-client/core/io/mcbp_session.cxx +5 -6
- package/deps/couchbase-cxx-client/core/meta/features.hxx +6 -0
- package/deps/couchbase-cxx-client/core/operations/document_query.cxx +11 -0
- package/deps/couchbase-cxx-client/core/operations/document_query.hxx +1 -0
- package/deps/couchbase-cxx-client/core/origin.cxx +270 -0
- package/deps/couchbase-cxx-client/core/origin.hxx +2 -0
- package/deps/couchbase-cxx-client/core/protocol/status.cxx +2 -2
- package/deps/couchbase-cxx-client/core/range_scan_options.cxx +3 -27
- package/deps/couchbase-cxx-client/core/range_scan_options.hxx +13 -17
- package/deps/couchbase-cxx-client/core/range_scan_orchestrator.cxx +367 -170
- package/deps/couchbase-cxx-client/core/range_scan_orchestrator.hxx +13 -2
- package/deps/couchbase-cxx-client/core/range_scan_orchestrator_options.hxx +5 -3
- package/deps/couchbase-cxx-client/core/scan_options.hxx +0 -19
- package/deps/couchbase-cxx-client/core/scan_result.cxx +19 -5
- package/deps/couchbase-cxx-client/core/scan_result.hxx +5 -2
- package/deps/couchbase-cxx-client/core/timeout_defaults.hxx +2 -3
- package/deps/couchbase-cxx-client/core/topology/capabilities.hxx +1 -0
- package/deps/couchbase-cxx-client/core/topology/capabilities_fmt.hxx +2 -0
- package/deps/couchbase-cxx-client/core/topology/collections_manifest_fmt.hxx +1 -1
- package/deps/couchbase-cxx-client/core/topology/configuration.hxx +5 -0
- package/deps/couchbase-cxx-client/core/topology/configuration_json.hxx +2 -0
- package/deps/couchbase-cxx-client/core/utils/connection_string.cxx +4 -0
- package/deps/couchbase-cxx-client/couchbase/behavior_options.hxx +19 -2
- package/deps/couchbase-cxx-client/couchbase/bucket_manager.hxx +135 -0
- package/deps/couchbase-cxx-client/couchbase/build_query_index_options.hxx +0 -30
- package/deps/couchbase-cxx-client/couchbase/cluster.hxx +14 -0
- package/deps/couchbase-cxx-client/couchbase/collection_query_index_manager.hxx +7 -48
- package/deps/couchbase-cxx-client/couchbase/create_bucket_options.hxx +41 -0
- package/deps/couchbase-cxx-client/couchbase/create_primary_query_index_options.hxx +0 -29
- package/deps/couchbase-cxx-client/couchbase/create_query_index_options.hxx +0 -33
- package/deps/couchbase-cxx-client/couchbase/drop_bucket_options.hxx +41 -0
- package/deps/couchbase-cxx-client/couchbase/drop_primary_query_index_options.hxx +0 -30
- package/deps/couchbase-cxx-client/couchbase/drop_query_index_options.hxx +0 -31
- package/deps/couchbase-cxx-client/couchbase/error_codes.hxx +1 -2
- package/deps/couchbase-cxx-client/couchbase/flush_bucket_options.hxx +41 -0
- package/deps/couchbase-cxx-client/couchbase/get_all_buckets_options.hxx +44 -0
- package/deps/couchbase-cxx-client/couchbase/get_all_query_indexes_options.hxx +0 -30
- package/deps/couchbase-cxx-client/couchbase/get_bucket_options.hxx +43 -0
- package/deps/couchbase-cxx-client/couchbase/management/bucket_settings.hxx +116 -0
- package/deps/couchbase-cxx-client/couchbase/manager_error_context.hxx +29 -53
- package/deps/couchbase-cxx-client/couchbase/query_index_manager.hxx +16 -83
- package/deps/couchbase-cxx-client/couchbase/query_options.hxx +18 -0
- package/deps/couchbase-cxx-client/couchbase/security_options.hxx +15 -0
- package/deps/couchbase-cxx-client/couchbase/update_bucket_options.hxx +41 -0
- package/deps/couchbase-cxx-client/couchbase/watch_query_indexes_options.hxx +0 -31
- package/deps/couchbase-cxx-client/docs/cbc-analytics.md +1 -0
- package/deps/couchbase-cxx-client/docs/cbc-get.md +1 -0
- package/deps/couchbase-cxx-client/docs/cbc-pillowfight.md +1 -0
- package/deps/couchbase-cxx-client/docs/cbc-query.md +1 -0
- package/deps/couchbase-cxx-client/docs/cbc.md +10 -0
- package/deps/couchbase-cxx-client/test/CMakeLists.txt +1 -0
- package/deps/couchbase-cxx-client/test/test_integration_collections.cxx +6 -0
- package/deps/couchbase-cxx-client/test/test_integration_crud.cxx +5 -0
- package/deps/couchbase-cxx-client/test/test_integration_examples.cxx +137 -1
- package/deps/couchbase-cxx-client/test/test_integration_management.cxx +709 -266
- package/deps/couchbase-cxx-client/test/test_integration_query.cxx +19 -7
- package/deps/couchbase-cxx-client/test/test_integration_range_scan.cxx +351 -112
- package/deps/couchbase-cxx-client/test/test_integration_search.cxx +10 -1
- package/deps/couchbase-cxx-client/test/test_transaction_public_async_api.cxx +13 -12
- package/deps/couchbase-cxx-client/test/test_transaction_public_blocking_api.cxx +27 -21
- package/deps/couchbase-cxx-client/test/test_unit_query.cxx +75 -0
- package/deps/couchbase-cxx-client/test/utils/server_version.hxx +5 -0
- package/deps/couchbase-cxx-client/test/utils/wait_until.cxx +29 -10
- package/deps/couchbase-cxx-client/test/utils/wait_until.hxx +3 -1
- package/deps/couchbase-cxx-client/tools/utils.cxx +4 -1
- package/dist/binding.d.ts +21 -16
- package/dist/binding.js +1 -4
- package/dist/bindingutilities.d.ts +6 -1
- package/dist/bindingutilities.js +36 -1
- package/dist/collection.d.ts +65 -3
- package/dist/collection.js +107 -0
- package/dist/crudoptypes.d.ts +34 -0
- package/dist/crudoptypes.js +18 -1
- package/dist/queryexecutor.js +1 -0
- package/dist/querytypes.d.ts +7 -0
- package/dist/rangeScan.d.ts +107 -0
- package/dist/rangeScan.js +91 -0
- package/dist/streamablepromises.d.ts +6 -0
- package/dist/streamablepromises.js +25 -1
- package/package.json +13 -14
- package/scripts/createPlatformPackages.js +1 -4
- package/src/addondata.hpp +1 -0
- package/src/binding.cpp +5 -2
- package/src/connection.cpp +108 -2
- package/src/connection.hpp +1 -0
- package/src/constants.cpp +2 -12
- package/src/jstocbpp_autogen.hpp +49 -22
- package/src/jstocbpp_basic.hpp +2 -8
- package/src/mutationtoken.cpp +13 -0
- package/src/scan_iterator.cpp +90 -0
- package/src/scan_iterator.hpp +30 -0
- package/tools/gen-bindings-json.py +9 -8
- package/deps/couchbase-cxx-client/core/impl/collection_query_index_manager.cxx +0 -93
@@ -53,10 +53,7 @@ try {
|
|
53
53
|
fs.renameSync(oldPath, path.join(newPath, newPrebuildName))
|
54
54
|
const platformPackage = `@${packageName}/${platPkg}`
|
55
55
|
// build the platform package files: package.json, README and index.js
|
56
|
-
const engines = { node:
|
57
|
-
if (runtime === 'napi') {
|
58
|
-
engines.node = nodeVersion >= 18 ? '>=18.0.0' : '<18'
|
59
|
-
}
|
56
|
+
const engines = { node: nodeVersion >= 18 ? '>=18' : '<18' }
|
60
57
|
fs.writeFileSync(
|
61
58
|
path.join(newPath, 'package.json'),
|
62
59
|
JSON.stringify(
|
package/src/addondata.hpp
CHANGED
package/src/binding.cpp
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
#include "connection.hpp"
|
4
4
|
#include "constants.hpp"
|
5
5
|
#include "mutationtoken.hpp"
|
6
|
+
#include "scan_iterator.hpp"
|
6
7
|
#include "transaction.hpp"
|
7
8
|
#include "transactions.hpp"
|
8
9
|
#include <napi.h>
|
@@ -55,11 +56,13 @@ Napi::Object Init(Napi::Env env, Napi::Object exports)
|
|
55
56
|
Connection::Init(env, exports);
|
56
57
|
Transactions::Init(env, exports);
|
57
58
|
Transaction::Init(env, exports);
|
59
|
+
ScanIterator::Init(env, exports);
|
58
60
|
|
59
61
|
exports.Set(Napi::String::New(env, "cbppVersion"),
|
60
62
|
Napi::String::New(env, "1.0.0-beta"));
|
61
|
-
exports.Set(
|
62
|
-
|
63
|
+
exports.Set(
|
64
|
+
Napi::String::New(env, "cbppMetadata"),
|
65
|
+
Napi::String::New(env, couchbase::core::meta::sdk_build_info_json()));
|
63
66
|
return exports;
|
64
67
|
}
|
65
68
|
|
package/src/connection.cpp
CHANGED
@@ -3,8 +3,11 @@
|
|
3
3
|
#include "instance.hpp"
|
4
4
|
#include "jstocbpp.hpp"
|
5
5
|
#include "mutationtoken.hpp"
|
6
|
+
#include "scan_iterator.hpp"
|
6
7
|
#include "transcoder.hpp"
|
8
|
+
#include <core/agent_group.hxx>
|
7
9
|
#include <core/operations/management/freeform.hxx>
|
10
|
+
#include <core/range_scan_orchestrator.hxx>
|
8
11
|
#include <type_traits>
|
9
12
|
|
10
13
|
namespace couchnode
|
@@ -35,6 +38,7 @@ void Connection::Init(Napi::Env env, Napi::Object exports)
|
|
35
38
|
InstanceMethod<&Connection::jsOpenBucket>("openBucket"),
|
36
39
|
InstanceMethod<&Connection::jsDiagnostics>("diagnostics"),
|
37
40
|
InstanceMethod<&Connection::jsPing>("ping"),
|
41
|
+
InstanceMethod<&Connection::jsScan>("scan"),
|
38
42
|
|
39
43
|
//#region Autogenerated Method Registration
|
40
44
|
|
@@ -253,9 +257,30 @@ Napi::Value Connection::jsConnect(const Napi::CallbackInfo &info)
|
|
253
257
|
jsToCbpp<couchbase::core::cluster_credentials>(credentialsJsObj);
|
254
258
|
|
255
259
|
if (!info[2].IsNull()) {
|
256
|
-
auto
|
260
|
+
auto jsDnsConfigObj = info[2].As<Napi::Object>();
|
261
|
+
auto jsNameserver = jsDnsConfigObj.Get("nameserver");
|
262
|
+
if (jsNameserver.IsNull() || jsNameserver.IsUndefined() ||
|
263
|
+
jsNameserver.IsEmpty()) {
|
264
|
+
jsDnsConfigObj.Set(
|
265
|
+
"nameserver",
|
266
|
+
cbpp_to_js<std::string>(
|
267
|
+
info.Env(), connstrInfo.options.dns_config.nameserver()));
|
268
|
+
}
|
269
|
+
auto jsPort = jsDnsConfigObj.Get("port");
|
270
|
+
if (jsPort.IsNull() || jsPort.IsUndefined()) {
|
271
|
+
jsDnsConfigObj.Set(
|
272
|
+
"port", cbpp_to_js<std::uint16_t>(
|
273
|
+
info.Env(), connstrInfo.options.dns_config.port()));
|
274
|
+
}
|
275
|
+
auto jsTimeout = jsDnsConfigObj.Get("dnsSrvTimeout");
|
276
|
+
if (jsTimeout.IsNull() || jsTimeout.IsUndefined()) {
|
277
|
+
jsDnsConfigObj.Set(
|
278
|
+
"dnsSrvTimeout",
|
279
|
+
cbpp_to_js<std::chrono::milliseconds>(
|
280
|
+
info.Env(), connstrInfo.options.dns_config.timeout()));
|
281
|
+
}
|
257
282
|
auto cppDnsConfig =
|
258
|
-
jsToCbpp<couchbase::core::io::dns::dns_config>(
|
283
|
+
jsToCbpp<couchbase::core::io::dns::dns_config>(jsDnsConfigObj);
|
259
284
|
connstrInfo.options.dns_config = cppDnsConfig;
|
260
285
|
}
|
261
286
|
|
@@ -366,4 +391,85 @@ Napi::Value Connection::jsPing(const Napi::CallbackInfo &info)
|
|
366
391
|
return info.Env().Null();
|
367
392
|
}
|
368
393
|
|
394
|
+
Napi::Value Connection::jsScan(const Napi::CallbackInfo &info)
|
395
|
+
{
|
396
|
+
auto bucketName = info[0].ToString().Utf8Value();
|
397
|
+
auto scopeName = info[1].ToString().Utf8Value();
|
398
|
+
auto collectionName = info[2].ToString().Utf8Value();
|
399
|
+
auto scanTypeName = info[3].ToString().Utf8Value();
|
400
|
+
// scanType handled below
|
401
|
+
auto optionsObj = info[5].As<Napi::Object>();
|
402
|
+
|
403
|
+
auto env = info.Env();
|
404
|
+
auto resObj = Napi::Object::New(env);
|
405
|
+
|
406
|
+
auto barrier = std::make_shared<std::promise<
|
407
|
+
tl::expected<couchbase::core::topology::configuration::vbucket_map,
|
408
|
+
std::error_code>>>();
|
409
|
+
auto f = barrier->get_future();
|
410
|
+
this->_instance->_cluster->with_bucket_configuration(
|
411
|
+
bucketName,
|
412
|
+
[barrier](
|
413
|
+
std::error_code ec,
|
414
|
+
const couchbase::core::topology::configuration &config) mutable {
|
415
|
+
if (ec) {
|
416
|
+
return barrier->set_value(tl::unexpected(ec));
|
417
|
+
}
|
418
|
+
if (!config.vbmap || config.vbmap->empty()) {
|
419
|
+
return barrier->set_value(tl::unexpected(
|
420
|
+
couchbase::errc::common::feature_not_available));
|
421
|
+
}
|
422
|
+
barrier->set_value(config.vbmap.value());
|
423
|
+
});
|
424
|
+
auto vbucket_map = f.get();
|
425
|
+
if (!vbucket_map.has_value()) {
|
426
|
+
resObj.Set("cppErr", cbpp_to_js(env, vbucket_map.error()));
|
427
|
+
resObj.Set("result", env.Null());
|
428
|
+
return resObj;
|
429
|
+
}
|
430
|
+
|
431
|
+
auto agentGroup = couchbase::core::agent_group(
|
432
|
+
this->_instance->_io,
|
433
|
+
couchbase::core::agent_group_config{{this->_instance->_cluster}});
|
434
|
+
agentGroup.open_bucket(bucketName);
|
435
|
+
auto agent = agentGroup.get_agent(bucketName);
|
436
|
+
auto options = js_to_cbpp<couchbase::core::range_scan_orchestrator_options>(
|
437
|
+
optionsObj);
|
438
|
+
|
439
|
+
if (!agent.has_value()) {
|
440
|
+
resObj.Set("cppErr", cbpp_to_js(env, agent.error()));
|
441
|
+
resObj.Set("result", env.Null());
|
442
|
+
return resObj;
|
443
|
+
}
|
444
|
+
|
445
|
+
std::variant<std::monostate, couchbase::core::range_scan,
|
446
|
+
couchbase::core::prefix_scan, couchbase::core::sampling_scan>
|
447
|
+
scanType;
|
448
|
+
if (scanTypeName.compare("range_scan") == 0) {
|
449
|
+
scanType = js_to_cbpp<couchbase::core::range_scan>(info[4]);
|
450
|
+
} else if (scanTypeName.compare("sampling_scan") == 0) {
|
451
|
+
scanType = js_to_cbpp<couchbase::core::sampling_scan>(info[4]);
|
452
|
+
} else {
|
453
|
+
scanType = js_to_cbpp<couchbase::core::prefix_scan>(info[4]);
|
454
|
+
}
|
455
|
+
|
456
|
+
auto orchestrator = couchbase::core::range_scan_orchestrator(
|
457
|
+
this->_instance->_io, agent.value(), vbucket_map.value(), scopeName,
|
458
|
+
collectionName, scanType, options);
|
459
|
+
auto scanResult = orchestrator.scan();
|
460
|
+
if (!scanResult.has_value()) {
|
461
|
+
resObj.Set("cppErr", cbpp_to_js(env, scanResult.error()));
|
462
|
+
resObj.Set("result", env.Null());
|
463
|
+
return resObj;
|
464
|
+
}
|
465
|
+
|
466
|
+
auto ext = Napi::External<couchbase::core::scan_result>::New(
|
467
|
+
env, &scanResult.value());
|
468
|
+
auto scanIterator = ScanIterator::constructor(info.Env()).New({ext});
|
469
|
+
resObj.Set("cppErr", env.Null());
|
470
|
+
resObj.Set("result", scanIterator);
|
471
|
+
|
472
|
+
return resObj;
|
473
|
+
}
|
474
|
+
|
369
475
|
} // namespace couchnode
|
package/src/connection.hpp
CHANGED
@@ -69,6 +69,7 @@ public:
|
|
69
69
|
Napi::Value jsOpenBucket(const Napi::CallbackInfo &info);
|
70
70
|
Napi::Value jsDiagnostics(const Napi::CallbackInfo &info);
|
71
71
|
Napi::Value jsPing(const Napi::CallbackInfo &info);
|
72
|
+
Napi::Value jsScan(const Napi::CallbackInfo &info);
|
72
73
|
|
73
74
|
//#region Autogenerated Method Declarations
|
74
75
|
|
package/src/constants.cpp
CHANGED
@@ -701,10 +701,8 @@ void Constants::InitAutogen(Napi::Env env, Napi::Object exports)
|
|
701
701
|
couchbase::errc::key_value::xattr_no_access},
|
702
702
|
{"cannot_revive_living_document",
|
703
703
|
couchbase::errc::key_value::cannot_revive_living_document},
|
704
|
-
{"
|
705
|
-
couchbase::errc::key_value::
|
706
|
-
{"range_scan_vb_uuid_not_equal",
|
707
|
-
couchbase::errc::key_value::range_scan_vb_uuid_not_equal},
|
704
|
+
{"mutation_token_outdated",
|
705
|
+
couchbase::errc::key_value::mutation_token_outdated},
|
708
706
|
{"range_scan_completed",
|
709
707
|
couchbase::errc::key_value::range_scan_completed},
|
710
708
|
}));
|
@@ -1050,14 +1048,6 @@ void Constants::InitAutogen(Napi::Env env, Napi::Object exports)
|
|
1050
1048
|
{"three", couchbase::replicate_to::three},
|
1051
1049
|
}));
|
1052
1050
|
|
1053
|
-
exports.Set(
|
1054
|
-
"scan_sort",
|
1055
|
-
cbppEnumToJs<couchbase::core::scan_sort>(
|
1056
|
-
env, {
|
1057
|
-
{"none", couchbase::core::scan_sort::none},
|
1058
|
-
{"ascending", couchbase::core::scan_sort::ascending},
|
1059
|
-
}));
|
1060
|
-
|
1061
1051
|
//#endregion Autogenerated Constants
|
1062
1052
|
}
|
1063
1053
|
|
package/src/jstocbpp_autogen.hpp
CHANGED
@@ -2418,6 +2418,8 @@ struct js_to_cbpp_t<couchbase::core::operations::query_request> {
|
|
2418
2418
|
js_to_cbpp<bool>(cppObj.readonly, jsObj.Get("readonly"));
|
2419
2419
|
js_to_cbpp<bool>(cppObj.flex_index, jsObj.Get("flex_index"));
|
2420
2420
|
js_to_cbpp<bool>(cppObj.preserve_expiry, jsObj.Get("preserve_expiry"));
|
2421
|
+
js_to_cbpp<std::optional<bool>>(cppObj.use_replica,
|
2422
|
+
jsObj.Get("use_replica"));
|
2421
2423
|
js_to_cbpp<std::optional<std::uint64_t>>(cppObj.max_parallelism,
|
2422
2424
|
jsObj.Get("max_parallelism"));
|
2423
2425
|
js_to_cbpp<std::optional<std::uint64_t>>(cppObj.scan_cap,
|
@@ -2469,6 +2471,8 @@ struct js_to_cbpp_t<couchbase::core::operations::query_request> {
|
|
2469
2471
|
resObj.Set("flex_index", cbpp_to_js<bool>(env, cppObj.flex_index));
|
2470
2472
|
resObj.Set("preserve_expiry",
|
2471
2473
|
cbpp_to_js<bool>(env, cppObj.preserve_expiry));
|
2474
|
+
resObj.Set("use_replica",
|
2475
|
+
cbpp_to_js<std::optional<bool>>(env, cppObj.use_replica));
|
2472
2476
|
resObj.Set("max_parallelism", cbpp_to_js<std::optional<std::uint64_t>>(
|
2473
2477
|
env, cppObj.max_parallelism));
|
2474
2478
|
resObj.Set("scan_cap", cbpp_to_js<std::optional<std::uint64_t>>(
|
@@ -9888,7 +9892,7 @@ struct js_to_cbpp_t<couchbase::core::scan_term> {
|
|
9888
9892
|
{
|
9889
9893
|
auto jsObj = jsVal.ToObject();
|
9890
9894
|
couchbase::core::scan_term cppObj;
|
9891
|
-
js_to_cbpp<std::
|
9895
|
+
js_to_cbpp<std::string>(cppObj.term, jsObj.Get("term"));
|
9892
9896
|
js_to_cbpp<bool>(cppObj.exclusive, jsObj.Get("exclusive"));
|
9893
9897
|
return cppObj;
|
9894
9898
|
}
|
@@ -9896,7 +9900,7 @@ struct js_to_cbpp_t<couchbase::core::scan_term> {
|
|
9896
9900
|
const couchbase::core::scan_term &cppObj)
|
9897
9901
|
{
|
9898
9902
|
auto resObj = Napi::Object::New(env);
|
9899
|
-
resObj.Set("
|
9903
|
+
resObj.Set("term", cbpp_to_js<std::string>(env, cppObj.term));
|
9900
9904
|
resObj.Set("exclusive", cbpp_to_js<bool>(env, cppObj.exclusive));
|
9901
9905
|
return resObj;
|
9902
9906
|
}
|
@@ -9908,19 +9912,39 @@ struct js_to_cbpp_t<couchbase::core::range_scan> {
|
|
9908
9912
|
{
|
9909
9913
|
auto jsObj = jsVal.ToObject();
|
9910
9914
|
couchbase::core::range_scan cppObj;
|
9911
|
-
js_to_cbpp<couchbase::core::scan_term
|
9912
|
-
|
9913
|
-
js_to_cbpp<couchbase::core::scan_term
|
9915
|
+
js_to_cbpp<std::optional<couchbase::core::scan_term>>(
|
9916
|
+
cppObj.from, jsObj.Get("from"));
|
9917
|
+
js_to_cbpp<std::optional<couchbase::core::scan_term>>(cppObj.to,
|
9918
|
+
jsObj.Get("to"));
|
9914
9919
|
return cppObj;
|
9915
9920
|
}
|
9916
9921
|
static inline Napi::Value to_js(Napi::Env env,
|
9917
9922
|
const couchbase::core::range_scan &cppObj)
|
9918
9923
|
{
|
9919
9924
|
auto resObj = Napi::Object::New(env);
|
9920
|
-
resObj.Set("
|
9921
|
-
cbpp_to_js<couchbase::core::scan_term
|
9922
|
-
|
9923
|
-
|
9925
|
+
resObj.Set("from",
|
9926
|
+
cbpp_to_js<std::optional<couchbase::core::scan_term>>(
|
9927
|
+
env, cppObj.from));
|
9928
|
+
resObj.Set("to", cbpp_to_js<std::optional<couchbase::core::scan_term>>(
|
9929
|
+
env, cppObj.to));
|
9930
|
+
return resObj;
|
9931
|
+
}
|
9932
|
+
};
|
9933
|
+
|
9934
|
+
template <>
|
9935
|
+
struct js_to_cbpp_t<couchbase::core::prefix_scan> {
|
9936
|
+
static inline couchbase::core::prefix_scan from_js(Napi::Value jsVal)
|
9937
|
+
{
|
9938
|
+
auto jsObj = jsVal.ToObject();
|
9939
|
+
couchbase::core::prefix_scan cppObj;
|
9940
|
+
js_to_cbpp<std::string>(cppObj.prefix, jsObj.Get("prefix"));
|
9941
|
+
return cppObj;
|
9942
|
+
}
|
9943
|
+
static inline Napi::Value to_js(Napi::Env env,
|
9944
|
+
const couchbase::core::prefix_scan &cppObj)
|
9945
|
+
{
|
9946
|
+
auto resObj = Napi::Object::New(env);
|
9947
|
+
resObj.Set("prefix", cbpp_to_js<std::string>(env, cppObj.prefix));
|
9924
9948
|
return resObj;
|
9925
9949
|
}
|
9926
9950
|
};
|
@@ -9932,7 +9956,7 @@ struct js_to_cbpp_t<couchbase::core::sampling_scan> {
|
|
9932
9956
|
auto jsObj = jsVal.ToObject();
|
9933
9957
|
couchbase::core::sampling_scan cppObj;
|
9934
9958
|
js_to_cbpp<std::size_t>(cppObj.limit, jsObj.Get("limit"));
|
9935
|
-
js_to_cbpp<std::optional<std::
|
9959
|
+
js_to_cbpp<std::optional<std::uint64_t>>(cppObj.seed,
|
9936
9960
|
jsObj.Get("seed"));
|
9937
9961
|
return cppObj;
|
9938
9962
|
}
|
@@ -9942,7 +9966,7 @@ struct js_to_cbpp_t<couchbase::core::sampling_scan> {
|
|
9942
9966
|
auto resObj = Napi::Object::New(env);
|
9943
9967
|
resObj.Set("limit", cbpp_to_js<std::size_t>(env, cppObj.limit));
|
9944
9968
|
resObj.Set("seed",
|
9945
|
-
cbpp_to_js<std::optional<std::
|
9969
|
+
cbpp_to_js<std::optional<std::uint64_t>>(env, cppObj.seed));
|
9946
9970
|
return resObj;
|
9947
9971
|
}
|
9948
9972
|
};
|
@@ -9986,11 +10010,15 @@ struct js_to_cbpp_t<couchbase::core::range_scan_create_options> {
|
|
9986
10010
|
auto scan_type_name =
|
9987
10011
|
jsToCbpp<std::string>(jsObj.Get("scan_type_name"));
|
9988
10012
|
std::variant<std::monostate, couchbase::core::range_scan,
|
10013
|
+
couchbase::core::prefix_scan,
|
9989
10014
|
couchbase::core::sampling_scan>
|
9990
10015
|
scan_type;
|
9991
10016
|
if (scan_type_name.compare("range_scan") == 0) {
|
9992
10017
|
scan_type = js_to_cbpp<couchbase::core::range_scan>(
|
9993
10018
|
jsObj.Get("scan_type_value"));
|
10019
|
+
} else if (scan_type_name.compare("prefix_scan") == 0) {
|
10020
|
+
scan_type = js_to_cbpp<couchbase::core::prefix_scan>(
|
10021
|
+
jsObj.Get("scan_type_value"));
|
9994
10022
|
} else if (scan_type_name.compare("sampling_scan") == 0) {
|
9995
10023
|
scan_type = js_to_cbpp<couchbase::core::sampling_scan>(
|
9996
10024
|
jsObj.Get("scan_type_value"));
|
@@ -10023,6 +10051,7 @@ struct js_to_cbpp_t<couchbase::core::range_scan_create_options> {
|
|
10023
10051
|
resObj.Set(
|
10024
10052
|
"scan_type",
|
10025
10053
|
cbpp_to_js<std::variant<std::monostate, couchbase::core::range_scan,
|
10054
|
+
couchbase::core::prefix_scan,
|
10026
10055
|
couchbase::core::sampling_scan>>(
|
10027
10056
|
env, cppObj.scan_type));
|
10028
10057
|
resObj.Set("timeout",
|
@@ -10076,10 +10105,11 @@ struct js_to_cbpp_t<couchbase::core::range_scan_continue_options> {
|
|
10076
10105
|
jsObj.Get("batch_item_limit"));
|
10077
10106
|
js_to_cbpp<std::uint32_t>(cppObj.batch_byte_limit,
|
10078
10107
|
jsObj.Get("batch_byte_limit"));
|
10108
|
+
js_to_cbpp<std::chrono::milliseconds>(cppObj.timeout,
|
10109
|
+
jsObj.Get("timeout"));
|
10079
10110
|
js_to_cbpp<std::chrono::milliseconds>(cppObj.batch_time_limit,
|
10080
10111
|
jsObj.Get("batch_time_limit"));
|
10081
10112
|
// retry_strategy
|
10082
|
-
js_to_cbpp<bool>(cppObj.ids_only, jsObj.Get("ids_only"));
|
10083
10113
|
// internal
|
10084
10114
|
return cppObj;
|
10085
10115
|
}
|
@@ -10092,10 +10122,11 @@ struct js_to_cbpp_t<couchbase::core::range_scan_continue_options> {
|
|
10092
10122
|
cbpp_to_js<std::uint32_t>(env, cppObj.batch_item_limit));
|
10093
10123
|
resObj.Set("batch_byte_limit",
|
10094
10124
|
cbpp_to_js<std::uint32_t>(env, cppObj.batch_byte_limit));
|
10125
|
+
resObj.Set("timeout",
|
10126
|
+
cbpp_to_js<std::chrono::milliseconds>(env, cppObj.timeout));
|
10095
10127
|
resObj.Set("batch_time_limit", cbpp_to_js<std::chrono::milliseconds>(
|
10096
10128
|
env, cppObj.batch_time_limit));
|
10097
10129
|
// retry_strategy
|
10098
|
-
resObj.Set("ids_only", cbpp_to_js<bool>(env, cppObj.ids_only));
|
10099
10130
|
// internal
|
10100
10131
|
return resObj;
|
10101
10132
|
}
|
@@ -10189,7 +10220,7 @@ struct js_to_cbpp_t<couchbase::core::range_scan_item> {
|
|
10189
10220
|
{
|
10190
10221
|
auto jsObj = jsVal.ToObject();
|
10191
10222
|
couchbase::core::range_scan_item cppObj;
|
10192
|
-
js_to_cbpp<std::
|
10223
|
+
js_to_cbpp<std::string>(cppObj.key, jsObj.Get("key"));
|
10193
10224
|
js_to_cbpp<std::optional<couchbase::core::range_scan_item_body>>(
|
10194
10225
|
cppObj.body, jsObj.Get("body"));
|
10195
10226
|
return cppObj;
|
@@ -10198,7 +10229,7 @@ struct js_to_cbpp_t<couchbase::core::range_scan_item> {
|
|
10198
10229
|
to_js(Napi::Env env, const couchbase::core::range_scan_item &cppObj)
|
10199
10230
|
{
|
10200
10231
|
auto resObj = Napi::Object::New(env);
|
10201
|
-
resObj.Set("key", cbpp_to_js<std::
|
10232
|
+
resObj.Set("key", cbpp_to_js<std::string>(env, cppObj.key));
|
10202
10233
|
resObj.Set(
|
10203
10234
|
"body",
|
10204
10235
|
cbpp_to_js<std::optional<couchbase::core::range_scan_item_body>>(
|
@@ -10255,13 +10286,11 @@ struct js_to_cbpp_t<couchbase::core::range_scan_orchestrator_options> {
|
|
10255
10286
|
js_to_cbpp<bool>(cppObj.ids_only, jsObj.Get("ids_only"));
|
10256
10287
|
js_to_cbpp<std::optional<couchbase::core::mutation_state>>(
|
10257
10288
|
cppObj.consistent_with, jsObj.Get("consistent_with"));
|
10258
|
-
js_to_cbpp<couchbase::core::scan_sort>(cppObj.sort, jsObj.Get("sort"));
|
10259
10289
|
js_to_cbpp<std::uint32_t>(cppObj.batch_item_limit,
|
10260
10290
|
jsObj.Get("batch_item_limit"));
|
10261
10291
|
js_to_cbpp<std::uint32_t>(cppObj.batch_byte_limit,
|
10262
10292
|
jsObj.Get("batch_byte_limit"));
|
10263
|
-
js_to_cbpp<std::
|
10264
|
-
jsObj.Get("batch_time_limit"));
|
10293
|
+
js_to_cbpp<std::uint16_t>(cppObj.concurrency, jsObj.Get("concurrency"));
|
10265
10294
|
// retry_strategy
|
10266
10295
|
js_to_cbpp<std::chrono::milliseconds>(cppObj.timeout,
|
10267
10296
|
jsObj.Get("timeout"));
|
@@ -10277,14 +10306,12 @@ struct js_to_cbpp_t<couchbase::core::range_scan_orchestrator_options> {
|
|
10277
10306
|
resObj.Set("consistent_with",
|
10278
10307
|
cbpp_to_js<std::optional<couchbase::core::mutation_state>>(
|
10279
10308
|
env, cppObj.consistent_with));
|
10280
|
-
resObj.Set("sort",
|
10281
|
-
cbpp_to_js<couchbase::core::scan_sort>(env, cppObj.sort));
|
10282
10309
|
resObj.Set("batch_item_limit",
|
10283
10310
|
cbpp_to_js<std::uint32_t>(env, cppObj.batch_item_limit));
|
10284
10311
|
resObj.Set("batch_byte_limit",
|
10285
10312
|
cbpp_to_js<std::uint32_t>(env, cppObj.batch_byte_limit));
|
10286
|
-
resObj.Set("
|
10287
|
-
|
10313
|
+
resObj.Set("concurrency",
|
10314
|
+
cbpp_to_js<std::uint16_t>(env, cppObj.concurrency));
|
10288
10315
|
// retry_strategy
|
10289
10316
|
resObj.Set("timeout",
|
10290
10317
|
cbpp_to_js<std::chrono::milliseconds>(env, cppObj.timeout));
|
package/src/jstocbpp_basic.hpp
CHANGED
@@ -49,15 +49,9 @@ struct js_to_cbpp_t<couchbase::core::io::dns::dns_config> {
|
|
49
49
|
from_js(Napi::Value jsVal)
|
50
50
|
{
|
51
51
|
auto jsObj = jsVal.ToObject();
|
52
|
-
auto nameserver = js_to_cbpp<std::string>(jsObj.Get("nameserver"));
|
53
|
-
auto jsPort = jsObj.Get("port");
|
54
52
|
auto cppObj = couchbase::core::io::dns::dns_config{
|
55
|
-
|
56
|
-
|
57
|
-
: nameserver,
|
58
|
-
jsPort.IsNull() || jsPort.IsUndefined()
|
59
|
-
? couchbase::core::io::dns::dns_config::default_port
|
60
|
-
: js_to_cbpp<std::uint16_t>(jsPort),
|
53
|
+
js_to_cbpp<std::string>(jsObj.Get("nameserver")),
|
54
|
+
js_to_cbpp<std::uint16_t>(jsObj.Get("port")),
|
61
55
|
js_to_cbpp<std::chrono::milliseconds>(jsObj.Get("dnsSrvTimeout"))};
|
62
56
|
return cppObj;
|
63
57
|
}
|
package/src/mutationtoken.cpp
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#include "mutationtoken.hpp"
|
2
|
+
#include "jstocbpp.hpp"
|
2
3
|
#include "utils.hpp"
|
3
4
|
#include <sstream>
|
4
5
|
|
@@ -81,6 +82,18 @@ couchbase::mutation_token MutationToken::parse(Napi::Value val)
|
|
81
82
|
return couchbase::mutation_token{};
|
82
83
|
} else if (val.IsObject()) {
|
83
84
|
auto objVal = val.As<Napi::Object>();
|
85
|
+
if (objVal.HasOwnProperty("partition_uuid")) {
|
86
|
+
auto partitionUuid =
|
87
|
+
jsToCbpp<std::uint64_t>(objVal.Get("partition_uuid"));
|
88
|
+
auto sequenceNumber =
|
89
|
+
jsToCbpp<std::uint64_t>(objVal.Get("sequence_number"));
|
90
|
+
auto partitionId =
|
91
|
+
jsToCbpp<std::uint16_t>(objVal.Get("partition_id"));
|
92
|
+
auto bucketName = jsToCbpp<std::string>(objVal.Get("bucket_name"));
|
93
|
+
return couchbase::mutation_token{partitionUuid, sequenceNumber,
|
94
|
+
partitionId, bucketName};
|
95
|
+
}
|
96
|
+
|
84
97
|
auto maybeRawVal = objVal.Get("raw");
|
85
98
|
if (!maybeRawVal.IsEmpty()) {
|
86
99
|
return MutationToken::fromBuffer(maybeRawVal);
|
@@ -0,0 +1,90 @@
|
|
1
|
+
#include "scan_iterator.hpp"
|
2
|
+
#include "connection.hpp"
|
3
|
+
#include "jstocbpp.hpp"
|
4
|
+
|
5
|
+
namespace couchnode
|
6
|
+
{
|
7
|
+
|
8
|
+
void ScanIterator::Init(Napi::Env env, Napi::Object exports)
|
9
|
+
{
|
10
|
+
Napi::Function func = DefineClass(
|
11
|
+
env, "ScanIterator",
|
12
|
+
{
|
13
|
+
InstanceMethod<&ScanIterator::jsNext>("next"),
|
14
|
+
InstanceMethod<&ScanIterator::jsCancel>("cancel"),
|
15
|
+
InstanceAccessor<&ScanIterator::jsCancelled>("cancelled"),
|
16
|
+
});
|
17
|
+
|
18
|
+
constructor(env) = Napi::Persistent(func);
|
19
|
+
exports.Set("ScanIterator", func);
|
20
|
+
}
|
21
|
+
|
22
|
+
ScanIterator::ScanIterator(const Napi::CallbackInfo &info)
|
23
|
+
: Napi::ObjectWrap<ScanIterator>(info)
|
24
|
+
{
|
25
|
+
if (info.Length() > 0) {
|
26
|
+
auto wrapped_result =
|
27
|
+
*info[0]
|
28
|
+
.As<const Napi::External<couchbase::core::scan_result>>()
|
29
|
+
.Data();
|
30
|
+
this->result_ =
|
31
|
+
std::make_shared<couchbase::core::scan_result>(wrapped_result);
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
ScanIterator::~ScanIterator()
|
36
|
+
{
|
37
|
+
}
|
38
|
+
|
39
|
+
Napi::Value ScanIterator::jsNext(const Napi::CallbackInfo &info)
|
40
|
+
{
|
41
|
+
auto env = info.Env();
|
42
|
+
auto callbackJsFn = info[0].As<Napi::Function>();
|
43
|
+
auto cookie = CallCookie(env, callbackJsFn, "cbRangeScanNext");
|
44
|
+
|
45
|
+
auto handler = [](Napi::Env env, Napi::Function callback,
|
46
|
+
couchbase::core::range_scan_item resp,
|
47
|
+
std::error_code ec) mutable {
|
48
|
+
Napi::Value jsErr, jsRes;
|
49
|
+
if (ec && ec == couchbase::errc::key_value::range_scan_completed) {
|
50
|
+
jsErr = env.Null();
|
51
|
+
jsRes = env.Undefined();
|
52
|
+
} else {
|
53
|
+
try {
|
54
|
+
jsErr = cbpp_to_js(env, ec);
|
55
|
+
jsRes = cbpp_to_js(env, resp);
|
56
|
+
} catch (const Napi::Error &e) {
|
57
|
+
jsErr = e.Value();
|
58
|
+
jsRes = env.Null();
|
59
|
+
}
|
60
|
+
}
|
61
|
+
callback.Call({jsErr, jsRes});
|
62
|
+
};
|
63
|
+
|
64
|
+
this->result_->next(
|
65
|
+
[cookie = std::move(cookie), handler = std::move(handler)](
|
66
|
+
couchbase::core::range_scan_item resp, std::error_code ec) mutable {
|
67
|
+
cookie.invoke([handler = std::move(handler), resp = std::move(resp),
|
68
|
+
ec = std::move(ec)](
|
69
|
+
Napi::Env env, Napi::Function callback) mutable {
|
70
|
+
handler(env, callback, std::move(resp), std::move(ec));
|
71
|
+
});
|
72
|
+
});
|
73
|
+
|
74
|
+
return env.Null();
|
75
|
+
}
|
76
|
+
|
77
|
+
Napi::Value ScanIterator::jsCancel(const Napi::CallbackInfo &info)
|
78
|
+
{
|
79
|
+
auto env = info.Env();
|
80
|
+
this->result_->cancel();
|
81
|
+
return Napi::Boolean::New(env, this->result_->is_cancelled());
|
82
|
+
}
|
83
|
+
|
84
|
+
Napi::Value ScanIterator::jsCancelled(const Napi::CallbackInfo &info)
|
85
|
+
{
|
86
|
+
auto env = info.Env();
|
87
|
+
return Napi::Boolean::New(env, this->result_->is_cancelled());
|
88
|
+
}
|
89
|
+
|
90
|
+
} // namespace couchnode
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#pragma once
|
2
|
+
#include "addondata.hpp"
|
3
|
+
#include "napi.h"
|
4
|
+
#include <core/scan_result.hxx>
|
5
|
+
|
6
|
+
namespace couchnode
|
7
|
+
{
|
8
|
+
|
9
|
+
class ScanIterator : public Napi::ObjectWrap<ScanIterator>
|
10
|
+
{
|
11
|
+
public:
|
12
|
+
static Napi::FunctionReference &constructor(Napi::Env env)
|
13
|
+
{
|
14
|
+
return AddonData::fromEnv(env)->_scanIteratorCtor;
|
15
|
+
}
|
16
|
+
|
17
|
+
static void Init(Napi::Env env, Napi::Object exports);
|
18
|
+
|
19
|
+
ScanIterator(const Napi::CallbackInfo &info);
|
20
|
+
~ScanIterator();
|
21
|
+
|
22
|
+
Napi::Value jsNext(const Napi::CallbackInfo &info);
|
23
|
+
Napi::Value jsCancel(const Napi::CallbackInfo &info);
|
24
|
+
Napi::Value jsCancelled(const Napi::CallbackInfo &info);
|
25
|
+
|
26
|
+
private:
|
27
|
+
std::shared_ptr<couchbase::core::scan_result> result_;
|
28
|
+
};
|
29
|
+
|
30
|
+
} // namespace couchnode
|
@@ -1,15 +1,12 @@
|
|
1
|
-
from logging.config import valid_ident
|
2
1
|
import os
|
3
2
|
import json
|
4
3
|
import re
|
5
|
-
import subprocess
|
6
|
-
from unittest import TestCase
|
7
4
|
import clang.cindex
|
8
5
|
|
9
6
|
# configurable part
|
10
7
|
|
11
8
|
CLANG_VERSION='13.0.1'
|
12
|
-
# homebrew installs for llvm (brew info llvm gives details):
|
9
|
+
# homebrew installs for llvm (brew info llvm gives details):
|
13
10
|
# x64: /usr/local/opt/llvm/lib
|
14
11
|
# arm64: /opt/homebrew/opt/llvm/lib
|
15
12
|
llvmLibPath = "/usr/local/Cellar/llvm/13.0.1_1/lib/"
|
@@ -148,6 +145,7 @@ typeList = [
|
|
148
145
|
"couchbase::core::scan_term",
|
149
146
|
"couchbase::core::scan_sort",
|
150
147
|
"couchbase::core::range_scan",
|
148
|
+
"couchbase::core::prefix_scan",
|
151
149
|
"couchbase::core::sampling_scan",
|
152
150
|
"couchbase::core::range_snapshot_requirements",
|
153
151
|
"couchbase::core::range_scan_item_body",
|
@@ -341,7 +339,7 @@ def parse_type_str(typeStr):
|
|
341
339
|
"to": parse_type_str(variantParts[1]),
|
342
340
|
"comparator": parse_type_str(variantParts[2])
|
343
341
|
}
|
344
|
-
|
342
|
+
|
345
343
|
if tplClassName == "std::shared_ptr":
|
346
344
|
return {
|
347
345
|
"name": "std::shared_ptr",
|
@@ -358,6 +356,7 @@ def parse_type_str(typeStr):
|
|
358
356
|
return {"name": typeStr}
|
359
357
|
|
360
358
|
internal_structs = []
|
359
|
+
UNNAMED_STRUCT_DELIM = '::(unnamed struct'
|
361
360
|
|
362
361
|
def traverse(node, namespace, main_file):
|
363
362
|
# only scan the elements of the file we parsed
|
@@ -366,8 +365,9 @@ def traverse(node, namespace, main_file):
|
|
366
365
|
|
367
366
|
if node.kind == clang.cindex.CursorKind.STRUCT_DECL or node.kind == clang.cindex.CursorKind.CLASS_DECL:
|
368
367
|
fullStructName = "::".join([*namespace, node.displayname])
|
369
|
-
if fullStructName.endswith('::'):
|
370
|
-
|
368
|
+
if fullStructName.endswith('::') or UNNAMED_STRUCT_DELIM in fullStructName:
|
369
|
+
struct_name = fullStructName if fullStructName.endswith('::') else fullStructName.split(UNNAMED_STRUCT_DELIM)[0]
|
370
|
+
match = next((s for s in internal_structs if struct_name in s), None)
|
371
371
|
if match:
|
372
372
|
fullStructName = match
|
373
373
|
|
@@ -451,7 +451,8 @@ for headerPath in fullFileList:
|
|
451
451
|
"-I" + cxxClientRoot + "/third_party/json/include",
|
452
452
|
"-I" + cxxClientRoot + "/third_party/json/external/PEGTL/include",
|
453
453
|
"-I" + cxxClientRoot + "/third_party/asio/asio/include",
|
454
|
-
"-I" + f"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/{CLANG_VERSION}/include"
|
454
|
+
"-I" + f"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/{CLANG_VERSION}/include",
|
455
|
+
# "-I" + f'/opt/homebrew/Cellar/llvm/{CLANG_VERSION}/lib/clang/{CLANG_VERSION[:2]}/include',
|
455
456
|
]
|
456
457
|
translation_unit = index.parse(headerPath, args=args)
|
457
458
|
|