libcouchbase 1.0.4 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +11 -8
  3. data/ext/libcouchbase/CMakeLists.txt +1 -1
  4. data/ext/libcouchbase/README.markdown +38 -6
  5. data/ext/libcouchbase/RELEASE_NOTES.markdown +151 -0
  6. data/ext/libcouchbase/cmake/Modules/GenerateConfigDotH.cmake +2 -2
  7. data/ext/libcouchbase/cmake/Modules/GetVersionInfo.cmake +3 -3
  8. data/ext/libcouchbase/cmake/source_files.cmake +1 -0
  9. data/ext/libcouchbase/contrib/cJSON/cJSON.c +686 -288
  10. data/ext/libcouchbase/contrib/cJSON/cJSON.h +0 -0
  11. data/ext/libcouchbase/contrib/cbsasl/src/hash.c +17 -17
  12. data/ext/libcouchbase/contrib/cliopts/cliopts.c +76 -0
  13. data/ext/libcouchbase/contrib/cliopts/cliopts.h +66 -15
  14. data/ext/libcouchbase/contrib/genhash/genhash.c +1 -2
  15. data/ext/libcouchbase/contrib/lcb-jsoncpp/lcb-jsoncpp.cpp +4 -3
  16. data/ext/libcouchbase/example/instancepool/main.cc +12 -2
  17. data/ext/libcouchbase/example/libeventdirect/main.c +99 -25
  18. data/ext/libcouchbase/example/minimal/minimal.c +7 -5
  19. data/ext/libcouchbase/example/observe/durability.c +102 -0
  20. data/ext/libcouchbase/example/observe/observe.c +19 -6
  21. data/ext/libcouchbase/example/subdoc/subdoc-xattrs.c +1 -2
  22. data/ext/libcouchbase/include/libcouchbase/cntl-private.h +6 -8
  23. data/ext/libcouchbase/include/libcouchbase/cntl.h +84 -64
  24. data/ext/libcouchbase/include/libcouchbase/couchbase.h +295 -78
  25. data/ext/libcouchbase/include/libcouchbase/deprecated.h +2 -2
  26. data/ext/libcouchbase/include/libcouchbase/error.h +1 -1
  27. data/ext/libcouchbase/include/libcouchbase/iops.h +9 -9
  28. data/ext/libcouchbase/include/libcouchbase/ixmgmt.h +2 -2
  29. data/ext/libcouchbase/include/libcouchbase/n1ql.h +69 -7
  30. data/ext/libcouchbase/include/libcouchbase/vbucket.h +17 -0
  31. data/ext/libcouchbase/include/libcouchbase/views.h +3 -3
  32. data/ext/libcouchbase/include/memcached/protocol_binary.h +62 -1
  33. data/ext/libcouchbase/packaging/deb/control +1 -1
  34. data/ext/libcouchbase/packaging/rpm/libcouchbase.spec.in +37 -36
  35. data/ext/libcouchbase/src/bootstrap.cc +22 -8
  36. data/ext/libcouchbase/src/bucketconfig/bc_cccp.cc +1 -1
  37. data/ext/libcouchbase/src/bucketconfig/bc_http.cc +0 -1
  38. data/ext/libcouchbase/src/bucketconfig/confmon.cc +13 -8
  39. data/ext/libcouchbase/src/callbacks.c +2 -0
  40. data/ext/libcouchbase/src/cntl.cc +28 -17
  41. data/ext/libcouchbase/src/dns-srv.cc +1 -2
  42. data/ext/libcouchbase/src/dump.cc +4 -0
  43. data/ext/libcouchbase/src/errmap.h +89 -16
  44. data/ext/libcouchbase/src/handler.cc +28 -11
  45. data/ext/libcouchbase/src/http/http-priv.h +4 -1
  46. data/ext/libcouchbase/src/http/http.cc +3 -0
  47. data/ext/libcouchbase/src/instance.cc +1 -1
  48. data/ext/libcouchbase/src/internal.h +1 -0
  49. data/ext/libcouchbase/src/lcbio/connect.cc +2 -3
  50. data/ext/libcouchbase/src/lcbio/manager.cc +2 -2
  51. data/ext/libcouchbase/src/lcbio/ssl.h +10 -0
  52. data/ext/libcouchbase/src/mc/mcreq.c +8 -0
  53. data/ext/libcouchbase/src/mcserver/mcserver.cc +14 -1
  54. data/ext/libcouchbase/src/n1ql/ixmgmt.cc +0 -3
  55. data/ext/libcouchbase/src/n1ql/n1ql.cc +22 -29
  56. data/ext/libcouchbase/src/n1ql/params.cc +46 -1
  57. data/ext/libcouchbase/src/newconfig.cc +4 -4
  58. data/ext/libcouchbase/src/operations/durability-seqno.cc +4 -0
  59. data/ext/libcouchbase/src/operations/durability.cc +3 -0
  60. data/ext/libcouchbase/src/operations/ping.cc +315 -0
  61. data/ext/libcouchbase/src/operations/stats.cc +10 -0
  62. data/ext/libcouchbase/src/operations/subdoc.cc +13 -1
  63. data/ext/libcouchbase/src/retrychk.cc +1 -0
  64. data/ext/libcouchbase/src/settings.c +2 -0
  65. data/ext/libcouchbase/src/settings.h +13 -7
  66. data/ext/libcouchbase/src/ssl/ssl_c.c +28 -2
  67. data/ext/libcouchbase/src/ssl/ssl_common.c +3 -0
  68. data/ext/libcouchbase/src/ssl/ssl_e.c +15 -1
  69. data/ext/libcouchbase/src/ssl/ssl_iot_common.h +3 -1
  70. data/ext/libcouchbase/src/timings.c +0 -1
  71. data/ext/libcouchbase/src/vbucket/vbucket.c +49 -1
  72. data/ext/libcouchbase/tests/iotests/mock-environment.cc +58 -40
  73. data/ext/libcouchbase/tests/iotests/mock-environment.h +23 -4
  74. data/ext/libcouchbase/tests/iotests/mock-unit-test.h +8 -8
  75. data/ext/libcouchbase/tests/iotests/t_behavior.cc +5 -5
  76. data/ext/libcouchbase/tests/iotests/t_durability.cc +50 -0
  77. data/ext/libcouchbase/tests/iotests/t_eerrs.cc +4 -2
  78. data/ext/libcouchbase/tests/iotests/t_errmap.cc +6 -3
  79. data/ext/libcouchbase/tests/iotests/t_lock.cc +5 -6
  80. data/ext/libcouchbase/tests/iotests/t_misc.cc +44 -0
  81. data/ext/libcouchbase/tests/iotests/t_serverops.cc +1 -0
  82. data/ext/libcouchbase/tests/iotests/t_subdoc.cc +28 -0
  83. data/ext/libcouchbase/tests/iotests/t_views.cc +22 -10
  84. data/ext/libcouchbase/tools/CMakeLists.txt +21 -1
  85. data/ext/libcouchbase/tools/cbc-handlers.h +23 -3
  86. data/ext/libcouchbase/tools/cbc-n1qlback.cc +1 -1
  87. data/ext/libcouchbase/tools/cbc-pillowfight.cc +126 -26
  88. data/ext/libcouchbase/tools/cbc-proxy.cc +403 -0
  89. data/ext/libcouchbase/tools/cbc-subdoc.cc +826 -0
  90. data/ext/libcouchbase/tools/cbc.cc +149 -37
  91. data/ext/libcouchbase/tools/common/options.h +5 -2
  92. data/ext/libcouchbase/tools/linenoise/linenoise.c +15 -15
  93. data/lib/libcouchbase.rb +4 -0
  94. data/lib/libcouchbase/bucket.rb +51 -0
  95. data/lib/libcouchbase/connection.rb +100 -13
  96. data/lib/libcouchbase/ext/libcouchbase.rb +40 -0
  97. data/lib/libcouchbase/ext/libcouchbase/cmdsubdoc.rb +13 -1
  98. data/lib/libcouchbase/ext/libcouchbase/enums.rb +2 -1
  99. data/lib/libcouchbase/ext/libcouchbase/sdspec.rb +5 -0
  100. data/lib/libcouchbase/subdoc_request.rb +129 -0
  101. data/lib/libcouchbase/version.rb +1 -1
  102. data/spec/bucket_spec.rb +15 -1
  103. data/spec/connection_spec.rb +1 -1
  104. data/spec/subdoc_spec.rb +192 -0
  105. metadata +13 -4
  106. data/ext/libcouchbase/.travis.yml +0 -19
