libcouchbase 0.0.7 → 0.0.8
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.
- checksums.yaml +4 -4
- data/ext/libcouchbase/.gitignore +2 -0
- data/ext/libcouchbase/CMakeLists.txt +5 -7
- data/ext/libcouchbase/README.markdown +2 -2
- data/ext/libcouchbase/RELEASE_NOTES.markdown +49 -0
- data/ext/libcouchbase/cmake/Modules/ConfigureDtrace.cmake +11 -0
- data/ext/libcouchbase/cmake/Modules/GenerateConfigDotH.cmake +2 -0
- data/ext/libcouchbase/cmake/Modules/GetLibcouchbaseFlags.cmake +2 -1
- data/ext/libcouchbase/cmake/Modules/GetVersionInfo.cmake +3 -3
- data/ext/libcouchbase/cmake/config-cmake.h.in +2 -0
- data/ext/libcouchbase/cmake/defs.mk.in +0 -2
- data/ext/libcouchbase/cmake/source_files.cmake +34 -14
- data/ext/libcouchbase/configure.pl +1 -1
- data/ext/libcouchbase/contrib/genhash/genhash.h +6 -0
- data/ext/libcouchbase/include/libcouchbase/auth.h +10 -0
- data/ext/libcouchbase/include/libcouchbase/couchbase.h +10 -0
- data/ext/libcouchbase/include/libcouchbase/error.h +7 -0
- data/ext/libcouchbase/include/libcouchbase/n1ql.h +13 -1
- data/ext/libcouchbase/include/libcouchbase/plugins/io/bsdio-inl.c +1 -1
- data/ext/libcouchbase/include/libcouchbase/subdoc.h +9 -0
- data/ext/libcouchbase/include/libcouchbase/views.h +7 -1
- data/ext/libcouchbase/include/libcouchbase/visibility.h +1 -0
- data/ext/libcouchbase/include/memcached/protocol_binary.h +21 -1132
- data/ext/libcouchbase/packaging/parse-git-describe.pl +1 -1
- data/ext/libcouchbase/plugins/io/libev/libev_io_opts.h +3 -2
- data/ext/libcouchbase/src/README.md +0 -2
- data/ext/libcouchbase/src/auth-priv.h +1 -0
- data/ext/libcouchbase/src/auth.cc +10 -0
- data/ext/libcouchbase/src/bootstrap.cc +216 -0
- data/ext/libcouchbase/src/bootstrap.h +50 -39
- data/ext/libcouchbase/src/bucketconfig/bc_cccp.cc +455 -0
- data/ext/libcouchbase/src/bucketconfig/bc_file.cc +281 -0
- data/ext/libcouchbase/src/bucketconfig/bc_http.cc +528 -0
- data/ext/libcouchbase/src/bucketconfig/bc_http.h +50 -25
- data/ext/libcouchbase/src/bucketconfig/bc_mcraw.cc +115 -0
- data/ext/libcouchbase/src/bucketconfig/clconfig.h +407 -386
- data/ext/libcouchbase/src/bucketconfig/confmon.cc +378 -0
- data/ext/libcouchbase/src/cbft.cc +22 -27
- data/ext/libcouchbase/src/cntl.cc +24 -24
- data/ext/libcouchbase/src/connspec.cc +30 -1
- data/ext/libcouchbase/src/connspec.h +17 -0
- data/ext/libcouchbase/src/dns-srv.cc +143 -0
- data/ext/libcouchbase/src/{dump.c → dump.cc} +8 -11
- data/ext/libcouchbase/src/getconfig.cc +73 -0
- data/ext/libcouchbase/src/handler.cc +84 -85
- data/ext/libcouchbase/src/hostlist.cc +0 -1
- data/ext/libcouchbase/src/hostlist.h +6 -1
- data/ext/libcouchbase/src/http/http-priv.h +125 -112
- data/ext/libcouchbase/src/http/http.cc +9 -29
- data/ext/libcouchbase/src/http/http.h +1 -34
- data/ext/libcouchbase/src/http/http_io.cc +22 -26
- data/ext/libcouchbase/src/instance.cc +102 -28
- data/ext/libcouchbase/src/internal.h +47 -29
- data/ext/libcouchbase/src/jsparse/parser.cc +146 -202
- data/ext/libcouchbase/src/jsparse/parser.h +91 -98
- data/ext/libcouchbase/src/lcbht/lcbht.cc +177 -0
- data/ext/libcouchbase/src/lcbht/lcbht.h +174 -163
- data/ext/libcouchbase/src/lcbio/connect.cc +562 -0
- data/ext/libcouchbase/src/lcbio/connect.h +9 -2
- data/ext/libcouchbase/src/lcbio/ctx.c +1 -1
- data/ext/libcouchbase/src/lcbio/iotable.h +61 -16
- data/ext/libcouchbase/src/lcbio/ioutils.h +1 -1
- data/ext/libcouchbase/src/lcbio/manager.c +2 -2
- data/ext/libcouchbase/src/lcbio/timer-cxx.h +87 -0
- data/ext/libcouchbase/src/mc/mcreq.h +9 -2
- data/ext/libcouchbase/src/mcserver/mcserver.cc +723 -0
- data/ext/libcouchbase/src/mcserver/mcserver.h +160 -70
- data/ext/libcouchbase/src/mcserver/negotiate.cc +118 -152
- data/ext/libcouchbase/src/mcserver/negotiate.h +85 -74
- data/ext/libcouchbase/src/mctx-helper.h +51 -0
- data/ext/libcouchbase/src/n1ql/ixmgmt.cc +1 -2
- data/ext/libcouchbase/src/n1ql/n1ql.cc +56 -32
- data/ext/libcouchbase/src/{newconfig.c → newconfig.cc} +42 -70
- data/ext/libcouchbase/src/nodeinfo.cc +4 -8
- data/ext/libcouchbase/src/operations/{cbflush.c → cbflush.cc} +7 -15
- data/ext/libcouchbase/src/operations/{counter.c → counter.cc} +0 -0
- data/ext/libcouchbase/src/operations/{durability-cas.c → durability-cas.cc} +92 -76
- data/ext/libcouchbase/src/operations/{durability-seqno.c → durability-seqno.cc} +55 -49
- data/ext/libcouchbase/src/operations/durability.cc +643 -0
- data/ext/libcouchbase/src/operations/durability_internal.h +212 -124
- data/ext/libcouchbase/src/operations/{get.c → get.cc} +24 -26
- data/ext/libcouchbase/src/operations/{observe-seqno.c → observe-seqno.cc} +5 -8
- data/ext/libcouchbase/src/operations/{observe.c → observe.cc} +69 -94
- data/ext/libcouchbase/src/operations/{pktfwd.c → pktfwd.cc} +0 -0
- data/ext/libcouchbase/src/operations/{remove.c → remove.cc} +0 -0
- data/ext/libcouchbase/src/operations/{stats.c → stats.cc} +66 -78
- data/ext/libcouchbase/src/operations/{store.c → store.cc} +27 -32
- data/ext/libcouchbase/src/operations/subdoc.cc +38 -18
- data/ext/libcouchbase/src/operations/{touch.c → touch.cc} +0 -0
- data/ext/libcouchbase/src/packetutils.h +200 -137
- data/ext/libcouchbase/src/probes.d +1 -1
- data/ext/libcouchbase/src/{retrychk.c → retrychk.cc} +3 -4
- data/ext/libcouchbase/src/retryq.cc +394 -0
- data/ext/libcouchbase/src/retryq.h +116 -104
- data/ext/libcouchbase/src/settings.h +2 -1
- data/ext/libcouchbase/src/ssl/ssl_c.c +1 -0
- data/ext/libcouchbase/src/ssl/ssl_e.c +0 -1
- data/ext/libcouchbase/src/trace.h +8 -8
- data/ext/libcouchbase/src/vbucket/vbucket.c +0 -1
- data/ext/libcouchbase/src/views/{docreq.c → docreq.cc} +48 -54
- data/ext/libcouchbase/src/views/docreq.h +24 -30
- data/ext/libcouchbase/src/views/viewreq.cc +318 -0
- data/ext/libcouchbase/src/views/viewreq.h +43 -13
- data/ext/libcouchbase/src/{wait.c → wait.cc} +12 -17
- data/ext/libcouchbase/tests/basic/t_connstr.cc +89 -50
- data/ext/libcouchbase/tests/basic/t_jsparse.cc +27 -78
- data/ext/libcouchbase/tests/basic/t_packet.cc +35 -42
- data/ext/libcouchbase/tests/htparse/t_basic.cc +58 -78
- data/ext/libcouchbase/tests/iotests/t_confmon.cc +94 -111
- data/ext/libcouchbase/tests/iotests/t_sched.cc +1 -2
- data/ext/libcouchbase/tests/mc/t_alloc.cc +9 -9
- data/ext/libcouchbase/tools/cbc-pillowfight.cc +1 -1
- data/lib/libcouchbase/version.rb +1 -1
- metadata +36 -39
- data/ext/libcouchbase/include/memcached/vbucket.h +0 -42
- data/ext/libcouchbase/src/bootstrap.c +0 -269
- data/ext/libcouchbase/src/bucketconfig/bc_cccp.c +0 -495
- data/ext/libcouchbase/src/bucketconfig/bc_file.c +0 -347
- data/ext/libcouchbase/src/bucketconfig/bc_http.c +0 -630
- data/ext/libcouchbase/src/bucketconfig/bc_mcraw.c +0 -150
- data/ext/libcouchbase/src/bucketconfig/confmon.c +0 -474
- data/ext/libcouchbase/src/getconfig.c +0 -100
- data/ext/libcouchbase/src/lcbht/lcbht.c +0 -282
- data/ext/libcouchbase/src/lcbio/connect.c +0 -557
- data/ext/libcouchbase/src/mcserver/mcserver.c +0 -784
- data/ext/libcouchbase/src/operations/durability.c +0 -668
- data/ext/libcouchbase/src/packetutils.c +0 -60
- data/ext/libcouchbase/src/retryq.c +0 -424
- data/ext/libcouchbase/src/simplestring.c +0 -211
- data/ext/libcouchbase/src/simplestring.h +0 -228
- data/ext/libcouchbase/src/ssobuf.h +0 -82
- data/ext/libcouchbase/src/views/viewreq.c +0 -358
- data/ext/libcouchbase/tests/basic/t_string.cc +0 -112
|
@@ -19,9 +19,8 @@
|
|
|
19
19
|
#define LCB_MCSERVER_NEGOTIATE_H
|
|
20
20
|
#include <libcouchbase/couchbase.h>
|
|
21
21
|
#include <lcbio/lcbio.h>
|
|
22
|
-
#
|
|
23
|
-
|
|
24
|
-
#endif
|
|
22
|
+
#include <string>
|
|
23
|
+
#include <vector>
|
|
25
24
|
|
|
26
25
|
/**
|
|
27
26
|
* @file
|
|
@@ -37,83 +36,95 @@ extern "C" {
|
|
|
37
36
|
*/
|
|
38
37
|
|
|
39
38
|
struct lcb_settings_st;
|
|
40
|
-
typedef struct mc_SESSREQ *mc_pSESSREQ;
|
|
41
|
-
typedef struct mc_SESSINFO *mc_pSESSINFO;
|
|
42
39
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
*
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
40
|
+
namespace lcb {
|
|
41
|
+
class SessionRequest {
|
|
42
|
+
public:
|
|
43
|
+
/**
|
|
44
|
+
* @brief Start negotiation on a connected socket
|
|
45
|
+
*
|
|
46
|
+
* This will start negotiation on the socket. Once complete (or an error has
|
|
47
|
+
* taken place) the `callback` will be invoked with the result.
|
|
48
|
+
*
|
|
49
|
+
* @param sock A connected socket to use. Its reference count will be increased
|
|
50
|
+
* @param settings A settings structure. Used for auth information as well as
|
|
51
|
+
* logging
|
|
52
|
+
* @param tmo Time in microseconds to wait until the negotiation is done
|
|
53
|
+
* @param callback A callback to invoke when a result has been received
|
|
54
|
+
* @param data User-defined pointer passed to the callback
|
|
55
|
+
* @return A new handle which may be cancelled via mc_sessreq_cancel(). As with
|
|
56
|
+
* other cancellable requests, once this handle is cancelled a callback will
|
|
57
|
+
* not be received for it, and once the callback is received the handle may not
|
|
58
|
+
* be cancelled as it will point to invalid memory.
|
|
59
|
+
*
|
|
60
|
+
* Once the socket has been negotiated successfuly, you may then use the
|
|
61
|
+
* mc_sess_get() function to query the socket about various negotiation aspects
|
|
62
|
+
*
|
|
63
|
+
* @code{.c}
|
|
64
|
+
* lcbio_CONNREQ creq;
|
|
65
|
+
* SessionRequest *req = SessionRequest::start(sock, settings, tmo, callback, data);
|
|
66
|
+
* LCBIO_CONNREQ_MKGENERIC(req, sessreq_cancel);
|
|
67
|
+
* @endcode
|
|
68
|
+
*
|
|
69
|
+
* @see lcb::sessreq_cancel()
|
|
70
|
+
* @see LCBIO_CONNREQ_MKGENERIC
|
|
71
|
+
*/
|
|
72
|
+
static SessionRequest *start(lcbio_SOCKET *sock, lcb_settings_st *settings,
|
|
73
|
+
uint32_t tmo, lcbio_CONNDONE_cb callback,
|
|
74
|
+
void *data);
|
|
77
75
|
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
void
|
|
83
|
-
|
|
76
|
+
/**
|
|
77
|
+
* @brief Cancel a pending SASL negotiation request
|
|
78
|
+
* @param handle The handle to cancel
|
|
79
|
+
*/
|
|
80
|
+
virtual void cancel() = 0;
|
|
81
|
+
virtual ~SessionRequest(){}
|
|
82
|
+
};
|
|
83
|
+
class SessionRequestImpl;
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
* @brief Get an opaque handle representing the negotiated state of the socket
|
|
87
|
-
* @param sock The negotiated socket
|
|
88
|
-
* @return the `SASLINFO` structure if the socket is negotiated, or `NULL` if
|
|
89
|
-
* the socket has not been negotiated.
|
|
90
|
-
*
|
|
91
|
-
* @see mc_sasl_getmech()
|
|
92
|
-
*/
|
|
93
|
-
mc_pSESSINFO
|
|
94
|
-
mc_sess_get(lcbio_SOCKET *sock);
|
|
85
|
+
extern "C" { void sessreq_cancel(SessionRequest *); }
|
|
95
86
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
87
|
+
class SessionInfo : public lcbio_PROTOCTX {
|
|
88
|
+
public:
|
|
89
|
+
/**
|
|
90
|
+
* @brief Get an opaque handle representing the negotiated state of the socket
|
|
91
|
+
* @param sock The negotiated socket
|
|
92
|
+
* @return the `SASLINFO` structure if the socket is negotiated, or `NULL` if
|
|
93
|
+
* the socket has not been negotiated.
|
|
94
|
+
*
|
|
95
|
+
* @see get_mech()
|
|
96
|
+
*/
|
|
97
|
+
static SessionInfo* get(lcbio_SOCKET*);
|
|
104
98
|
|
|
105
|
-
/**
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
99
|
+
/**
|
|
100
|
+
* @brief Get the mechanism employed for authentication
|
|
101
|
+
* @param info pointer retrieved via mc_sasl_get()
|
|
102
|
+
* @return A string indicating the mechanism used. This may be `PLAIN` or
|
|
103
|
+
* `CRAM-MD5`.
|
|
104
|
+
*/
|
|
105
|
+
const std::string& get_mech() const {
|
|
106
|
+
return mech;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @brief Determine if a specific protocol feature is supported on the server
|
|
111
|
+
* @param info info pointer returned via mc_sasl_get()
|
|
112
|
+
* @param feature A feature ID
|
|
113
|
+
* @return true if supported, false otherwise
|
|
114
|
+
*/
|
|
115
|
+
bool has_feature(uint16_t feature) const;
|
|
116
|
+
|
|
117
|
+
private:
|
|
118
|
+
SessionInfo();
|
|
119
|
+
friend class lcb::SessionRequestImpl;
|
|
120
|
+
|
|
121
|
+
std::string mech;
|
|
122
|
+
std::vector<uint16_t> server_features;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
} // namespace
|
|
113
127
|
|
|
114
128
|
/**@}*/
|
|
115
129
|
|
|
116
|
-
#ifdef __cplusplus
|
|
117
|
-
}
|
|
118
|
-
#endif
|
|
119
130
|
#endif
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2016 Couchbase, Inc.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
#ifndef LCB_MCTX_HELPER_H
|
|
18
|
+
#define LCB_MCTX_HELPER_H
|
|
19
|
+
#include <libcouchbase/couchbase.h>
|
|
20
|
+
|
|
21
|
+
namespace lcb {
|
|
22
|
+
|
|
23
|
+
class MultiCmdContext : public lcb_MULTICMD_CTX {
|
|
24
|
+
protected:
|
|
25
|
+
virtual lcb_error_t MCTX_addcmd(const lcb_CMDBASE* cmd) = 0;
|
|
26
|
+
virtual lcb_error_t MCTX_done(const void *cookie) = 0;
|
|
27
|
+
virtual void MCTX_fail() = 0;
|
|
28
|
+
|
|
29
|
+
MultiCmdContext() {
|
|
30
|
+
lcb_MULTICMD_CTX::addcmd = dispatch_mctx_addcmd;
|
|
31
|
+
lcb_MULTICMD_CTX::done = dispatch_mctx_done;
|
|
32
|
+
lcb_MULTICMD_CTX::fail = dispatch_mctx_fail;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
virtual ~MultiCmdContext() {}
|
|
36
|
+
|
|
37
|
+
private:
|
|
38
|
+
static lcb_error_t dispatch_mctx_addcmd(lcb_MULTICMD_CTX* ctx, const lcb_CMDBASE * cmd) {
|
|
39
|
+
return static_cast<MultiCmdContext*>(ctx)->MCTX_addcmd(cmd);
|
|
40
|
+
}
|
|
41
|
+
static lcb_error_t dispatch_mctx_done(lcb_MULTICMD_CTX* ctx, const void *cookie) {
|
|
42
|
+
return static_cast<MultiCmdContext*>(ctx)->MCTX_done(cookie);
|
|
43
|
+
}
|
|
44
|
+
static void dispatch_mctx_fail(lcb_MULTICMD_CTX* ctx) {
|
|
45
|
+
static_cast<MultiCmdContext*>(ctx)->MCTX_fail();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#endif
|
|
@@ -210,8 +210,7 @@ dispatch_common(lcb_t instance,
|
|
|
210
210
|
// mind-numbing buffer copies. Maybe this can be done via a macro instead?
|
|
211
211
|
class IndexSpec : public lcb_N1XSPEC {
|
|
212
212
|
public:
|
|
213
|
-
IndexSpec(const char *s, size_t n) {
|
|
214
|
-
memset(static_cast<lcb_N1XSPEC*>(this), 0, sizeof (lcb_N1XSPEC));
|
|
213
|
+
IndexSpec(const char *s, size_t n) : lcb_N1XSPEC() {
|
|
215
214
|
load_json(s, n);
|
|
216
215
|
}
|
|
217
216
|
inline IndexSpec(const lcb_N1XSPEC *spec);
|
|
@@ -142,10 +142,10 @@ struct lcb_N1QLCACHE_st {
|
|
|
142
142
|
}
|
|
143
143
|
};
|
|
144
144
|
|
|
145
|
-
typedef struct lcb_N1QLREQ {
|
|
145
|
+
typedef struct lcb_N1QLREQ : lcb::jsparse::Parser::Actions {
|
|
146
146
|
const lcb_RESPHTTP *cur_htresp;
|
|
147
147
|
struct lcb_http_request_st *htreq;
|
|
148
|
-
|
|
148
|
+
lcb::jsparse::Parser *parser;
|
|
149
149
|
const void *cookie;
|
|
150
150
|
lcb_N1QLCALLBACK callback;
|
|
151
151
|
lcb_t instance;
|
|
@@ -155,6 +155,9 @@ typedef struct lcb_N1QLREQ {
|
|
|
155
155
|
// How many rows were received. Used to avoid parsing the meta
|
|
156
156
|
size_t nrows;
|
|
157
157
|
|
|
158
|
+
// Host for CBAS/Analytics query
|
|
159
|
+
std::string cbashost;
|
|
160
|
+
|
|
158
161
|
/** The PREPARE query itself */
|
|
159
162
|
struct lcb_N1QLREQ *prepare_req;
|
|
160
163
|
|
|
@@ -227,9 +230,28 @@ typedef struct lcb_N1QLREQ {
|
|
|
227
230
|
*/
|
|
228
231
|
inline void fail_prepared(const lcb_RESPN1QL *orig, lcb_error_t err);
|
|
229
232
|
|
|
233
|
+
bool is_cbas() const {
|
|
234
|
+
return !cbashost.empty();
|
|
235
|
+
}
|
|
236
|
+
|
|
230
237
|
inline lcb_N1QLREQ(lcb_t obj, const void *user_cookie, const lcb_CMDN1QL *cmd);
|
|
231
238
|
inline ~lcb_N1QLREQ();
|
|
232
239
|
|
|
240
|
+
// Parser overrides:
|
|
241
|
+
void JSPARSE_on_row(const lcb::jsparse::Row& row) {
|
|
242
|
+
lcb_RESPN1QL resp = { 0 };
|
|
243
|
+
resp.row = static_cast<const char *>(row.row.iov_base);
|
|
244
|
+
resp.nrow = row.row.iov_len;
|
|
245
|
+
nrows++;
|
|
246
|
+
invoke_row(&resp, false);
|
|
247
|
+
}
|
|
248
|
+
void JSPARSE_on_error(const std::string&) {
|
|
249
|
+
lasterr = LCB_PROTOCOL_ERROR;
|
|
250
|
+
}
|
|
251
|
+
void JSPARSE_on_complete(const std::string&) {
|
|
252
|
+
// Nothing
|
|
253
|
+
}
|
|
254
|
+
|
|
233
255
|
} N1QLREQ;
|
|
234
256
|
|
|
235
257
|
static bool
|
|
@@ -330,8 +352,7 @@ N1QLREQ::maybe_retry()
|
|
|
330
352
|
}
|
|
331
353
|
|
|
332
354
|
was_retried = true;
|
|
333
|
-
|
|
334
|
-
lcbjsp_get_postmortem(parser, &meta);
|
|
355
|
+
parser->get_postmortem(meta);
|
|
335
356
|
if (!parse_json(static_cast<const char*>(meta.iov_base), meta.iov_len, root)) {
|
|
336
357
|
return false; // Not JSON
|
|
337
358
|
}
|
|
@@ -351,7 +372,7 @@ N1QLREQ::maybe_retry()
|
|
|
351
372
|
|
|
352
373
|
} else {
|
|
353
374
|
// We'll be parsing more rows later on..
|
|
354
|
-
|
|
375
|
+
parser->reset();
|
|
355
376
|
}
|
|
356
377
|
return true;
|
|
357
378
|
}
|
|
@@ -366,7 +387,7 @@ N1QLREQ::invoke_row(lcb_RESPN1QL *resp, bool is_last)
|
|
|
366
387
|
lcb_IOV meta;
|
|
367
388
|
resp->rflags |= LCB_RESP_F_FINAL;
|
|
368
389
|
resp->rc = lasterr;
|
|
369
|
-
|
|
390
|
+
parser->get_postmortem(meta);
|
|
370
391
|
resp->row = static_cast<const char*>(meta.iov_base);
|
|
371
392
|
resp->nrow = meta.iov_len;
|
|
372
393
|
}
|
|
@@ -392,31 +413,13 @@ lcb_N1QLREQ::~lcb_N1QLREQ()
|
|
|
392
413
|
}
|
|
393
414
|
|
|
394
415
|
if (parser) {
|
|
395
|
-
|
|
416
|
+
delete parser;
|
|
396
417
|
}
|
|
397
418
|
if (prepare_req) {
|
|
398
419
|
lcb_n1ql_cancel(instance, prepare_req);
|
|
399
420
|
}
|
|
400
421
|
}
|
|
401
422
|
|
|
402
|
-
static void
|
|
403
|
-
row_callback(lcbjsp_PARSER *parser, const lcbjsp_ROW *datum)
|
|
404
|
-
{
|
|
405
|
-
N1QLREQ *req = static_cast<N1QLREQ*>(parser->data);
|
|
406
|
-
lcb_RESPN1QL resp = { 0 };
|
|
407
|
-
|
|
408
|
-
if (datum->type == LCBJSP_TYPE_ROW) {
|
|
409
|
-
resp.row = static_cast<const char*>(datum->row.iov_base);
|
|
410
|
-
resp.nrow = datum->row.iov_len;
|
|
411
|
-
req->nrows++;
|
|
412
|
-
req->invoke_row(&resp, 0);
|
|
413
|
-
} else if (datum->type == LCBJSP_TYPE_ERROR) {
|
|
414
|
-
req->lasterr = resp.rc = LCB_PROTOCOL_ERROR;
|
|
415
|
-
} else if (datum->type == LCBJSP_TYPE_COMPLETE) {
|
|
416
|
-
/* Nothing */
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
423
|
static void
|
|
421
424
|
chunk_callback(lcb_t instance, int ign, const lcb_RESPBASE *rb)
|
|
422
425
|
{
|
|
@@ -444,8 +447,7 @@ chunk_callback(lcb_t instance, int ign, const lcb_RESPBASE *rb)
|
|
|
444
447
|
delete req;
|
|
445
448
|
return;
|
|
446
449
|
}
|
|
447
|
-
|
|
448
|
-
lcbjsp_feed(req->parser, static_cast<const char*>(rh->body), rh->nbody);
|
|
450
|
+
req->parser->feed(static_cast<const char*>(rh->body), rh->nbody);
|
|
449
451
|
}
|
|
450
452
|
|
|
451
453
|
#define QUERY_PATH "/query/service"
|
|
@@ -512,7 +514,14 @@ N1QLREQ::issue_htreq(const std::string& body)
|
|
|
512
514
|
|
|
513
515
|
htcmd.content_type = "application/json";
|
|
514
516
|
htcmd.method = LCB_HTTP_METHOD_POST;
|
|
515
|
-
|
|
517
|
+
|
|
518
|
+
if (is_cbas()) {
|
|
519
|
+
htcmd.type = LCB_HTTP_TYPE_RAW;
|
|
520
|
+
htcmd.host = cbashost.c_str();
|
|
521
|
+
} else {
|
|
522
|
+
htcmd.type = LCB_HTTP_TYPE_N1QL;
|
|
523
|
+
}
|
|
524
|
+
|
|
516
525
|
htcmd.cmdflags = LCB_CMDHTTP_F_STREAM|LCB_CMDHTTP_F_CASTMO;
|
|
517
526
|
if (flags & F_CMDN1QL_CREDSAUTH) {
|
|
518
527
|
htcmd.cmdflags |= LCB_CMDHTTP_F_NOUPASS;
|
|
@@ -522,7 +531,7 @@ N1QLREQ::issue_htreq(const std::string& body)
|
|
|
522
531
|
|
|
523
532
|
lcb_error_t rc = lcb_http3(instance, this, &htcmd);
|
|
524
533
|
if (rc == LCB_SUCCESS) {
|
|
525
|
-
|
|
534
|
+
htreq->set_callback(chunk_callback);
|
|
526
535
|
}
|
|
527
536
|
return rc;
|
|
528
537
|
}
|
|
@@ -586,13 +595,12 @@ lcb_n1qlreq_parsetmo(const std::string& s)
|
|
|
586
595
|
|
|
587
596
|
lcb_N1QLREQ::lcb_N1QLREQ(lcb_t obj,
|
|
588
597
|
const void *user_cookie, const lcb_CMDN1QL *cmd)
|
|
589
|
-
: cur_htresp(NULL), htreq(NULL),
|
|
598
|
+
: cur_htresp(NULL), htreq(NULL),
|
|
599
|
+
parser(new lcb::jsparse::Parser(lcb::jsparse::Parser::MODE_N1QL, this)),
|
|
590
600
|
cookie(user_cookie), callback(cmd->callback), instance(obj),
|
|
591
601
|
lasterr(LCB_SUCCESS), flags(cmd->cmdflags), timeout(0),
|
|
592
602
|
nrows(0), prepare_req(NULL), was_retried(false)
|
|
593
603
|
{
|
|
594
|
-
parser->data = this;
|
|
595
|
-
parser->callback = row_callback;
|
|
596
604
|
if (cmd->handle) {
|
|
597
605
|
*cmd->handle = this;
|
|
598
606
|
}
|
|
@@ -604,6 +612,22 @@ lcb_N1QLREQ::lcb_N1QLREQ(lcb_t obj,
|
|
|
604
612
|
return;
|
|
605
613
|
}
|
|
606
614
|
|
|
615
|
+
if (flags & LCB_CMDN1QL_F_CBASQUERY) {
|
|
616
|
+
if (!cmd->host) {
|
|
617
|
+
lasterr = LCB_EINVAL;
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
cbashost.assign(cmd->host);
|
|
621
|
+
if (cbashost.empty()) {
|
|
622
|
+
lasterr = LCB_EINVAL;
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
if (flags & LCB_CMDN1QL_F_PREPCACHE) {
|
|
626
|
+
lasterr = LCB_OPTIONS_CONFLICT;
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
607
631
|
const Json::Value& j_statement = json_const()["statement"];
|
|
608
632
|
if (j_statement.isString()) {
|
|
609
633
|
statement = j_statement.asString();
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
#define LOG(instance, lvlbase, msg) lcb_log(instance->settings, "newconfig", LCB_LOG_##lvlbase, __FILE__, __LINE__, msg)
|
|
26
26
|
|
|
27
27
|
#define SERVER_FMT "%s:%s (%p)"
|
|
28
|
-
#define SERVER_ARGS(s) (s)->
|
|
28
|
+
#define SERVER_ARGS(s) (s)->get_host().host, (s)->get_host().port, (void *)s
|
|
29
29
|
|
|
30
30
|
typedef struct lcb_GUESSVB_st {
|
|
31
31
|
time_t last_update; /**< Last time this vBucket was heuristically set */
|
|
@@ -106,8 +106,8 @@ lcb_vbguess_remap(lcb_t instance, int vbid, int bad)
|
|
|
106
106
|
lcb_GUESSVB *guess = guesses + vbid;
|
|
107
107
|
int newix = lcbvb_nmv_remap_ex(LCBT_VBCONFIG(instance), vbid, bad, 1);
|
|
108
108
|
if (!guesses) {
|
|
109
|
-
guesses = instance->vbguess = calloc(
|
|
110
|
-
LCBT_VBCONFIG(instance)->nvb, sizeof *guesses);
|
|
109
|
+
guesses = instance->vbguess = reinterpret_cast<lcb_GUESSVB*>(calloc(
|
|
110
|
+
LCBT_VBCONFIG(instance)->nvb, sizeof *guesses));
|
|
111
111
|
}
|
|
112
112
|
if (newix > -1 && newix != bad) {
|
|
113
113
|
guess->newix = newix;
|
|
@@ -136,18 +136,17 @@ lcb_vbguess_remap(lcb_t instance, int vbid, int bad)
|
|
|
136
136
|
*/
|
|
137
137
|
static int
|
|
138
138
|
find_new_data_index(lcbvb_CONFIG *oldconfig, lcbvb_CONFIG* newconfig,
|
|
139
|
-
|
|
139
|
+
lcb::Server *server)
|
|
140
140
|
{
|
|
141
|
-
size_t ii;
|
|
142
141
|
const char *old_datahost = lcbvb_get_hostport(oldconfig,
|
|
143
|
-
server->
|
|
142
|
+
server->get_index(), LCBVB_SVCTYPE_DATA, LCBVB_SVCMODE_PLAIN);
|
|
144
143
|
|
|
145
144
|
if (!old_datahost) {
|
|
146
145
|
/* Old server had no data service */
|
|
147
146
|
return -1;
|
|
148
147
|
}
|
|
149
148
|
|
|
150
|
-
for (ii = 0; ii < LCBVB_NSERVERS(newconfig); ii++) {
|
|
149
|
+
for (size_t ii = 0; ii < LCBVB_NSERVERS(newconfig); ii++) {
|
|
151
150
|
const char *new_datahost = lcbvb_get_hostport(newconfig, ii,
|
|
152
151
|
LCBVB_SVCTYPE_DATA, LCBVB_SVCMODE_PLAIN);
|
|
153
152
|
if (new_datahost && strcmp(new_datahost, old_datahost) == 0) {
|
|
@@ -160,15 +159,14 @@ find_new_data_index(lcbvb_CONFIG *oldconfig, lcbvb_CONFIG* newconfig,
|
|
|
160
159
|
static void
|
|
161
160
|
log_vbdiff(lcb_t instance, lcbvb_CONFIGDIFF *diff)
|
|
162
161
|
{
|
|
163
|
-
char **curserver;
|
|
164
162
|
lcb_log(LOGARGS(instance, INFO), "Config Diff: [ vBuckets Modified=%d ], [Sequence Changed=%d]", diff->n_vb_changes, diff->sequence_changed);
|
|
165
163
|
if (diff->servers_added) {
|
|
166
|
-
for (curserver = diff->servers_added; *curserver; curserver++) {
|
|
164
|
+
for (char **curserver = diff->servers_added; *curserver; curserver++) {
|
|
167
165
|
lcb_log(LOGARGS(instance, INFO), "Detected server %s added", *curserver);
|
|
168
166
|
}
|
|
169
167
|
}
|
|
170
168
|
if (diff->servers_removed) {
|
|
171
|
-
for (curserver = diff->servers_removed; *curserver; curserver++) {
|
|
169
|
+
for (char **curserver = diff->servers_removed; *curserver; curserver++) {
|
|
172
170
|
lcb_log(LOGARGS(instance, INFO), "Detected server %s removed", *curserver);
|
|
173
171
|
}
|
|
174
172
|
}
|
|
@@ -187,19 +185,15 @@ log_vbdiff(lcb_t instance, lcbvb_CONFIGDIFF *diff)
|
|
|
187
185
|
* another server.
|
|
188
186
|
*/
|
|
189
187
|
static int
|
|
190
|
-
iterwipe_cb(mc_CMDQUEUE *cq, mc_PIPELINE *oldpl, mc_PACKET *oldpkt, void *
|
|
188
|
+
iterwipe_cb(mc_CMDQUEUE *cq, mc_PIPELINE *oldpl, mc_PACKET *oldpkt, void *)
|
|
191
189
|
{
|
|
192
190
|
protocol_binary_request_header hdr;
|
|
193
|
-
|
|
194
|
-
mc_PIPELINE *newpl;
|
|
195
|
-
mc_PACKET *newpkt;
|
|
191
|
+
lcb::Server *srv = static_cast<lcb::Server *>(oldpl);
|
|
196
192
|
int newix;
|
|
197
193
|
|
|
198
|
-
(void)arg;
|
|
199
|
-
|
|
200
194
|
mcreq_read_hdr(oldpkt, &hdr);
|
|
201
195
|
|
|
202
|
-
if (!lcb_should_retry(srv->
|
|
196
|
+
if (!lcb_should_retry(srv->get_settings(), oldpkt, LCB_MAX_ERROR)) {
|
|
203
197
|
return MCREQ_KEEP_PACKET;
|
|
204
198
|
}
|
|
205
199
|
|
|
@@ -222,23 +216,23 @@ iterwipe_cb(mc_CMDQUEUE *cq, mc_PIPELINE *oldpl, mc_PACKET *oldpkt, void *arg)
|
|
|
222
216
|
}
|
|
223
217
|
|
|
224
218
|
|
|
225
|
-
newpl = cq->pipelines[newix];
|
|
219
|
+
mc_PIPELINE *newpl = cq->pipelines[newix];
|
|
226
220
|
if (newpl == oldpl || newpl == NULL) {
|
|
227
221
|
return MCREQ_KEEP_PACKET;
|
|
228
222
|
}
|
|
229
223
|
|
|
230
|
-
lcb_log(LOGARGS((lcb_t)cq->cqdata, DEBUG), "Remapped packet %p (SEQ=%u) from "SERVER_FMT " to " SERVER_FMT,
|
|
231
|
-
(void*)oldpkt, oldpkt->opaque, SERVER_ARGS((
|
|
224
|
+
lcb_log(LOGARGS((lcb_t)cq->cqdata, DEBUG), "Remapped packet %p (SEQ=%u) from " SERVER_FMT " to " SERVER_FMT,
|
|
225
|
+
(void*)oldpkt, oldpkt->opaque, SERVER_ARGS((lcb::Server*)oldpl), SERVER_ARGS((lcb::Server*)newpl));
|
|
232
226
|
|
|
233
227
|
/** Otherwise, copy over the packet and find the new vBucket to map to */
|
|
234
|
-
newpkt = mcreq_renew_packet(oldpkt);
|
|
228
|
+
mc_PACKET *newpkt = mcreq_renew_packet(oldpkt);
|
|
235
229
|
newpkt->flags &= ~MCREQ_STATE_FLAGS;
|
|
236
230
|
mcreq_reenqueue_packet(newpl, newpkt);
|
|
237
231
|
mcreq_packet_handled(oldpl, oldpkt);
|
|
238
232
|
return MCREQ_REMOVE_PACKET;
|
|
239
233
|
}
|
|
240
234
|
|
|
241
|
-
static
|
|
235
|
+
static void
|
|
242
236
|
replace_config(lcb_t instance, lcbvb_CONFIG *oldconfig, lcbvb_CONFIG *newconfig)
|
|
243
237
|
{
|
|
244
238
|
mc_CMDQUEUE *cq = &instance->cmdq;
|
|
@@ -248,7 +242,7 @@ replace_config(lcb_t instance, lcbvb_CONFIG *oldconfig, lcbvb_CONFIG *newconfig)
|
|
|
248
242
|
assert(LCBT_VBCONFIG(instance) == newconfig);
|
|
249
243
|
|
|
250
244
|
nnew = LCBVB_NSERVERS(newconfig);
|
|
251
|
-
ppnew = calloc(nnew, sizeof(*ppnew));
|
|
245
|
+
ppnew = reinterpret_cast<mc_PIPELINE**>(calloc(nnew, sizeof(*ppnew)));
|
|
252
246
|
ppold = mcreq_queue_take_pipelines(cq, &nold);
|
|
253
247
|
|
|
254
248
|
/**
|
|
@@ -256,26 +250,25 @@ replace_config(lcb_t instance, lcbvb_CONFIG *oldconfig, lcbvb_CONFIG *newconfig)
|
|
|
256
250
|
* and place it inside the new list.
|
|
257
251
|
*/
|
|
258
252
|
for (ii = 0; ii < nold; ii++) {
|
|
259
|
-
|
|
253
|
+
lcb::Server *cur = static_cast<lcb::Server *>(ppold[ii]);
|
|
260
254
|
int newix = find_new_data_index(oldconfig, newconfig, cur);
|
|
261
255
|
if (newix > -1) {
|
|
262
|
-
cur->
|
|
263
|
-
ppnew[newix] =
|
|
256
|
+
cur->set_new_index(newix);
|
|
257
|
+
ppnew[newix] = cur;
|
|
264
258
|
ppold[ii] = NULL;
|
|
265
|
-
lcb_log(LOGARGS(instance, INFO), "Reusing server "SERVER_FMT". OldIndex=%d. NewIndex=%d", SERVER_ARGS(cur), ii, newix);
|
|
259
|
+
lcb_log(LOGARGS(instance, INFO), "Reusing server " SERVER_FMT ". OldIndex=%d. NewIndex=%d", SERVER_ARGS(cur), ii, newix);
|
|
266
260
|
}
|
|
267
261
|
}
|
|
268
262
|
|
|
269
263
|
/**
|
|
270
|
-
* Once we've moved the kept servers to the new list, allocate new
|
|
271
|
-
* structures for slots that don't have an existing
|
|
264
|
+
* Once we've moved the kept servers to the new list, allocate new lcb::Server
|
|
265
|
+
* structures for slots that don't have an existing lcb::Server. We must do
|
|
272
266
|
* this before add_pipelines() is called, so that there are no holes inside
|
|
273
267
|
* ppnew
|
|
274
268
|
*/
|
|
275
269
|
for (ii = 0; ii < nnew; ii++) {
|
|
276
270
|
if (!ppnew[ii]) {
|
|
277
|
-
ppnew[ii] =
|
|
278
|
-
ppnew[ii]->index = ii;
|
|
271
|
+
ppnew[ii] = new lcb::Server(instance, ii);
|
|
279
272
|
}
|
|
280
273
|
}
|
|
281
274
|
|
|
@@ -298,31 +291,28 @@ replace_config(lcb_t instance, lcbvb_CONFIG *oldconfig, lcbvb_CONFIG *newconfig)
|
|
|
298
291
|
}
|
|
299
292
|
|
|
300
293
|
mcreq_iterwipe(cq, ppold[ii], iterwipe_cb, NULL);
|
|
301
|
-
|
|
302
|
-
|
|
294
|
+
static_cast<lcb::Server*>(ppold[ii])->purge(LCB_MAP_CHANGED);
|
|
295
|
+
static_cast<lcb::Server*>(ppold[ii])->close();
|
|
303
296
|
}
|
|
304
297
|
|
|
305
298
|
for (ii = 0; ii < nnew; ii++) {
|
|
306
|
-
if (
|
|
299
|
+
if (static_cast<lcb::Server*>(ppnew[ii])->has_pending()) {
|
|
307
300
|
ppnew[ii]->flush_start(ppnew[ii]);
|
|
308
301
|
}
|
|
309
302
|
}
|
|
310
303
|
|
|
311
304
|
free(ppnew);
|
|
312
305
|
free(ppold);
|
|
313
|
-
return LCB_CONFIGURATION_CHANGED;
|
|
314
306
|
}
|
|
315
307
|
|
|
316
|
-
void lcb_update_vbconfig(lcb_t instance,
|
|
308
|
+
void lcb_update_vbconfig(lcb_t instance, lcb_pCONFIGINFO config)
|
|
317
309
|
{
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
clconfig_info *old_config;
|
|
310
|
+
lcb_configuration_t change_status;
|
|
311
|
+
lcb::clconfig::ConfigInfo *old_config = instance->cur_configinfo;
|
|
321
312
|
mc_CMDQUEUE *q = &instance->cmdq;
|
|
322
313
|
|
|
323
|
-
old_config = instance->cur_configinfo;
|
|
324
314
|
instance->cur_configinfo = config;
|
|
325
|
-
|
|
315
|
+
config->incref();
|
|
326
316
|
q->config = instance->cur_configinfo->vbc;
|
|
327
317
|
q->cqdata = instance;
|
|
328
318
|
|
|
@@ -337,49 +327,31 @@ void lcb_update_vbconfig(lcb_t instance, clconfig_info *config)
|
|
|
337
327
|
/* Apply the vb guesses */
|
|
338
328
|
lcb_vbguess_newconfig(instance, config->vbc, instance->vbguess);
|
|
339
329
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
lcb_clconfig_decref(old_config);
|
|
330
|
+
replace_config(instance, old_config->vbc, config->vbc);
|
|
331
|
+
old_config->decref();
|
|
332
|
+
change_status = LCB_CONFIGURATION_CHANGED;
|
|
346
333
|
} else {
|
|
347
|
-
|
|
348
|
-
mc_PIPELINE
|
|
349
|
-
nservers = VB_NSERVERS(config->vbc);
|
|
350
|
-
if ((servers = malloc(sizeof(*servers) * nservers)) == NULL) {
|
|
351
|
-
assert(servers);
|
|
352
|
-
lcb_log(LOGARGS(instance, FATAL), "Couldn't allocate memory for new server list! (n=%u)", nservers);
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
334
|
+
size_t nservers = VB_NSERVERS(config->vbc);
|
|
335
|
+
std::vector<mc_PIPELINE*> servers;
|
|
355
336
|
|
|
356
|
-
for (ii = 0; ii < nservers; ii++) {
|
|
357
|
-
|
|
358
|
-
if ((srv = mcserver_alloc(instance, ii)) == NULL) {
|
|
359
|
-
assert(srv);
|
|
360
|
-
lcb_log(LOGARGS(instance, FATAL), "Couldn't allocate memory for server instance!");
|
|
361
|
-
return;
|
|
362
|
-
}
|
|
363
|
-
servers[ii] = &srv->pipeline;
|
|
337
|
+
for (size_t ii = 0; ii < nservers; ii++) {
|
|
338
|
+
servers.push_back(new lcb::Server(instance, ii));
|
|
364
339
|
}
|
|
365
340
|
|
|
366
|
-
mcreq_queue_add_pipelines(q, servers, nservers, config->vbc);
|
|
341
|
+
mcreq_queue_add_pipelines(q, &servers[0], nservers, config->vbc);
|
|
367
342
|
change_status = LCB_CONFIGURATION_NEW;
|
|
368
|
-
free(servers);
|
|
369
343
|
}
|
|
370
344
|
|
|
371
345
|
/* Update the list of nodes here for server list */
|
|
372
|
-
|
|
373
|
-
for (ii = 0; ii < LCBVB_NSERVERS(config->vbc); ++ii) {
|
|
346
|
+
instance->ht_nodes->clear();
|
|
347
|
+
for (size_t ii = 0; ii < LCBVB_NSERVERS(config->vbc); ++ii) {
|
|
374
348
|
const char *hp = lcbvb_get_hostport(config->vbc, ii,
|
|
375
349
|
LCBVB_SVCTYPE_MGMT, LCBVB_SVCMODE_PLAIN);
|
|
376
350
|
if (hp) {
|
|
377
|
-
|
|
351
|
+
instance->ht_nodes->add(hp, LCB_CONFIG_HTTP_PORT);
|
|
378
352
|
}
|
|
379
353
|
}
|
|
380
354
|
|
|
381
355
|
instance->callbacks.configuration(instance, change_status);
|
|
382
356
|
lcb_maybe_breakout(instance);
|
|
383
357
|
}
|
|
384
|
-
|
|
385
|
-
|