libcouchbase 1.0.4 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
+ }