couchbase 1.1.5 → 1.2.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/.gitignore +2 -1
  2. data/.travis.yml +12 -1
  3. data/HISTORY.markdown +112 -1
  4. data/README.markdown +149 -6
  5. data/couchbase.gemspec +5 -1
  6. data/ext/couchbase_ext/.gitignore +4 -0
  7. data/ext/couchbase_ext/arguments.c +973 -0
  8. data/ext/couchbase_ext/arithmetic.c +322 -0
  9. data/ext/couchbase_ext/bucket.c +1092 -0
  10. data/ext/couchbase_ext/couchbase_ext.c +618 -3247
  11. data/ext/couchbase_ext/couchbase_ext.h +519 -0
  12. data/ext/couchbase_ext/delete.c +167 -0
  13. data/ext/couchbase_ext/extconf.rb +24 -5
  14. data/ext/couchbase_ext/get.c +301 -0
  15. data/ext/couchbase_ext/gethrtime.c +124 -0
  16. data/ext/couchbase_ext/http.c +402 -0
  17. data/ext/couchbase_ext/observe.c +174 -0
  18. data/ext/couchbase_ext/result.c +126 -0
  19. data/ext/couchbase_ext/stats.c +169 -0
  20. data/ext/couchbase_ext/store.c +522 -0
  21. data/ext/couchbase_ext/timer.c +192 -0
  22. data/ext/couchbase_ext/touch.c +190 -0
  23. data/ext/couchbase_ext/unlock.c +180 -0
  24. data/ext/couchbase_ext/utils.c +471 -0
  25. data/ext/couchbase_ext/version.c +147 -0
  26. data/lib/action_dispatch/middleware/session/couchbase_store.rb +38 -0
  27. data/lib/active_support/cache/couchbase_store.rb +356 -0
  28. data/lib/couchbase.rb +24 -3
  29. data/lib/couchbase/bucket.rb +372 -9
  30. data/lib/couchbase/result.rb +26 -0
  31. data/lib/couchbase/utils.rb +59 -0
  32. data/lib/couchbase/version.rb +1 -1
  33. data/lib/couchbase/view.rb +305 -0
  34. data/lib/couchbase/view_row.rb +230 -0
  35. data/lib/ext/multi_json_fix.rb +47 -0
  36. data/lib/rack/session/couchbase.rb +104 -0
  37. data/tasks/compile.rake +5 -14
  38. data/test/setup.rb +6 -2
  39. data/test/test_arithmetic.rb +32 -2
  40. data/test/test_async.rb +18 -4
  41. data/test/test_bucket.rb +11 -1
  42. data/test/test_cas.rb +13 -3
  43. data/test/test_couchbase_rails_cache_store.rb +294 -0
  44. data/test/test_delete.rb +60 -3
  45. data/test/test_format.rb +28 -17
  46. data/test/test_get.rb +91 -14
  47. data/test/test_store.rb +31 -1
  48. data/test/{test_flush.rb → test_timer.rb} +11 -18
  49. data/test/test_touch.rb +33 -5
  50. data/test/test_unlock.rb +120 -0
  51. data/test/test_utils.rb +26 -0
  52. metadata +101 -8