@@ -0,0 +1,826 @@
1
+ /*
2
+ * Copyright 2017 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
+ #define LCB_NO_DEPR_CXX_CTORS
18
+
19
+ #include "common/my_inttypes.h"
20
+ #include "config.h"
21
+ #include <sys/types.h>
22
+ #include <libcouchbase/couchbase.h>
23
+ #include <libcouchbase/api3.h>
24
+ #include <iostream>
25
+ #include <map>
26
+ #include <cassert>
27
+ #include <cstdio>
28
+ #include <cerrno>
29
+ #include <stdexcept>
30
+ #include <sstream>
31
+ #include "common/options.h"
32
+ #include "common/histogram.h"
33
+
34
+ #include "linenoise/linenoise.h"
35
+
36
+ static std::string get_resp_key(const lcb_RESPBASE *resp)
37
+ {
38
+ if (!resp->nkey) {
39
+ return "";
40
+ }
41
+ return std::string((const char *)resp->key, resp->nkey);
42
+ }
43
+
44
+ extern "C" {
45
+ void subdoc_callback(lcb_t, int cbtype, const lcb_RESPBASE *rb)
46
+ {
47
+ const lcb_RESPSUBDOC *resp = (const lcb_RESPSUBDOC *)rb;
48
+ lcb_SDENTRY cur;
49
+ std::string key = get_resp_key(rb);
50
+
51
+ if (rb->rc == LCB_SUCCESS || rb->rc == LCB_SUBDOC_MULTI_FAILURE) {
52
+ fprintf(stderr, "%-20s CAS=0x%" PRIx64 "\n", key.c_str(), resp->cas);
53
+ } else {
54
+ fprintf(stderr, "%-20s %s (0x%x)\n", key.c_str(), lcb_strerror(NULL, rb->rc), rb->rc);
55
+ const char *ctx = lcb_resp_get_error_context(cbtype, rb);
56
+ if (ctx != NULL) {
57
+ fprintf(stderr, "%-20s %s\n", "", ctx);
58
+ }
59
+ const char *ref = lcb_resp_get_error_ref(cbtype, rb);
60
+ if (ref != NULL) {
61
+ fprintf(stderr, "%-20s Ref: %s\n", "", ref);
62
+ }
63
+ }
64
+ size_t vii = 0, oix = 0;
65
+ while (lcb_sdresult_next(resp, &cur, &vii)) {
66
+ int index = oix++;
67
+
68
+ if (cbtype == LCB_CALLBACK_SDMUTATE) {
69
+ index = cur.index;
70
+ }
71
+ printf("%d. Size=%lu, RC=0x%02x %s\n", index, (unsigned long)cur.nvalue, cur.status,
72
+ lcb_strerror(NULL, cur.status));
73
+ fflush(stdout);
74
+ if (cur.nvalue > 0) {
75
+ fwrite(cur.value, 1, cur.nvalue, stdout);
76
+ printf("\n");
77
+ fflush(stdout);
78
+ }
79
+ }
80
+ }
81
+ }
82
+
83
+ #define CBCSUBDOC_HISTORY_FILENAME ".cbcsubdoc_history"
84
+
85
+ using namespace cbc;
86
+ using namespace cliopts;
87
+
88
+ static void do_or_die(lcb_error_t rc, std::string msg = "")
89
+ {
90
+ if (rc != LCB_SUCCESS) {
91
+ std::stringstream ss;
92
+ if (!msg.empty()) {
93
+ ss << msg << ". ";
94
+ }
95
+ ss << "(0x" << std::hex << rc << ") " << lcb_strerror(NULL, rc);
96
+ throw std::runtime_error(ss.str());
97
+ }
98
+ }
99
+
100
+ static lcb_t instance = NULL;
101
+ static Histogram hg;
102
+
103
+ class Configuration
104
+ {
105
+ public:
106
+ Configuration()
107
+ {
108
+ }
109
+
110
+ ~Configuration()
111
+ {
112
+ }
113
+
114
+ void addToParser(Parser &parser)
115
+ {
116
+ m_params.addToParser(parser);
117
+ }
118
+
119
+ void processOptions()
120
+ {
121
+ }
122
+
123
+ void fillCropts(lcb_create_st &opts)
124
+ {
125
+ m_params.fillCropts(opts);
126
+ }
127
+ lcb_error_t doCtls()
128
+ {
129
+ return m_params.doCtls(instance);
130
+ }
131
+ bool useTimings()
132
+ {
133
+ return m_params.useTimings();
134
+ }
135
+ bool shouldDump()
136
+ {
137
+ return m_params.shouldDump();
138
+ }
139
+
140
+ private:
141
+ ConnParams m_params;
142
+ };
143
+
144
+ static Configuration config;
145
+
146
+ static const char *handlers_sorted[] = {"help",
147
+ "get",
148
+ "set",
149
+ "exists",
150
+ "remove",
151
+ "replace",
152
+ "array-insert",
153
+ "array-add-first",
154
+ "array-add-last",
155
+ "array-add-unique",
156
+ "dict-add",
157
+ "dict-upsert",
158
+ "counter",
159
+ "size",
160
+ NULL};
161
+
162
+ static void command_completion(const char *buf, linenoiseCompletions *lc)
163
+ {
164
+ size_t nbuf = strlen(buf);
165
+ for (const char **cur = handlers_sorted; *cur; cur++) {
166
+ if (memcmp(buf, *cur, nbuf) == 0) {
167
+ linenoiseAddCompletion(lc, *cur);
168
+ }
169
+ }
170
+ }
171
+
172
+ namespace subdoc
173
+ {
174
+ class Handler;
175
+ }
176
+
177
+ static std::map< std::string, subdoc::Handler * > handlers;
178
+
179
+ namespace subdoc
180
+ {
181
+ #define HANDLER_DESCRIPTION(s) \
182
+ const char *description() const \
183
+ { \
184
+ return s; \
185
+ }
186
+ #define HANDLER_USAGE(s) \
187
+ const char *usagestr() const \
188
+ { \
189
+ return s; \
190
+ }
191
+
192
+ class Handler
193
+ {
194
+ public:
195
+ Handler(const char *name) : parser(name)
196
+ {
197
+ if (name != NULL) {
198
+ cmdname = name;
199
+ }
200
+ parser.default_settings.error_noexit = 1;
201
+ parser.default_settings.help_noexit = 1;
202
+ }
203
+
204
+ virtual ~Handler()
205
+ {
206
+ }
207
+ virtual const char *description() const
208
+ {
209
+ return NULL;
210
+ }
211
+ virtual const char *usagestr() const
212
+ {
213
+ return NULL;
214
+ }
215
+ void execute(int argc, char **argv)
216
+ {
217
+ parser.reset();
218
+ parser.default_settings.argstring = usagestr();
219
+ parser.default_settings.shortdesc = description();
220
+ addOptions();
221
+ parser.parse(argc, argv, true);
222
+ run();
223
+ }
224
+
225
+ protected:
226
+ virtual const std::string &getLoneArg(bool required = false)
227
+ {
228
+ static std::string empty("");
229
+
230
+ const std::vector< std::string > &args = parser.getRestArgs();
231
+ if (args.empty() || args.size() != 1) {
232
+ if (required) {
233
+ throw BadArg("Command requires single argument");
234
+ }
235
+ return empty;
236
+ }
237
+ return args[0];
238
+ }
239
+
240
+ virtual const std::string &getRequiredArg()
241
+ {
242
+ return getLoneArg(true);
243
+ }
244
+
245
+ virtual void addOptions()
246
+ {
247
+ }
248
+
249
+ virtual void run() = 0;
250
+
251
+ void splitNameValue(std::string &arg, const char **name, size_t *nname, const char **value, size_t *nvalue)
252
+ {
253
+ size_t sep = arg.find("=");
254
+ if (sep == std::string::npos) {
255
+ throw BadArg("Name and value have to be separated with '='");
256
+ }
257
+
258
+ const char *k = arg.c_str();
259
+ size_t nk = sep;
260
+ for (size_t j = nk - 1; j > 0; j--, nk--) {
261
+ if (k[j] != ' ' && k[j] != '\t') {
262
+ break;
263
+ }
264
+ }
265
+ if (nk == 0) {
266
+ throw BadArg("Name cannot be empty");
267
+ }
268
+
269
+ *name = k;
270
+ *nname = nk;
271
+ *value = arg.c_str() + sep + 1;
272
+ *nvalue = arg.size() - sep - 1;
273
+ }
274
+
275
+ cliopts::Parser parser;
276
+ std::string cmdname;
277
+ };
278
+
279
+ class LookupHandler : public Handler
280
+ {
281
+ public:
282
+ HANDLER_USAGE("[OPTIONS...] KEY...")
283
+
284
+ LookupHandler(const char *name, lcb_SUBDOCOP opcode, const char *description_)
285
+ : Handler(name), m_opcode(opcode), m_description(description_), o_paths("path"), o_xattrs("xattr"),
286
+ o_deleted("deleted")
287
+ {
288
+ o_paths.abbrev('p').argdesc("PATH").description("JSON path in the document");
289
+ o_xattrs.abbrev('x').argdesc("PATH").description("Access XATTR path (exentnded attributes)");
290
+ o_deleted.abbrev('d').description("Access XATTR attributes of deleted documents");
291
+ }
292
+
293
+ const char *description() const
294
+ {
295
+ return m_description;
296
+ }
297
+
298
+ protected:
299
+ void addOptions()
300
+ {
301
+ Handler::addOptions();
302
+ parser.addOption(o_paths.reset());
303
+ parser.addOption(o_xattrs.reset());
304
+ parser.addOption(o_deleted.reset());
305
+ }
306
+
307
+ void run()
308
+ {
309
+ lcb_error_t err;
310
+
311
+ const std::vector< std::string > &keys = parser.getRestArgs();
312
+ if (keys.empty()) {
313
+ throw BadArg("At least one key has to be specified");
314
+ }
315
+ std::vector< std::string > paths = o_paths.result();
316
+ std::vector< std::string > xattrs = o_xattrs.result();
317
+
318
+ if (m_opcode != LCB_SDCMD_GET) {
319
+ if (paths.empty() && xattrs.empty()) {
320
+ throw BadArg("At least one path has to be specified");
321
+ }
322
+ }
323
+
324
+ lcb_sched_enter(instance);
325
+ for (size_t ii = 0; ii < keys.size(); ++ii) {
326
+ const std::string &key = keys[ii];
327
+ lcb_CMDSUBDOC cmd = {0};
328
+
329
+ LCB_KREQ_SIMPLE(&cmd.key, key.c_str(), key.size());
330
+ cmd.nspecs = 0;
331
+ std::vector< lcb_SDSPEC > specs;
332
+
333
+ if (o_xattrs.passed()) {
334
+ for (size_t i = 0; i < xattrs.size(); i++) {
335
+ lcb_SDSPEC spec = lcb_SDSPEC();
336
+ spec.sdcmd = m_opcode;
337
+ spec.options |= LCB_SDSPEC_F_XATTRPATH;
338
+ if (o_deleted.passed()) {
339
+ spec.options |= LCB_SDSPEC_F_XATTR_DELETED_OK;
340
+ }
341
+ LCB_SDSPEC_SET_PATH(&spec, xattrs[i].c_str(), xattrs[i].size());
342
+ specs.push_back(spec);
343
+ }
344
+ }
345
+ if (o_paths.passed()) {
346
+ for (size_t i = 0; i < paths.size(); i++) {
347
+ lcb_SDSPEC spec = lcb_SDSPEC();
348
+ spec.sdcmd = m_opcode;
349
+ LCB_SDSPEC_SET_PATH(&spec, paths[i].c_str(), paths[i].size());
350
+ specs.push_back(spec);
351
+ }
352
+ } else if (m_opcode == LCB_SDCMD_GET) {
353
+ lcb_SDSPEC spec = lcb_SDSPEC();
354
+ spec.sdcmd = LCB_SDCMD_GET_FULLDOC;
355
+ specs.push_back(spec);
356
+ }
357
+ cmd.specs = specs.data();
358
+ cmd.nspecs = specs.size();
359
+ err = lcb_subdoc3(instance, this, &cmd);
360
+ if (err != LCB_SUCCESS) {
361
+ throw LcbError(err, "Failed to schedule " + cmdname + " command");
362
+ }
363
+ }
364
+ lcb_sched_leave(instance);
365
+ err = lcb_wait(instance);
366
+ if (err != LCB_SUCCESS) {
367
+ throw LcbError(err, "Failed to execute " + cmdname + " command");
368
+ }
369
+ }
370
+
371
+ protected:
372
+ lcb_SUBDOCOP m_opcode;
373
+ const char *m_description;
374
+
375
+ cliopts::ListOption o_paths;
376
+ cliopts::ListOption o_xattrs;
377
+ cliopts::BoolOption o_deleted;
378
+ };
379
+
380
+ class RemoveHandler : public Handler
381
+ {
382
+ public:
383
+ HANDLER_DESCRIPTION("Remove path in the item on the server")
384
+ HANDLER_USAGE("[OPTIONS...] KEY...")
385
+
386
+ RemoveHandler(const char *name = "remove") : Handler(name), o_paths("path"), o_xattrs("xattr")
387
+ {
388
+ o_paths.abbrev('p').argdesc("PATH").description(
389
+ "JSON path in the document. When skipped, the operation applied to full document.");
390
+ o_xattrs.abbrev('x').argdesc("PATH").description("Access XATTR path (exentnded attributes)");
391
+ }
392
+
393
+ protected:
394
+ void addOptions()
395
+ {
396
+ Handler::addOptions();
397
+ parser.addOption(o_paths.reset());
398
+ parser.addOption(o_xattrs.reset());
399
+ }
400
+
401
+ void run()
402
+ {
403
+ lcb_error_t err;
404
+
405
+ const std::vector< std::string > &keys = parser.getRestArgs();
406
+ if (keys.empty()) {
407
+ throw BadArg("At least one key has to be specified");
408
+ }
409
+ std::vector< std::string > paths = o_paths.result();
410
+ std::vector< std::string > xattrs = o_xattrs.result();
411
+
412
+ lcb_sched_enter(instance);
413
+ for (size_t ii = 0; ii < keys.size(); ++ii) {
414
+ const std::string &key = keys[ii];
415
+ lcb_CMDSUBDOC cmd = {0};
416
+
417
+ LCB_KREQ_SIMPLE(&cmd.key, key.c_str(), key.size());
418
+ std::vector< lcb_SDSPEC > specs;
419
+
420
+ if (o_xattrs.passed()) {
421
+ for (size_t i = 0; i < xattrs.size(); i++) {
422
+ lcb_SDSPEC spec = lcb_SDSPEC();
423
+ spec.sdcmd = LCB_SDCMD_REMOVE;
424
+ spec.options |= LCB_SDSPEC_F_XATTRPATH;
425
+ LCB_SDSPEC_SET_PATH(&spec, xattrs[i].c_str(), xattrs[i].size());
426
+ specs.push_back(spec);
427
+ }
428
+ }
429
+ if (o_paths.passed()) {
430
+ for (size_t i = 0; i < paths.size(); i++) {
431
+ lcb_SDSPEC spec = lcb_SDSPEC();
432
+ spec.sdcmd = LCB_SDCMD_REMOVE;
433
+ LCB_SDSPEC_SET_PATH(&spec, paths[i].c_str(), paths[i].size());
434
+ specs.push_back(spec);
435
+ }
436
+ } else {
437
+ lcb_SDSPEC spec = lcb_SDSPEC();
438
+ spec.sdcmd = LCB_SDCMD_REMOVE_FULLDOC;
439
+ specs.push_back(spec);
440
+ }
441
+ cmd.specs = specs.data();
442
+ cmd.nspecs = specs.size();
443
+ err = lcb_subdoc3(instance, this, &cmd);
444
+ if (err != LCB_SUCCESS) {
445
+ throw LcbError(err, "Failed to schedule remove command");
446
+ }
447
+ }
448
+ lcb_sched_leave(instance);
449
+ err = lcb_wait(instance);
450
+ if (err != LCB_SUCCESS) {
451
+ throw LcbError(err, "Failed to execute remove");
452
+ }
453
+ }
454
+
455
+ protected:
456
+ cliopts::ListOption o_paths;
457
+ cliopts::ListOption o_xattrs;
458
+ };
459
+
460
+ class UpsertHandler : public Handler
461
+ {
462
+ public:
463
+ HANDLER_DESCRIPTION("Store document on the server")
464
+ HANDLER_USAGE("[OPTIONS...] KEY VALUE")
465
+
466
+ UpsertHandler(const char *name = "upsert") : Handler(name), o_xattrs("xattr"), o_expiry("expiry")
467
+ {
468
+ o_xattrs.abbrev('x').argdesc("PATH=VALUE").description("Store XATTR path (exentnded attributes)");
469
+ o_expiry.abbrev('e').argdesc("TIME").description(
470
+ "Expiration time in seconds. Relative (up to 30 days) or absolute (as Unix timestamp)");
471
+ }
472
+
473
+ protected:
474
+ void addOptions()
475
+ {
476
+ Handler::addOptions();
477
+ parser.addOption(o_xattrs.reset());
478
+ parser.addOption(o_expiry.reset());
479
+ }
480
+
481
+ void run()
482
+ {
483
+ lcb_error_t err;
484
+
485
+ const std::vector< std::string > &args = parser.getRestArgs();
486
+ if (args.size() != 2) {
487
+ throw BadArg("Exactly two arguments required: KEY and VALUE");
488
+ }
489
+ // currently it is not possible to upsert document without XATTRs
490
+ // so lets allocate "_cbc" object with some useful stuff
491
+ std::string ver = "\"libcouchbase/" LCB_VERSION_STRING "\"";
492
+ std::string path = "_cbc.version";
493
+
494
+ std::string key = args[0];
495
+ std::string value = args[1];
496
+ std::vector< std::pair< std::string, std::string > > xattrs = o_xattrs.result();
497
+
498
+ std::vector< lcb_SDSPEC > specs;
499
+ lcb_CMDSUBDOC cmd = {0};
500
+
501
+ LCB_KREQ_SIMPLE(&cmd.key, key.c_str(), key.size());
502
+ cmd.cmdflags = LCB_CMDSUBDOC_F_UPSERT_DOC;
503
+
504
+ if (o_xattrs.passed()) {
505
+ for (size_t i = 0; i < xattrs.size(); i++) {
506
+ lcb_SDSPEC spec = lcb_SDSPEC();
507
+ spec.sdcmd = LCB_SDCMD_DICT_UPSERT;
508
+ spec.options = LCB_SDSPEC_F_XATTRPATH | LCB_SDSPEC_F_MKINTERMEDIATES;
509
+ LCB_SDSPEC_SET_PATH(&spec, xattrs[i].first.c_str(), xattrs[i].first.size());
510
+ LCB_SDSPEC_SET_VALUE(&spec, xattrs[i].second.c_str(), xattrs[i].second.size());
511
+ specs.push_back(spec);
512
+ }
513
+ } else {
514
+ lcb_SDSPEC spec = lcb_SDSPEC();
515
+ spec.sdcmd = LCB_SDCMD_DICT_UPSERT;
516
+ spec.options = LCB_SDSPEC_F_XATTRPATH | LCB_SDSPEC_F_MKINTERMEDIATES;
517
+ LCB_SDSPEC_SET_PATH(&spec, path.c_str(), path.size());
518
+ LCB_SDSPEC_SET_VALUE(&spec, ver.c_str(), ver.size());
519
+ specs.push_back(spec);
520
+ }
521
+ {
522
+ lcb_SDSPEC spec = lcb_SDSPEC();
523
+ spec.sdcmd = LCB_SDCMD_SET_FULLDOC;
524
+ LCB_SDSPEC_SET_VALUE(&spec, value.c_str(), value.size());
525
+ specs.push_back(spec);
526
+ }
527
+ cmd.specs = specs.data();
528
+ cmd.nspecs = specs.size();
529
+ if (o_expiry.passed()) {
530
+ cmd.exptime = o_expiry.result();
531
+ }
532
+
533
+ lcb_sched_enter(instance);
534
+ err = lcb_subdoc3(instance, this, &cmd);
535
+ if (err != LCB_SUCCESS) {
536
+ throw LcbError(err, "Failed to schedule upsert command");
537
+ }
538
+ lcb_sched_leave(instance);
539
+
540
+ err = lcb_wait(instance);
541
+ if (err != LCB_SUCCESS) {
542
+ throw LcbError(err, "Failed to execute upsert");
543
+ }
544
+ }
545
+
546
+ protected:
547
+ cliopts::PairListOption o_xattrs;
548
+ cliopts::UIntOption o_expiry;
549
+ };
550
+
551
+ class MutationHandler : public Handler
552
+ {
553
+ public:
554
+ HANDLER_USAGE("[OPTIONS...] KEY...")
555
+
556
+ MutationHandler(const char *name, lcb_SUBDOCOP opcode, const char *description_, bool enable_intermediates = true)
557
+ : Handler(name), m_opcode(opcode), m_description(description_), o_paths("path"), o_xattrs("xattr"),
558
+ o_expiry("expiry"), o_intermediates("intermediates"), o_upsert("upsert"),
559
+ m_enable_intermediates(enable_intermediates)
560
+ {
561
+ o_paths.abbrev('p').argdesc("PATH=VALUE").description("JSON path in the document");
562
+ o_xattrs.abbrev('x').argdesc("PATH=VALUE").description("XATTR path (exentnded attributes)");
563
+ o_expiry.abbrev('e').argdesc("TIME").description(
564
+ "Expiration time in seconds. Relative (up to 30 days) or absolute (as Unix timestamp)");
565
+ o_intermediates.abbrev('i').description("Create intermediate paths");
566
+ o_upsert.abbrev('u').description("Create document if it doesn't exist");
567
+ }
568
+
569
+ const char *description() const
570
+ {
571
+ return m_description;
572
+ }
573
+
574
+ protected:
575
+ void addOptions()
576
+ {
577
+ Handler::addOptions();
578
+ parser.addOption(o_xattrs.reset());
579
+ parser.addOption(o_paths.reset());
580
+ parser.addOption(o_expiry.reset());
581
+ parser.addOption(o_upsert.reset());
582
+ if (m_enable_intermediates) {
583
+ parser.addOption(o_intermediates.reset());
584
+ }
585
+ }
586
+
587
+ void run()
588
+ {
589
+ lcb_error_t err;
590
+
591
+ const std::vector< std::string > &keys = parser.getRestArgs();
592
+ if (keys.empty()) {
593
+ throw BadArg("At least one key has to be specified");
594
+ }
595
+ std::vector< std::pair< std::string, std::string > > paths = o_paths.result();
596
+ std::vector< std::pair< std::string, std::string > > xattrs = o_xattrs.result();
597
+
598
+ if (xattrs.empty() && paths.empty()) {
599
+ throw BadArg("At least one path has to be specified");
600
+ }
601
+
602
+ lcb_sched_enter(instance);
603
+
604
+ for (size_t ii = 0; ii < keys.size(); ++ii) {
605
+ const std::string &key = keys[ii];
606
+ lcb_CMDSUBDOC cmd = {0};
607
+ std::vector< lcb_SDSPEC > specs;
608
+
609
+ LCB_KREQ_SIMPLE(&cmd.key, key.c_str(), key.size());
610
+ if (o_upsert.passed()) {
611
+ cmd.cmdflags = LCB_CMDSUBDOC_F_UPSERT_DOC;
612
+ }
613
+ if (o_xattrs.passed()) {
614
+ for (size_t i = 0; i < xattrs.size(); i++) {
615
+ lcb_SDSPEC spec = lcb_SDSPEC();
616
+ spec.sdcmd = m_opcode;
617
+ spec.options = LCB_SDSPEC_F_XATTRPATH;
618
+ if (o_intermediates.passed()) {
619
+ spec.options |= LCB_SDSPEC_F_MKINTERMEDIATES;
620
+ }
621
+ LCB_SDSPEC_SET_PATH(&spec, xattrs[i].first.c_str(), xattrs[i].first.size());
622
+ LCB_SDSPEC_SET_VALUE(&spec, xattrs[i].second.c_str(), xattrs[i].second.size());
623
+ specs.push_back(spec);
624
+ }
625
+ }
626
+ if (o_paths.passed()) {
627
+ for (size_t i = 0; i < paths.size(); i++) {
628
+ lcb_SDSPEC spec = lcb_SDSPEC();
629
+ spec.sdcmd = m_opcode;
630
+ spec.options = 0;
631
+ if (o_intermediates.passed()) {
632
+ spec.options |= LCB_SDSPEC_F_MKINTERMEDIATES;
633
+ }
634
+ LCB_SDSPEC_SET_PATH(&spec, paths[i].first.c_str(), paths[i].first.size());
635
+ LCB_SDSPEC_SET_VALUE(&spec, paths[i].second.c_str(), paths[i].second.size());
636
+ specs.push_back(spec);
637
+ }
638
+ }
639
+ cmd.specs = specs.data();
640
+ cmd.nspecs = specs.size();
641
+ if (o_expiry.passed()) {
642
+ cmd.exptime = o_expiry.result();
643
+ }
644
+ err = lcb_subdoc3(instance, this, &cmd);
645
+ if (err != LCB_SUCCESS) {
646
+ throw LcbError(err, "Failed to schedule " + cmdname + " command");
647
+ }
648
+ }
649
+ lcb_sched_leave(instance);
650
+
651
+ err = lcb_wait(instance);
652
+ if (err != LCB_SUCCESS) {
653
+ throw LcbError(err, "Failed to execute " + cmdname + " command");
654
+ }
655
+ }
656
+
657
+ protected:
658
+ lcb_SUBDOCOP m_opcode;
659
+ const char *m_description;
660
+
661
+ cliopts::PairListOption o_paths;
662
+ cliopts::PairListOption o_xattrs;
663
+ cliopts::UIntOption o_expiry;
664
+ cliopts::BoolOption o_intermediates;
665
+ cliopts::BoolOption o_upsert;
666
+
667
+ bool m_enable_intermediates;
668
+ };
669
+
670
+ class HelpHandler : public Handler
671
+ {
672
+ public:
673
+ HANDLER_DESCRIPTION("Show help")
674
+ HelpHandler() : Handler("help")
675
+ {
676
+ }
677
+
678
+ protected:
679
+ void run()
680
+ {
681
+ fprintf(stderr, "Usage: <command> [options]\n");
682
+ fprintf(stderr, "command may be:\n");
683
+ for (const char **cur = handlers_sorted; *cur; cur++) {
684
+ const Handler *handler = handlers[*cur];
685
+ fprintf(stderr, " %-20s", *cur);
686
+ fprintf(stderr, "%s\n", handler->description());
687
+ }
688
+ }
689
+ };
690
+ }
691
+
692
+ static void setupHandlers()
693
+ {
694
+ handlers["help"] = new subdoc::HelpHandler();
695
+ handlers["get"] = new subdoc::LookupHandler("get", LCB_SDCMD_GET, "Retrieve path from the item on the server");
696
+ handlers["exists"] =
697
+ new subdoc::LookupHandler("exists", LCB_SDCMD_EXISTS, "Check if path exists in the item on the server");
698
+ handlers["exist"] = handlers["exists"];
699
+ handlers["remove"] = new subdoc::RemoveHandler();
700
+ handlers["delete"] = handlers["remove"];
701
+ handlers["upsert"] = new subdoc::UpsertHandler();
702
+ handlers["set"] = handlers["upsert"];
703
+ handlers["dict-upsert"] =
704
+ new subdoc::MutationHandler("dict-upsert", LCB_SDCMD_DICT_UPSERT, "Unconditionally set the value at the path");
705
+ handlers["dict-add"] = new subdoc::MutationHandler(
706
+ "dict-add", LCB_SDCMD_DICT_ADD, "Add the value at the given path, if the given path does not exist");
707
+ handlers["replace"] =
708
+ new subdoc::MutationHandler("replace", LCB_SDCMD_REPLACE, "Replace the value at the specified path", false);
709
+ handlers["array-add-first"] =
710
+ new subdoc::MutationHandler("array-add-first", LCB_SDCMD_ARRAY_ADD_FIRST, "Prepend the value(s) to the array");
711
+ handlers["array-add-last"] =
712
+ new subdoc::MutationHandler("array-add-last", LCB_SDCMD_ARRAY_ADD_LAST, "Append the value(s) to the array");
713
+ handlers["array-add-unique"] = new subdoc::MutationHandler(
714
+ "array-add-unique", LCB_SDCMD_ARRAY_ADD_UNIQUE,
715
+ "Add the value to the array indicated by the path, if the value is not already in the array");
716
+ handlers["array-insert"] = new subdoc::MutationHandler(
717
+ "array-insert", LCB_SDCMD_ARRAY_INSERT,
718
+ "Add the value at the given array index. Path must include index, e.g. `my.list[4]`");
719
+ handlers["counter"] = new subdoc::MutationHandler(
720
+ "counter", LCB_SDCMD_COUNTER,
721
+ "Increment or decrement an existing numeric path. The value must be 64-bit integer");
722
+ handlers["size"] = new subdoc::LookupHandler("size", LCB_SDCMD_GET_COUNT,
723
+ "Count the number of elements in an array or dictionary");
724
+ handlers["get-count"] = handlers["size"];
725
+ }
726
+
727
+ static void cleanup()
728
+ {
729
+ std::map< std::string, subdoc::Handler * >::iterator iter = handlers.begin();
730
+
731
+ handlers["exists"] = NULL;
732
+ handlers["delete"] = NULL;
733
+ handlers["set"] = NULL;
734
+ handlers["get-count"] = NULL;
735
+
736
+ for (; iter != handlers.end(); iter++) {
737
+ if (iter->second) {
738
+ delete iter->second;
739
+ }
740
+ }
741
+
742
+ if (instance) {
743
+ if (config.shouldDump()) {
744
+ lcb_dump(instance, stderr, LCB_DUMP_ALL);
745
+ }
746
+ if (config.useTimings()) {
747
+ hg.write();
748
+ }
749
+ if (instance) {
750
+ lcb_destroy(instance);
751
+ }
752
+ }
753
+ }
754
+
755
+ static void real_main(int argc, char **argv)
756
+ {
757
+ std::string history_path = ConnParams::getUserHome() + CBCSUBDOC_HISTORY_FILENAME;
758
+ Parser parser;
759
+
760
+ config.addToParser(parser);
761
+ parser.parse(argc, argv);
762
+ config.processOptions();
763
+
764
+ lcb_create_st cropts;
765
+ memset(&cropts, 0, sizeof cropts);
766
+ config.fillCropts(cropts);
767
+ do_or_die(lcb_create(&instance, &cropts), "Failed to create connection");
768
+ config.doCtls();
769
+ do_or_die(lcb_connect(instance), "Failed to connect to cluster");
770
+ do_or_die(lcb_wait(instance), "Failed to wait for connection bootstrap");
771
+ do_or_die(lcb_get_bootstrap_status(instance), "Failed to bootstrap");
772
+ if (config.useTimings()) {
773
+ hg.install(instance, stdout);
774
+ }
775
+ setupHandlers();
776
+ std::atexit(cleanup);
777
+ lcb_install_callback3(instance, LCB_CALLBACK_SDLOOKUP, subdoc_callback);
778
+ lcb_install_callback3(instance, LCB_CALLBACK_SDMUTATE, subdoc_callback);
779
+
780
+ linenoiseSetCompletionCallback(command_completion);
781
+ linenoiseSetMultiLine(1);
782
+ linenoiseHistoryLoad(history_path.c_str());
783
+
784
+ char *line;
785
+ while ((line = linenoise("subdoc> ")) != NULL) {
786
+ if (line[0] != '\0') {
787
+ linenoiseHistoryAdd(line);
788
+ linenoiseHistorySave(history_path.c_str());
789
+
790
+ int cmd_argc = 0;
791
+ char **cmd_argv = NULL;
792
+ int rv = cliopts_split_args(line, &cmd_argc, &cmd_argv);
793
+ if (rv) {
794
+ fprintf(stderr, "Invalid input: unterminated single quote\n");
795
+ } else {
796
+ if (rv == 0 && cmd_argc > 0) {
797
+ char *cmd_name = cmd_argv[0];
798
+ subdoc::Handler *handler = handlers[cmd_name];
799
+ if (handler == NULL) {
800
+ fprintf(stderr, "Unknown command %s\n", cmd_name);
801
+ subdoc::HelpHandler().execute(cmd_argc, cmd_argv);
802
+ } else {
803
+ try {
804
+ handler->execute(cmd_argc, cmd_argv);
805
+ } catch (std::exception &err) {
806
+ fprintf(stderr, "%s\n", err.what());
807
+ }
808
+ }
809
+ free(cmd_argv);
810
+ }
811
+ }
812
+ }
813
+ free(line);
814
+ }
815
+ }
816
+
817
+ int main(int argc, char **argv)
818
+ {
819
+ try {
820
+ real_main(argc, argv);
821
+ return 0;
822
+ } catch (std::exception &exc) {
823
+ std::cerr << exc.what() << std::endl;
824
+ exit(EXIT_FAILURE);
825
+ }
826
+ }