@@ -0,0 +1,471 @@
1
+ /* vim: ft=c et ts=8 sts=4 sw=4 cino=
2
+ *
3
+ * Copyright 2011, 2012 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "couchbase_ext.h"
19
+
20
+ VALUE
21
+ cb_gc_protect(struct bucket_st *bucket, VALUE val)
22
+ {
23
+ rb_hash_aset(bucket->object_space, val|1, val);
24
+ return val;
25
+ }
26
+
27
+ VALUE
28
+ cb_gc_unprotect(struct bucket_st *bucket, VALUE val)
29
+ {
30
+ rb_funcall(bucket->object_space, id_delete, 1, val|1);
31
+ return val;
32
+ }
33
+
34
+ VALUE
35
+ cb_proc_call(VALUE recv, int argc, ...)
36
+ {
37
+ VALUE *argv;
38
+ va_list ar;
39
+ int arity;
40
+ int ii;
41
+
42
+ arity = FIX2INT(rb_funcall(recv, id_arity, 0));
43
+ if (arity < 0) {
44
+ arity = argc;
45
+ }
46
+ if (arity > 0) {
47
+ va_init_list(ar, argc);
48
+ argv = ALLOCA_N(VALUE, argc);
49
+ for (ii = 0; ii < arity; ++ii) {
50
+ if (ii < argc) {
51
+ argv[ii] = va_arg(ar, VALUE);
52
+ } else {
53
+ argv[ii] = Qnil;
54
+ }
55
+ }
56
+ va_end(ar);
57
+ } else {
58
+ argv = NULL;
59
+ }
60
+ return rb_funcall2(recv, id_call, arity, argv);
61
+ }
62
+
63
+ VALUE
64
+ cb_hash_delete(VALUE hash, VALUE key)
65
+ {
66
+ return rb_funcall(hash, id_delete, 1, key);
67
+ }
68
+
69
+ /* Helper to convert return code from libcouchbase to meaningful exception.
70
+ * Returns nil if the code considering successful and exception object
71
+ * otherwise. Store given string to exceptions as message, and also
72
+ * initialize +error+ attribute with given return code. */
73
+ VALUE
74
+ cb_check_error_with_status(lcb_error_t rc, const char *msg, VALUE key,
75
+ lcb_http_status_t status)
76
+ {
77
+ VALUE klass, exc, str;
78
+ char buf[300];
79
+
80
+ if (rc == LCB_SUCCESS || rc == LCB_AUTH_CONTINUE) {
81
+ return Qnil;
82
+ }
83
+ switch (rc) {
84
+ case LCB_AUTH_ERROR:
85
+ klass = eAuthError;
86
+ break;
87
+ case LCB_DELTA_BADVAL:
88
+ klass = eDeltaBadvalError;
89
+ break;
90
+ case LCB_E2BIG:
91
+ klass = eTooBigError;
92
+ break;
93
+ case LCB_EBUSY:
94
+ klass = eBusyError;
95
+ break;
96
+ case LCB_EINTERNAL:
97
+ klass = eInternalError;
98
+ break;
99
+ case LCB_EINVAL:
100
+ klass = eInvalidError;
101
+ break;
102
+ case LCB_ENOMEM:
103
+ klass = eNoMemoryError;
104
+ break;
105
+ case LCB_ERANGE:
106
+ klass = eRangeError;
107
+ break;
108
+ case LCB_ETMPFAIL:
109
+ klass = eTmpFailError;
110
+ break;
111
+ case LCB_KEY_EEXISTS:
112
+ klass = eKeyExistsError;
113
+ break;
114
+ case LCB_KEY_ENOENT:
115
+ klass = eNotFoundError;
116
+ break;
117
+ case LCB_LIBEVENT_ERROR:
118
+ klass = eLibeventError;
119
+ break;
120
+ case LCB_NETWORK_ERROR:
121
+ klass = eNetworkError;
122
+ break;
123
+ case LCB_NOT_MY_VBUCKET:
124
+ klass = eNotMyVbucketError;
125
+ break;
126
+ case LCB_NOT_STORED:
127
+ klass = eNotStoredError;
128
+ break;
129
+ case LCB_NOT_SUPPORTED:
130
+ klass = eNotSupportedError;
131
+ break;
132
+ case LCB_UNKNOWN_COMMAND:
133
+ klass = eUnknownCommandError;
134
+ break;
135
+ case LCB_UNKNOWN_HOST:
136
+ klass = eUnknownHostError;
137
+ break;
138
+ case LCB_PROTOCOL_ERROR:
139
+ klass = eProtocolError;
140
+ break;
141
+ case LCB_ETIMEDOUT:
142
+ klass = eTimeoutError;
143
+ break;
144
+ case LCB_CONNECT_ERROR:
145
+ klass = eConnectError;
146
+ break;
147
+ case LCB_BUCKET_ENOENT:
148
+ klass = eBucketNotFoundError;
149
+ break;
150
+ case LCB_CLIENT_ENOMEM:
151
+ klass = eClientNoMemoryError;
152
+ break;
153
+ case LCB_ERROR:
154
+ /* fall through */
155
+ default:
156
+ klass = eLibcouchbaseError;
157
+ }
158
+
159
+ str = rb_str_buf_new2(msg ? msg : "");
160
+ rb_str_buf_cat2(str, " (");
161
+ if (key != Qnil) {
162
+ snprintf(buf, 300, "key=\"%s\", ", RSTRING_PTR(key));
163
+ rb_str_buf_cat2(str, buf);
164
+ }
165
+ if (status > 0) {
166
+ const char *reason = NULL;
167
+ snprintf(buf, 300, "status=\"%d\"", status);
168
+ rb_str_buf_cat2(str, buf);
169
+ switch (status) {
170
+ case LCB_HTTP_STATUS_BAD_REQUEST:
171
+ reason = " (Bad Request)";
172
+ break;
173
+ case LCB_HTTP_STATUS_UNAUTHORIZED:
174
+ reason = " (Unauthorized)";
175
+ break;
176
+ case LCB_HTTP_STATUS_PAYMENT_REQUIRED:
177
+ reason = " (Payment Required)";
178
+ break;
179
+ case LCB_HTTP_STATUS_FORBIDDEN:
180
+ reason = " (Forbidden)";
181
+ break;
182
+ case LCB_HTTP_STATUS_NOT_FOUND:
183
+ reason = " (Not Found)";
184
+ break;
185
+ case LCB_HTTP_STATUS_METHOD_NOT_ALLOWED:
186
+ reason = " (Method Not Allowed)";
187
+ break;
188
+ case LCB_HTTP_STATUS_NOT_ACCEPTABLE:
189
+ reason = " (Not Acceptable)";
190
+ break;
191
+ case LCB_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED:
192
+ reason = " (Proxy Authentication Required)";
193
+ break;
194
+ case LCB_HTTP_STATUS_REQUEST_TIMEOUT:
195
+ reason = " (Request Timeout)";
196
+ break;
197
+ case LCB_HTTP_STATUS_CONFLICT:
198
+ reason = " (Conflict)";
199
+ break;
200
+ case LCB_HTTP_STATUS_GONE:
201
+ reason = " (Gone)";
202
+ break;
203
+ case LCB_HTTP_STATUS_LENGTH_REQUIRED:
204
+ reason = " (Length Required)";
205
+ break;
206
+ case LCB_HTTP_STATUS_PRECONDITION_FAILED:
207
+ reason = " (Precondition Failed)";
208
+ break;
209
+ case LCB_HTTP_STATUS_REQUEST_ENTITY_TOO_LARGE:
210
+ reason = " (Request Entity Too Large)";
211
+ break;
212
+ case LCB_HTTP_STATUS_REQUEST_URI_TOO_LONG:
213
+ reason = " (Request Uri Too Long)";
214
+ break;
215
+ case LCB_HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE:
216
+ reason = " (Unsupported Media Type)";
217
+ break;
218
+ case LCB_HTTP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE:
219
+ reason = " (Requested Range Not Satisfiable)";
220
+ break;
221
+ case LCB_HTTP_STATUS_EXPECTATION_FAILED:
222
+ reason = " (Expectation Failed)";
223
+ break;
224
+ case LCB_HTTP_STATUS_UNPROCESSABLE_ENTITY:
225
+ reason = " (Unprocessable Entity)";
226
+ break;
227
+ case LCB_HTTP_STATUS_LOCKED:
228
+ reason = " (Locked)";
229
+ break;
230
+ case LCB_HTTP_STATUS_FAILED_DEPENDENCY:
231
+ reason = " (Failed Dependency)";
232
+ break;
233
+ case LCB_HTTP_STATUS_INTERNAL_SERVER_ERROR:
234
+ reason = " (Internal Server Error)";
235
+ break;
236
+ case LCB_HTTP_STATUS_NOT_IMPLEMENTED:
237
+ reason = " (Not Implemented)";
238
+ break;
239
+ case LCB_HTTP_STATUS_BAD_GATEWAY:
240
+ reason = " (Bad Gateway)";
241
+ break;
242
+ case LCB_HTTP_STATUS_SERVICE_UNAVAILABLE:
243
+ reason = " (Service Unavailable)";
244
+ break;
245
+ case LCB_HTTP_STATUS_GATEWAY_TIMEOUT:
246
+ reason = " (Gateway Timeout)";
247
+ break;
248
+ case LCB_HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED:
249
+ reason = " (Http Version Not Supported)";
250
+ break;
251
+ case LCB_HTTP_STATUS_INSUFFICIENT_STORAGE:
252
+ reason = " (Insufficient Storage)";
253
+ break;
254
+ default:
255
+ reason = "";
256
+ }
257
+ rb_str_buf_cat2(str, reason);
258
+ rb_str_buf_cat2(str, ", ");
259
+
260
+ }
261
+ snprintf(buf, 300, "error=0x%02x)", rc);
262
+ rb_str_buf_cat2(str, buf);
263
+ exc = rb_exc_new3(klass, str);
264
+ rb_ivar_set(exc, id_iv_error, INT2FIX(rc));
265
+ rb_ivar_set(exc, id_iv_key, key);
266
+ rb_ivar_set(exc, id_iv_cas, Qnil);
267
+ rb_ivar_set(exc, id_iv_operation, Qnil);
268
+ rb_ivar_set(exc, id_iv_status, status ? INT2FIX(status) : Qnil);
269
+ return exc;
270
+ }
271
+
272
+ VALUE
273
+ cb_check_error(lcb_error_t rc, const char *msg, VALUE key)
274
+ {
275
+ return cb_check_error_with_status(rc, msg, key, 0);
276
+ }
277
+
278
+
279
+ uint32_t
280
+ flags_set_format(uint32_t flags, ID format)
281
+ {
282
+ flags &= ~((uint32_t)FMT_MASK); /* clear format bits */
283
+
284
+ if (format == sym_document) {
285
+ return flags | FMT_DOCUMENT;
286
+ } else if (format == sym_marshal) {
287
+ return flags | FMT_MARSHAL;
288
+ } else if (format == sym_plain) {
289
+ return flags | FMT_PLAIN;
290
+ }
291
+ return flags; /* document is the default */
292
+ }
293
+
294
+ ID
295
+ flags_get_format(uint32_t flags)
296
+ {
297
+ flags &= FMT_MASK; /* select format bits */
298
+
299
+ switch (flags) {
300
+ case FMT_DOCUMENT:
301
+ return sym_document;
302
+ case FMT_MARSHAL:
303
+ return sym_marshal;
304
+ case FMT_PLAIN:
305
+ /* fall through */
306
+ default:
307
+ /* all other formats treated as plain */
308
+ return sym_plain;
309
+ }
310
+ }
311
+
312
+
313
+ static VALUE
314
+ do_encode(VALUE *args)
315
+ {
316
+ VALUE val = args[0];
317
+ uint32_t flags = ((uint32_t)args[1] & FMT_MASK);
318
+
319
+ switch (flags) {
320
+ case FMT_DOCUMENT:
321
+ return rb_funcall(mMultiJson, id_dump, 1, val);
322
+ case FMT_MARSHAL:
323
+ return rb_funcall(mMarshal, id_dump, 1, val);
324
+ case FMT_PLAIN:
325
+ /* fall through */
326
+ default:
327
+ /* all other formats treated as plain */
328
+ return val;
329
+ }
330
+ }
331
+
332
+ static VALUE
333
+ do_decode(VALUE *args)
334
+ {
335
+ VALUE blob = args[0];
336
+ VALUE force_format = args[2];
337
+
338
+ if (TYPE(force_format) == T_SYMBOL) {
339
+ if (force_format == sym_document) {
340
+ return rb_funcall(mMultiJson, id_load, 1, blob);
341
+ } else if (force_format == sym_marshal) {
342
+ return rb_funcall(mMarshal, id_load, 1, blob);
343
+ } else { /* sym_plain and any other symbol */
344
+ return blob;
345
+ }
346
+ } else {
347
+ uint32_t flags = ((uint32_t)args[1] & FMT_MASK);
348
+
349
+ switch (flags) {
350
+ case FMT_DOCUMENT:
351
+ return rb_funcall(mMultiJson, id_load, 1, blob);
352
+ case FMT_MARSHAL:
353
+ return rb_funcall(mMarshal, id_load, 1, blob);
354
+ case FMT_PLAIN:
355
+ /* fall through */
356
+ default:
357
+ /* all other formats treated as plain */
358
+ return blob;
359
+ }
360
+ }
361
+ }
362
+
363
+ static VALUE
364
+ coding_failed(void)
365
+ {
366
+ return Qundef;
367
+ }
368
+
369
+ VALUE
370
+ encode_value(VALUE val, uint32_t flags)
371
+ {
372
+ VALUE blob, args[2];
373
+
374
+ args[0] = val;
375
+ args[1] = (VALUE)flags;
376
+ /* FIXME re-raise proper exception */
377
+ blob = rb_rescue(do_encode, (VALUE)args, coding_failed, 0);
378
+ /* it must be bytestring after all */
379
+ if (TYPE(blob) != T_STRING) {
380
+ return Qundef;
381
+ }
382
+ return blob;
383
+ }
384
+
385
+ VALUE
386
+ decode_value(VALUE blob, uint32_t flags, VALUE force_format)
387
+ {
388
+ VALUE val, args[3];
389
+
390
+ /* first it must be bytestring */
391
+ if (TYPE(blob) != T_STRING) {
392
+ return Qundef;
393
+ }
394
+ args[0] = blob;
395
+ args[1] = (VALUE)flags;
396
+ args[2] = (VALUE)force_format;
397
+ val = rb_rescue(do_decode, (VALUE)args, coding_failed, 0);
398
+ return val;
399
+ }
400
+
401
+ void
402
+ strip_key_prefix(struct bucket_st *bucket, VALUE key)
403
+ {
404
+ if (bucket->key_prefix) {
405
+ rb_str_update(key, 0, RSTRING_LEN(bucket->key_prefix_val), STR_NEW_CSTR(""));
406
+ }
407
+ }
408
+
409
+ VALUE
410
+ unify_key(struct bucket_st *bucket, VALUE key, int apply_prefix)
411
+ {
412
+ VALUE ret = Qnil, tmp;
413
+
414
+ if (bucket->key_prefix && apply_prefix) {
415
+ ret = rb_str_dup(bucket->key_prefix_val);
416
+ }
417
+ switch (TYPE(key)) {
418
+ case T_STRING:
419
+ return NIL_P(ret) ? key : rb_str_concat(ret, key);
420
+ case T_SYMBOL:
421
+ tmp = STR_NEW_CSTR(rb_id2name(SYM2ID(key)));
422
+ return NIL_P(ret) ? tmp : rb_str_concat(ret, tmp);
423
+ default: /* call #to_str or raise error */
424
+ tmp = StringValue(key);
425
+ return NIL_P(ret) ? tmp : rb_str_concat(ret, tmp);
426
+ }
427
+ }
428
+
429
+ void
430
+ cb_build_headers(struct context_st *ctx, const char * const *headers)
431
+ {
432
+ if (!ctx->headers_built) {
433
+ VALUE key = Qnil, val;
434
+ for (size_t ii = 1; *headers != NULL; ++ii, ++headers) {
435
+ if (ii % 2 == 0) {
436
+ if (key == Qnil) {
437
+ break;
438
+ }
439
+ val = rb_hash_aref(ctx->headers_val, key);
440
+ switch (TYPE(val)) {
441
+ case T_NIL:
442
+ rb_hash_aset(ctx->headers_val, key, STR_NEW_CSTR(*headers));
443
+ break;
444
+ case T_ARRAY:
445
+ rb_ary_push(val, STR_NEW_CSTR(*headers));
446
+ break;
447
+ default:
448
+ {
449
+ VALUE ary = rb_ary_new();
450
+ rb_ary_push(ary, val);
451
+ rb_ary_push(ary, STR_NEW_CSTR(*headers));
452
+ rb_hash_aset(ctx->headers_val, key, ary);
453
+ }
454
+ }
455
+ } else {
456
+ key = STR_NEW_CSTR(*headers);
457
+ }
458
+ }
459
+ ctx->headers_built = 1;
460
+ }
461
+ }
462
+
463
+ int
464
+ cb_first_value_i(VALUE key, VALUE value, VALUE arg)
465
+ {
466
+ VALUE *val = (VALUE *)arg;
467
+
468
+ *val = value;
469
+ (void)key;
470
+ return ST_STOP;
471
+ }