couchbase 1.1.5-x86-mingw32 → 1.2.0.beta-x86-mingw32
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.
- data/.gitignore +2 -1
- data/.travis.yml +12 -1
- data/HISTORY.markdown +112 -1
- data/README.markdown +149 -6
- data/couchbase.gemspec +5 -1
- data/ext/couchbase_ext/.gitignore +4 -0
- data/ext/couchbase_ext/arguments.c +973 -0
- data/ext/couchbase_ext/arithmetic.c +322 -0
- data/ext/couchbase_ext/bucket.c +1092 -0
- data/ext/couchbase_ext/couchbase_ext.c +618 -3247
- data/ext/couchbase_ext/couchbase_ext.h +519 -0
- data/ext/couchbase_ext/delete.c +167 -0
- data/ext/couchbase_ext/extconf.rb +24 -5
- data/ext/couchbase_ext/get.c +301 -0
- data/ext/couchbase_ext/gethrtime.c +124 -0
- data/ext/couchbase_ext/http.c +402 -0
- data/ext/couchbase_ext/observe.c +174 -0
- data/ext/couchbase_ext/result.c +126 -0
- data/ext/couchbase_ext/stats.c +169 -0
- data/ext/couchbase_ext/store.c +522 -0
- data/ext/couchbase_ext/timer.c +192 -0
- data/ext/couchbase_ext/touch.c +190 -0
- data/ext/couchbase_ext/unlock.c +180 -0
- data/ext/couchbase_ext/utils.c +471 -0
- data/ext/couchbase_ext/version.c +147 -0
- data/lib/action_dispatch/middleware/session/couchbase_store.rb +38 -0
- data/lib/active_support/cache/couchbase_store.rb +356 -0
- data/lib/couchbase.rb +24 -3
- data/lib/couchbase/bucket.rb +372 -9
- data/lib/couchbase/result.rb +26 -0
- data/lib/couchbase/utils.rb +59 -0
- data/lib/couchbase/version.rb +1 -1
- data/lib/couchbase/view.rb +305 -0
- data/lib/couchbase/view_row.rb +230 -0
- data/lib/ext/multi_json_fix.rb +47 -0
- data/lib/rack/session/couchbase.rb +104 -0
- data/tasks/compile.rake +5 -14
- data/test/setup.rb +6 -2
- data/test/test_arithmetic.rb +32 -2
- data/test/test_async.rb +18 -4
- data/test/test_bucket.rb +11 -1
- data/test/test_cas.rb +13 -3
- data/test/test_couchbase_rails_cache_store.rb +294 -0
- data/test/test_delete.rb +60 -3
- data/test/test_format.rb +28 -17
- data/test/test_get.rb +91 -14
- data/test/test_store.rb +31 -1
- data/test/{test_flush.rb → test_timer.rb} +11 -18
- data/test/test_touch.rb +33 -5
- data/test/test_unlock.rb +120 -0
- data/test/test_utils.rb +26 -0
- metadata +102 -12
@@ -0,0 +1,322 @@
|
|
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
|
+
void
|
21
|
+
arithmetic_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_arithmetic_resp_t *resp)
|
22
|
+
{
|
23
|
+
struct context_st *ctx = (struct context_st *)cookie;
|
24
|
+
struct bucket_st *bucket = ctx->bucket;
|
25
|
+
VALUE cas, key, val, *rv = ctx->rv, exc, res;
|
26
|
+
ID o;
|
27
|
+
|
28
|
+
ctx->nqueries--;
|
29
|
+
key = STR_NEW((const char*)resp->v.v0.key, resp->v.v0.nkey);
|
30
|
+
strip_key_prefix(bucket, key);
|
31
|
+
|
32
|
+
cas = resp->v.v0.cas > 0 ? ULL2NUM(resp->v.v0.cas) : Qnil;
|
33
|
+
o = ctx->arith > 0 ? sym_increment : sym_decrement;
|
34
|
+
exc = cb_check_error(error, "failed to perform arithmetic operation", key);
|
35
|
+
if (exc != Qnil) {
|
36
|
+
rb_ivar_set(exc, id_iv_cas, cas);
|
37
|
+
rb_ivar_set(exc, id_iv_operation, o);
|
38
|
+
if (bucket->async) {
|
39
|
+
if (bucket->on_error_proc != Qnil) {
|
40
|
+
cb_proc_call(bucket->on_error_proc, 3, o, key, exc);
|
41
|
+
} else {
|
42
|
+
if (NIL_P(bucket->exception)) {
|
43
|
+
bucket->exception = exc;
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}
|
47
|
+
if (NIL_P(ctx->exception)) {
|
48
|
+
ctx->exception = cb_gc_protect(bucket, exc);
|
49
|
+
}
|
50
|
+
}
|
51
|
+
val = ULL2NUM(resp->v.v0.value);
|
52
|
+
if (bucket->async) { /* asynchronous */
|
53
|
+
if (ctx->proc != Qnil) {
|
54
|
+
res = rb_class_new_instance(0, NULL, cResult);
|
55
|
+
rb_ivar_set(res, id_iv_error, exc);
|
56
|
+
rb_ivar_set(res, id_iv_operation, o);
|
57
|
+
rb_ivar_set(res, id_iv_key, key);
|
58
|
+
rb_ivar_set(res, id_iv_value, val);
|
59
|
+
rb_ivar_set(res, id_iv_cas, cas);
|
60
|
+
cb_proc_call(ctx->proc, 1, res);
|
61
|
+
}
|
62
|
+
} else { /* synchronous */
|
63
|
+
if (NIL_P(exc)) {
|
64
|
+
if (ctx->extended) {
|
65
|
+
rb_hash_aset(*rv, key, rb_ary_new3(2, val, cas));
|
66
|
+
} else {
|
67
|
+
rb_hash_aset(*rv, key, val);
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}
|
71
|
+
if (ctx->nqueries == 0) {
|
72
|
+
cb_gc_unprotect(bucket, ctx->proc);
|
73
|
+
}
|
74
|
+
(void)handle;
|
75
|
+
}
|
76
|
+
|
77
|
+
static inline VALUE
|
78
|
+
cb_bucket_arithmetic(int sign, int argc, VALUE *argv, VALUE self)
|
79
|
+
{
|
80
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
81
|
+
struct context_st *ctx;
|
82
|
+
VALUE args, rv, proc, exc;
|
83
|
+
lcb_error_t err;
|
84
|
+
struct params_st params;
|
85
|
+
|
86
|
+
if (bucket->handle == NULL) {
|
87
|
+
rb_raise(eConnectError, "closed connection");
|
88
|
+
}
|
89
|
+
rb_scan_args(argc, argv, "0*&", &args, &proc);
|
90
|
+
if (!bucket->async && proc != Qnil) {
|
91
|
+
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
92
|
+
}
|
93
|
+
memset(¶ms, 0, sizeof(struct params_st));
|
94
|
+
params.type = cmd_arith;
|
95
|
+
params.bucket = bucket;
|
96
|
+
params.cmd.arith.sign = sign;
|
97
|
+
cb_params_build(¶ms, RARRAY_LEN(args), args);
|
98
|
+
ctx = xcalloc(1, sizeof(struct context_st));
|
99
|
+
if (ctx == NULL) {
|
100
|
+
rb_raise(eClientNoMemoryError, "failed to allocate memory for context");
|
101
|
+
}
|
102
|
+
rv = rb_hash_new();
|
103
|
+
ctx->rv = &rv;
|
104
|
+
ctx->bucket = bucket;
|
105
|
+
ctx->proc = cb_gc_protect(bucket, proc);
|
106
|
+
ctx->exception = Qnil;
|
107
|
+
ctx->nqueries = params.cmd.arith.num;
|
108
|
+
err = lcb_arithmetic(bucket->handle, (const void *)ctx,
|
109
|
+
params.cmd.arith.num, params.cmd.arith.ptr);
|
110
|
+
cb_params_destroy(¶ms);
|
111
|
+
exc = cb_check_error(err, "failed to schedule arithmetic request", Qnil);
|
112
|
+
if (exc != Qnil) {
|
113
|
+
xfree(ctx);
|
114
|
+
rb_exc_raise(exc);
|
115
|
+
}
|
116
|
+
bucket->nbytes += params.npayload;
|
117
|
+
if (bucket->async) {
|
118
|
+
maybe_do_loop(bucket);
|
119
|
+
return Qnil;
|
120
|
+
} else {
|
121
|
+
if (ctx->nqueries > 0) {
|
122
|
+
/* we have some operations pending */
|
123
|
+
lcb_wait(bucket->handle);
|
124
|
+
}
|
125
|
+
exc = ctx->exception;
|
126
|
+
xfree(ctx);
|
127
|
+
if (exc != Qnil) {
|
128
|
+
cb_gc_unprotect(bucket, exc);
|
129
|
+
rb_exc_raise(exc);
|
130
|
+
}
|
131
|
+
if (params.cmd.store.num > 1) {
|
132
|
+
return rv; /* return as a hash {key => cas, ...} */
|
133
|
+
} else {
|
134
|
+
VALUE vv = Qnil;
|
135
|
+
rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
|
136
|
+
return vv;
|
137
|
+
}
|
138
|
+
return rv;
|
139
|
+
}
|
140
|
+
}
|
141
|
+
|
142
|
+
/*
|
143
|
+
* Increment the value of an existing numeric key
|
144
|
+
*
|
145
|
+
* @since 1.0.0
|
146
|
+
*
|
147
|
+
* The increment methods allow you to increase a given stored integer
|
148
|
+
* value. These are the incremental equivalent of the decrement operations
|
149
|
+
* and work on the same basis; updating the value of a key if it can be
|
150
|
+
* parsed to an integer. The update operation occurs on the server and is
|
151
|
+
* provided at the protocol level. This simplifies what would otherwise be a
|
152
|
+
* two-stage get and set operation.
|
153
|
+
*
|
154
|
+
* @note that server values stored and transmitted as unsigned numbers,
|
155
|
+
* therefore if you try to store negative number and then increment or
|
156
|
+
* decrement it will cause overflow. (see "Integer overflow" example
|
157
|
+
* below)
|
158
|
+
*
|
159
|
+
* @overload incr(key, delta = 1, options = {})
|
160
|
+
* @param key [String, Symbol] Key used to reference the value.
|
161
|
+
* @param delta [Fixnum] Integer (up to 64 bits) value to increment
|
162
|
+
* @param options [Hash] Options for operation.
|
163
|
+
* @option options [true, false] :create (false) If set to +true+, it will
|
164
|
+
* initialize the key with zero value and zero flags (use +:initial+
|
165
|
+
* option to set another initial value). Note: it won't increment the
|
166
|
+
* missing value.
|
167
|
+
* @option options [Fixnum] :initial (0) Integer (up to 64 bits) value for
|
168
|
+
* missing key initialization. This option imply +:create+ option is
|
169
|
+
* +true+.
|
170
|
+
* @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
|
171
|
+
* Values larger than 30*24*60*60 seconds (30 days) are interpreted as
|
172
|
+
* absolute times (from the epoch). This option ignored for existent
|
173
|
+
* keys.
|
174
|
+
* @option options [true, false] :extended (false) If set to +true+, the
|
175
|
+
* operation will return tuple +[value, cas]+, otherwise (by default) it
|
176
|
+
* returns just value.
|
177
|
+
*
|
178
|
+
* @yieldparam ret [Result] the result of operation in asynchronous mode
|
179
|
+
* (valid attributes: +error+, +operation+, +key+, +value+, +cas+).
|
180
|
+
*
|
181
|
+
* @return [Fixnum] the actual value of the key.
|
182
|
+
*
|
183
|
+
* @raise [Couchbase::Error::NotFound] if key is missing and +:create+
|
184
|
+
* option isn't +true+.
|
185
|
+
*
|
186
|
+
* @raise [Couchbase::Error::DeltaBadval] if the key contains non-numeric
|
187
|
+
* value
|
188
|
+
*
|
189
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
190
|
+
*
|
191
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
192
|
+
*
|
193
|
+
* @example Increment key by one
|
194
|
+
* c.incr("foo")
|
195
|
+
*
|
196
|
+
* @example Increment key by 50
|
197
|
+
* c.incr("foo", 50)
|
198
|
+
*
|
199
|
+
* @example Increment key by one <b>OR</b> initialize with zero
|
200
|
+
* c.incr("foo", :create => true) #=> will return old+1 or 0
|
201
|
+
*
|
202
|
+
* @example Increment key by one <b>OR</b> initialize with three
|
203
|
+
* c.incr("foo", 50, :initial => 3) #=> will return old+50 or 3
|
204
|
+
*
|
205
|
+
* @example Increment key and get its CAS value
|
206
|
+
* val, cas = c.incr("foo", :extended => true)
|
207
|
+
*
|
208
|
+
* @example Integer overflow
|
209
|
+
* c.set("foo", -100)
|
210
|
+
* c.get("foo") #=> -100
|
211
|
+
* c.incr("foo") #=> 18446744073709551517
|
212
|
+
*
|
213
|
+
* @example Asynchronous invocation
|
214
|
+
* c.run do
|
215
|
+
* c.incr("foo") do |ret|
|
216
|
+
* ret.operation #=> :increment
|
217
|
+
* ret.success? #=> true
|
218
|
+
* ret.key #=> "foo"
|
219
|
+
* ret.value
|
220
|
+
* ret.cas
|
221
|
+
* end
|
222
|
+
* end
|
223
|
+
*
|
224
|
+
*/
|
225
|
+
VALUE
|
226
|
+
cb_bucket_incr(int argc, VALUE *argv, VALUE self)
|
227
|
+
{
|
228
|
+
return cb_bucket_arithmetic(+1, argc, argv, self);
|
229
|
+
}
|
230
|
+
|
231
|
+
/*
|
232
|
+
* Decrement the value of an existing numeric key
|
233
|
+
*
|
234
|
+
* @since 1.0.0
|
235
|
+
*
|
236
|
+
* The decrement methods reduce the value of a given key if the
|
237
|
+
* corresponding value can be parsed to an integer value. These operations
|
238
|
+
* are provided at a protocol level to eliminate the need to get, update,
|
239
|
+
* and reset a simple integer value in the database. It supports the use of
|
240
|
+
* an explicit offset value that will be used to reduce the stored value in
|
241
|
+
* the database.
|
242
|
+
*
|
243
|
+
* @note that server values stored and transmitted as unsigned numbers,
|
244
|
+
* therefore if you try to decrement negative or zero key, you will always
|
245
|
+
* get zero.
|
246
|
+
*
|
247
|
+
* @overload decr(key, delta = 1, options = {})
|
248
|
+
* @param key [String, Symbol] Key used to reference the value.
|
249
|
+
* @param delta [Fixnum] Integer (up to 64 bits) value to decrement
|
250
|
+
* @param options [Hash] Options for operation.
|
251
|
+
* @option options [true, false] :create (false) If set to +true+, it will
|
252
|
+
* initialize the key with zero value and zero flags (use +:initial+
|
253
|
+
* option to set another initial value). Note: it won't decrement the
|
254
|
+
* missing value.
|
255
|
+
* @option options [Fixnum] :initial (0) Integer (up to 64 bits) value for
|
256
|
+
* missing key initialization. This option imply +:create+ option is
|
257
|
+
* +true+.
|
258
|
+
* @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
|
259
|
+
* Values larger than 30*24*60*60 seconds (30 days) are interpreted as
|
260
|
+
* absolute times (from the epoch). This option ignored for existent
|
261
|
+
* keys.
|
262
|
+
* @option options [true, false] :extended (false) If set to +true+, the
|
263
|
+
* operation will return tuple +[value, cas]+, otherwise (by default) it
|
264
|
+
* returns just value.
|
265
|
+
*
|
266
|
+
* @yieldparam ret [Result] the result of operation in asynchronous mode
|
267
|
+
* (valid attributes: +error+, +operation+, +key+, +value+, +cas+).
|
268
|
+
*
|
269
|
+
* @return [Fixnum] the actual value of the key.
|
270
|
+
*
|
271
|
+
* @raise [Couchbase::Error::NotFound] if key is missing and +:create+
|
272
|
+
* option isn't +true+.
|
273
|
+
*
|
274
|
+
* @raise [Couchbase::Error::DeltaBadval] if the key contains non-numeric
|
275
|
+
* value
|
276
|
+
*
|
277
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
278
|
+
*
|
279
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
280
|
+
*
|
281
|
+
* @example Decrement key by one
|
282
|
+
* c.decr("foo")
|
283
|
+
*
|
284
|
+
* @example Decrement key by 50
|
285
|
+
* c.decr("foo", 50)
|
286
|
+
*
|
287
|
+
* @example Decrement key by one <b>OR</b> initialize with zero
|
288
|
+
* c.decr("foo", :create => true) #=> will return old-1 or 0
|
289
|
+
*
|
290
|
+
* @example Decrement key by one <b>OR</b> initialize with three
|
291
|
+
* c.decr("foo", 50, :initial => 3) #=> will return old-50 or 3
|
292
|
+
*
|
293
|
+
* @example Decrement key and get its CAS value
|
294
|
+
* val, cas = c.decr("foo", :extended => true)
|
295
|
+
*
|
296
|
+
* @example Decrementing zero
|
297
|
+
* c.set("foo", 0)
|
298
|
+
* c.decrement("foo", 100500) #=> 0
|
299
|
+
*
|
300
|
+
* @example Decrementing negative value
|
301
|
+
* c.set("foo", -100)
|
302
|
+
* c.decrement("foo", 100500) #=> 0
|
303
|
+
*
|
304
|
+
* @example Asynchronous invocation
|
305
|
+
* c.run do
|
306
|
+
* c.decr("foo") do |ret|
|
307
|
+
* ret.operation #=> :decrement
|
308
|
+
* ret.success? #=> true
|
309
|
+
* ret.key #=> "foo"
|
310
|
+
* ret.value
|
311
|
+
* ret.cas
|
312
|
+
* end
|
313
|
+
* end
|
314
|
+
*
|
315
|
+
*/
|
316
|
+
VALUE
|
317
|
+
cb_bucket_decr(int argc, VALUE *argv, VALUE self)
|
318
|
+
{
|
319
|
+
return cb_bucket_arithmetic(-1, argc, argv, self);
|
320
|
+
}
|
321
|
+
|
322
|
+
|
@@ -0,0 +1,1092 @@
|
|
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
|
+
static void
|
21
|
+
error_callback(lcb_t handle, lcb_error_t error, const char *errinfo)
|
22
|
+
{
|
23
|
+
struct bucket_st *bucket = (struct bucket_st *)lcb_get_cookie(handle);
|
24
|
+
|
25
|
+
bucket->io->stop_event_loop(bucket->io);
|
26
|
+
bucket->exception = cb_check_error(error, errinfo, Qnil);
|
27
|
+
}
|
28
|
+
|
29
|
+
void
|
30
|
+
cb_bucket_free(void *ptr)
|
31
|
+
{
|
32
|
+
struct bucket_st *bucket = ptr;
|
33
|
+
|
34
|
+
if (bucket) {
|
35
|
+
if (bucket->handle) {
|
36
|
+
lcb_destroy(bucket->handle);
|
37
|
+
}
|
38
|
+
xfree(bucket->authority);
|
39
|
+
xfree(bucket->hostname);
|
40
|
+
xfree(bucket->pool);
|
41
|
+
xfree(bucket->bucket);
|
42
|
+
xfree(bucket->username);
|
43
|
+
xfree(bucket->password);
|
44
|
+
xfree(bucket->key_prefix);
|
45
|
+
xfree(bucket);
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
void
|
50
|
+
cb_bucket_mark(void *ptr)
|
51
|
+
{
|
52
|
+
struct bucket_st *bucket = ptr;
|
53
|
+
|
54
|
+
if (bucket) {
|
55
|
+
rb_gc_mark(bucket->exception);
|
56
|
+
rb_gc_mark(bucket->on_error_proc);
|
57
|
+
rb_gc_mark(bucket->key_prefix_val);
|
58
|
+
rb_gc_mark(bucket->object_space);
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
static void
|
63
|
+
do_scan_connection_options(struct bucket_st *bucket, int argc, VALUE *argv)
|
64
|
+
{
|
65
|
+
VALUE uri, opts, arg;
|
66
|
+
size_t len;
|
67
|
+
|
68
|
+
if (rb_scan_args(argc, argv, "02", &uri, &opts) > 0) {
|
69
|
+
if (TYPE(uri) == T_HASH && argc == 1) {
|
70
|
+
opts = uri;
|
71
|
+
uri = Qnil;
|
72
|
+
}
|
73
|
+
if (uri != Qnil) {
|
74
|
+
const char path_re[] = "^(/pools/([A-Za-z0-9_.-]+)(/buckets/([A-Za-z0-9_.-]+))?)?";
|
75
|
+
VALUE match, uri_obj, re;
|
76
|
+
|
77
|
+
Check_Type(uri, T_STRING);
|
78
|
+
uri_obj = rb_funcall(mURI, id_parse, 1, uri);
|
79
|
+
|
80
|
+
arg = rb_funcall(uri_obj, id_scheme, 0);
|
81
|
+
if (arg == Qnil || rb_str_cmp(arg, STR_NEW_CSTR("http"))) {
|
82
|
+
rb_raise(rb_eArgError, "invalid URI: invalid scheme");
|
83
|
+
}
|
84
|
+
|
85
|
+
arg = rb_funcall(uri_obj, id_user, 0);
|
86
|
+
if (arg != Qnil) {
|
87
|
+
xfree(bucket->username);
|
88
|
+
bucket->username = strdup(RSTRING_PTR(arg));
|
89
|
+
if (bucket->username == NULL) {
|
90
|
+
rb_raise(eClientNoMemoryError, "failed to allocate memory for Bucket");
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
arg = rb_funcall(uri_obj, id_password, 0);
|
95
|
+
if (arg != Qnil) {
|
96
|
+
xfree(bucket->password);
|
97
|
+
bucket->password = strdup(RSTRING_PTR(arg));
|
98
|
+
if (bucket->password == NULL) {
|
99
|
+
rb_raise(eClientNoMemoryError, "failed to allocate memory for Bucket");
|
100
|
+
}
|
101
|
+
}
|
102
|
+
arg = rb_funcall(uri_obj, id_host, 0);
|
103
|
+
if (arg != Qnil) {
|
104
|
+
xfree(bucket->hostname);
|
105
|
+
bucket->hostname = strdup(RSTRING_PTR(arg));
|
106
|
+
if (bucket->hostname == NULL) {
|
107
|
+
rb_raise(eClientNoMemoryError, "failed to allocate memory for Bucket");
|
108
|
+
}
|
109
|
+
} else {
|
110
|
+
rb_raise(rb_eArgError, "invalid URI: missing hostname");
|
111
|
+
}
|
112
|
+
|
113
|
+
arg = rb_funcall(uri_obj, id_port, 0);
|
114
|
+
bucket->port = NIL_P(arg) ? 8091 : (uint16_t)NUM2UINT(arg);
|
115
|
+
|
116
|
+
arg = rb_funcall(uri_obj, id_path, 0);
|
117
|
+
re = rb_reg_new(path_re, sizeof(path_re) - 1, 0);
|
118
|
+
match = rb_funcall(re, id_match, 1, arg);
|
119
|
+
arg = rb_reg_nth_match(2, match);
|
120
|
+
xfree(bucket->pool);
|
121
|
+
bucket->pool = strdup(NIL_P(arg) ? "default" : RSTRING_PTR(arg));
|
122
|
+
arg = rb_reg_nth_match(4, match);
|
123
|
+
xfree(bucket->bucket);
|
124
|
+
bucket->bucket = strdup(NIL_P(arg) ? "default" : RSTRING_PTR(arg));
|
125
|
+
}
|
126
|
+
if (TYPE(opts) == T_HASH) {
|
127
|
+
arg = rb_hash_aref(opts, sym_node_list);
|
128
|
+
if (arg != Qnil) {
|
129
|
+
VALUE tt;
|
130
|
+
xfree(bucket->node_list);
|
131
|
+
Check_Type(arg, T_ARRAY);
|
132
|
+
tt = rb_ary_join(arg, STR_NEW_CSTR(";"));
|
133
|
+
bucket->node_list = strdup(StringValueCStr(tt));
|
134
|
+
}
|
135
|
+
arg = rb_hash_aref(opts, sym_hostname);
|
136
|
+
if (arg != Qnil) {
|
137
|
+
xfree(bucket->hostname);
|
138
|
+
bucket->hostname = strdup(StringValueCStr(arg));
|
139
|
+
}
|
140
|
+
arg = rb_hash_aref(opts, sym_pool);
|
141
|
+
if (arg != Qnil) {
|
142
|
+
xfree(bucket->pool);
|
143
|
+
bucket->pool = strdup(StringValueCStr(arg));
|
144
|
+
}
|
145
|
+
arg = rb_hash_aref(opts, sym_bucket);
|
146
|
+
if (arg != Qnil) {
|
147
|
+
xfree(bucket->bucket);
|
148
|
+
bucket->bucket = strdup(StringValueCStr(arg));
|
149
|
+
}
|
150
|
+
arg = rb_hash_aref(opts, sym_username);
|
151
|
+
if (arg != Qnil) {
|
152
|
+
xfree(bucket->username);
|
153
|
+
bucket->username = strdup(StringValueCStr(arg));
|
154
|
+
}
|
155
|
+
arg = rb_hash_aref(opts, sym_password);
|
156
|
+
if (arg != Qnil) {
|
157
|
+
xfree(bucket->password);
|
158
|
+
bucket->password = strdup(StringValueCStr(arg));
|
159
|
+
}
|
160
|
+
arg = rb_hash_aref(opts, sym_port);
|
161
|
+
if (arg != Qnil) {
|
162
|
+
bucket->port = (uint16_t)NUM2UINT(arg);
|
163
|
+
}
|
164
|
+
if (RTEST(rb_funcall(opts, id_has_key_p, 1, sym_quiet))) {
|
165
|
+
bucket->quiet = RTEST(rb_hash_aref(opts, sym_quiet));
|
166
|
+
}
|
167
|
+
arg = rb_hash_aref(opts, sym_timeout);
|
168
|
+
if (arg != Qnil) {
|
169
|
+
bucket->timeout = (uint32_t)NUM2ULONG(arg);
|
170
|
+
}
|
171
|
+
arg = rb_hash_aref(opts, sym_default_ttl);
|
172
|
+
if (arg != Qnil) {
|
173
|
+
bucket->default_ttl = (uint32_t)NUM2ULONG(arg);
|
174
|
+
}
|
175
|
+
arg = rb_hash_aref(opts, sym_default_observe_timeout);
|
176
|
+
if (arg != Qnil) {
|
177
|
+
bucket->default_observe_timeout = (uint32_t)NUM2ULONG(arg);
|
178
|
+
}
|
179
|
+
arg = rb_hash_aref(opts, sym_default_flags);
|
180
|
+
if (arg != Qnil) {
|
181
|
+
bucket->default_flags = (uint32_t)NUM2ULONG(arg);
|
182
|
+
}
|
183
|
+
arg = rb_hash_aref(opts, sym_default_format);
|
184
|
+
if (arg != Qnil) {
|
185
|
+
if (TYPE(arg) == T_FIXNUM) {
|
186
|
+
switch (FIX2INT(arg)) {
|
187
|
+
case FMT_DOCUMENT:
|
188
|
+
arg = sym_document;
|
189
|
+
break;
|
190
|
+
case FMT_MARSHAL:
|
191
|
+
arg = sym_marshal;
|
192
|
+
break;
|
193
|
+
case FMT_PLAIN:
|
194
|
+
arg = sym_plain;
|
195
|
+
break;
|
196
|
+
}
|
197
|
+
}
|
198
|
+
if (arg == sym_document || arg == sym_marshal || arg == sym_plain) {
|
199
|
+
bucket->default_format = arg;
|
200
|
+
bucket->default_flags = flags_set_format(bucket->default_flags, arg);
|
201
|
+
}
|
202
|
+
}
|
203
|
+
arg = rb_hash_aref(opts, sym_environment);
|
204
|
+
if (arg != Qnil) {
|
205
|
+
if (arg == sym_production || arg == sym_development) {
|
206
|
+
bucket->environment = arg;
|
207
|
+
}
|
208
|
+
}
|
209
|
+
arg = rb_hash_aref(opts, sym_key_prefix);
|
210
|
+
if (arg != Qnil) {
|
211
|
+
xfree(bucket->key_prefix);
|
212
|
+
bucket->key_prefix = strdup(StringValueCStr(arg));
|
213
|
+
bucket->key_prefix_val = STR_NEW_CSTR(bucket->key_prefix);
|
214
|
+
}
|
215
|
+
} else {
|
216
|
+
opts = Qnil;
|
217
|
+
}
|
218
|
+
}
|
219
|
+
if (bucket->password && bucket->username == NULL) {
|
220
|
+
bucket->username = strdup(bucket->bucket);
|
221
|
+
}
|
222
|
+
len = strlen(bucket->hostname) + 10;
|
223
|
+
if (bucket->default_observe_timeout < 2) {
|
224
|
+
rb_raise(rb_eArgError, "default_observe_timeout is too low");
|
225
|
+
}
|
226
|
+
xfree(bucket->authority);
|
227
|
+
bucket->authority = xcalloc(len, sizeof(char));
|
228
|
+
if (bucket->authority == NULL) {
|
229
|
+
rb_raise(eClientNoMemoryError, "failed to allocate memory for Bucket");
|
230
|
+
}
|
231
|
+
snprintf(bucket->authority, len, "%s:%u", bucket->hostname, bucket->port);
|
232
|
+
}
|
233
|
+
|
234
|
+
static void
|
235
|
+
do_connect(struct bucket_st *bucket)
|
236
|
+
{
|
237
|
+
lcb_error_t err;
|
238
|
+
struct lcb_create_st create_opts;
|
239
|
+
|
240
|
+
if (bucket->handle) {
|
241
|
+
lcb_destroy(bucket->handle);
|
242
|
+
bucket->handle = NULL;
|
243
|
+
bucket->io = NULL;
|
244
|
+
}
|
245
|
+
err = lcb_create_io_ops(&bucket->io, NULL);
|
246
|
+
if (err != LCB_SUCCESS) {
|
247
|
+
rb_exc_raise(cb_check_error(err, "failed to create IO instance", Qnil));
|
248
|
+
}
|
249
|
+
|
250
|
+
memset(&create_opts, 0, sizeof(struct lcb_create_st));
|
251
|
+
create_opts.v.v0.host = bucket->node_list ? bucket-> node_list : bucket->authority;
|
252
|
+
create_opts.v.v0.user = bucket->username;
|
253
|
+
create_opts.v.v0.passwd = bucket->password;
|
254
|
+
create_opts.v.v0.bucket = bucket->bucket;
|
255
|
+
create_opts.v.v0.io = bucket->io;
|
256
|
+
err = lcb_create(&bucket->handle, &create_opts);
|
257
|
+
if (err != LCB_SUCCESS) {
|
258
|
+
rb_exc_raise(cb_check_error(err, "failed to create libcouchbase instance", Qnil));
|
259
|
+
}
|
260
|
+
lcb_set_cookie(bucket->handle, bucket);
|
261
|
+
(void)lcb_set_error_callback(bucket->handle, error_callback);
|
262
|
+
(void)lcb_set_store_callback(bucket->handle, storage_callback);
|
263
|
+
(void)lcb_set_get_callback(bucket->handle, get_callback);
|
264
|
+
(void)lcb_set_touch_callback(bucket->handle, touch_callback);
|
265
|
+
(void)lcb_set_remove_callback(bucket->handle, delete_callback);
|
266
|
+
(void)lcb_set_stat_callback(bucket->handle, stat_callback);
|
267
|
+
(void)lcb_set_arithmetic_callback(bucket->handle, arithmetic_callback);
|
268
|
+
(void)lcb_set_version_callback(bucket->handle, version_callback);
|
269
|
+
(void)lcb_set_view_complete_callback(bucket->handle, http_complete_callback);
|
270
|
+
(void)lcb_set_view_data_callback(bucket->handle, http_data_callback);
|
271
|
+
(void)lcb_set_management_complete_callback(bucket->handle, http_complete_callback);
|
272
|
+
(void)lcb_set_management_data_callback(bucket->handle, http_data_callback);
|
273
|
+
(void)lcb_set_observe_callback(bucket->handle, observe_callback);
|
274
|
+
(void)lcb_set_unlock_callback(bucket->handle, unlock_callback);
|
275
|
+
|
276
|
+
if (bucket->timeout > 0) {
|
277
|
+
lcb_set_timeout(bucket->handle, bucket->timeout);
|
278
|
+
} else {
|
279
|
+
bucket->timeout = lcb_get_timeout(bucket->handle);
|
280
|
+
}
|
281
|
+
err = lcb_connect(bucket->handle);
|
282
|
+
if (err != LCB_SUCCESS) {
|
283
|
+
lcb_destroy(bucket->handle);
|
284
|
+
bucket->handle = NULL;
|
285
|
+
bucket->io = NULL;
|
286
|
+
rb_exc_raise(cb_check_error(err, "failed to connect libcouchbase instance to server", Qnil));
|
287
|
+
}
|
288
|
+
bucket->exception = Qnil;
|
289
|
+
lcb_wait(bucket->handle);
|
290
|
+
if (bucket->exception != Qnil) {
|
291
|
+
lcb_destroy(bucket->handle);
|
292
|
+
bucket->handle = NULL;
|
293
|
+
bucket->io = NULL;
|
294
|
+
rb_exc_raise(bucket->exception);
|
295
|
+
}
|
296
|
+
}
|
297
|
+
|
298
|
+
VALUE
|
299
|
+
cb_bucket_alloc(VALUE klass)
|
300
|
+
{
|
301
|
+
VALUE obj;
|
302
|
+
struct bucket_st *bucket;
|
303
|
+
|
304
|
+
/* allocate new bucket struct and set it to zero */
|
305
|
+
obj = Data_Make_Struct(klass, struct bucket_st, cb_bucket_mark, cb_bucket_free,
|
306
|
+
bucket);
|
307
|
+
return obj;
|
308
|
+
}
|
309
|
+
|
310
|
+
/*
|
311
|
+
* Initialize new Bucket.
|
312
|
+
*
|
313
|
+
* @since 1.0.0
|
314
|
+
*
|
315
|
+
* @overload initialize(url, options = {})
|
316
|
+
* Initialize bucket using URI of the cluster and options. It is possible
|
317
|
+
* to override some parts of URI using the options keys (e.g. :host or
|
318
|
+
* :port)
|
319
|
+
*
|
320
|
+
* @param [String] url The full URL of management API of the cluster.
|
321
|
+
* @param [Hash] options The options for connection. See options definition
|
322
|
+
* below.
|
323
|
+
*
|
324
|
+
* @overload initialize(options = {})
|
325
|
+
* Initialize bucket using options only.
|
326
|
+
*
|
327
|
+
* @param [Hash] options The options for operation for connection
|
328
|
+
* @option options [Array] :node_list (nil) the list of nodes to connect
|
329
|
+
* to. If specified it takes precedence over +:host+ option. The list
|
330
|
+
* must be array of strings in form of host names or host names with
|
331
|
+
* ports (in first case port 8091 will be used, see examples).
|
332
|
+
* @option options [String] :host ("localhost") the hostname or IP address
|
333
|
+
* of the node
|
334
|
+
* @option options [Fixnum] :port (8091) the port of the managemenent API
|
335
|
+
* @option options [String] :pool ("default") the pool name
|
336
|
+
* @option options [String] :bucket ("default") the bucket name
|
337
|
+
* @option options [Fixnum] :default_ttl (0) the TTL used by default during
|
338
|
+
* storing key-value pairs.
|
339
|
+
* @option options [Fixnum] :default_flags (0) the default flags.
|
340
|
+
* @option options [Symbol] :default_format (:document) the format, which
|
341
|
+
* will be used for values by default. Note that changing format will
|
342
|
+
* amend flags. (see {Bucket#default_format})
|
343
|
+
* @option options [String] :username (nil) the user name to connect to the
|
344
|
+
* cluster. Used to authenticate on management API. The username could
|
345
|
+
* be skipped for protected buckets, the bucket name will be used
|
346
|
+
* instead.
|
347
|
+
* @option options [String] :password (nil) the password of the user.
|
348
|
+
* @option options [true, false] :quiet (false) the flag controlling if raising
|
349
|
+
* exception when the client executes operations on unexising keys. If it
|
350
|
+
* is +true+ it will raise {Couchbase::Error::NotFound} exceptions. The
|
351
|
+
* default behaviour is to return +nil+ value silently (might be useful in
|
352
|
+
* Rails cache).
|
353
|
+
* @option options [Symbol] :environment (:production) the mode of the
|
354
|
+
* connection. Currently it influences only on design documents set. If
|
355
|
+
* the environment is +:development+, you will able to get design
|
356
|
+
* documents with 'dev_' prefix, otherwise (in +:production+ mode) the
|
357
|
+
* library will hide them from you.
|
358
|
+
* @option options [String] :key_prefix (nil) the prefix string which will
|
359
|
+
* be prepended to each key before sending out, and sripped before
|
360
|
+
* returning back to the application.
|
361
|
+
* @option options [Fixnum] :timeout (2500000) the timeout for IO
|
362
|
+
* operations (in microseconds)
|
363
|
+
*
|
364
|
+
* @example Initialize connection using default options
|
365
|
+
* Couchbase.new
|
366
|
+
*
|
367
|
+
* @example Select custom bucket
|
368
|
+
* Couchbase.new(:bucket => 'foo')
|
369
|
+
* Couchbase.new('http://localhost:8091/pools/default/buckets/foo')
|
370
|
+
*
|
371
|
+
* @example Connect to protected bucket
|
372
|
+
* Couchbase.new(:bucket => 'protected', :username => 'protected', :password => 'secret')
|
373
|
+
* Couchbase.new('http://localhost:8091/pools/default/buckets/protected',
|
374
|
+
* :username => 'protected', :password => 'secret')
|
375
|
+
*
|
376
|
+
* @example Use list of nodes, in case some nodes might be dead
|
377
|
+
* Couchbase.new(:node_list => ['example.com:8091', 'example.org:8091', 'example.net'])
|
378
|
+
*
|
379
|
+
* @raise [Couchbase::Error::BucketNotFound] if there no such bucket to
|
380
|
+
* connect
|
381
|
+
*
|
382
|
+
* @raise [Couchbase::Error::Connect] if the socket wasn't accessible
|
383
|
+
* (doesn't accept connections or doesn't respond in time)
|
384
|
+
*
|
385
|
+
* @return [Bucket]
|
386
|
+
*/
|
387
|
+
VALUE
|
388
|
+
cb_bucket_init(int argc, VALUE *argv, VALUE self)
|
389
|
+
{
|
390
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
391
|
+
|
392
|
+
bucket->self = self;
|
393
|
+
bucket->exception = Qnil;
|
394
|
+
bucket->hostname = strdup("localhost");
|
395
|
+
bucket->port = 8091;
|
396
|
+
bucket->pool = strdup("default");
|
397
|
+
bucket->bucket = strdup("default");
|
398
|
+
bucket->async = 0;
|
399
|
+
bucket->quiet = 0;
|
400
|
+
bucket->default_ttl = 0;
|
401
|
+
bucket->default_flags = 0;
|
402
|
+
bucket->default_format = sym_document;
|
403
|
+
bucket->default_observe_timeout = 2500000;
|
404
|
+
bucket->on_error_proc = Qnil;
|
405
|
+
bucket->timeout = 0;
|
406
|
+
bucket->environment = sym_production;
|
407
|
+
bucket->key_prefix = NULL;
|
408
|
+
bucket->key_prefix_val = Qnil;
|
409
|
+
bucket->node_list = NULL;
|
410
|
+
bucket->object_space = rb_hash_new();
|
411
|
+
bucket->node_list = NULL;
|
412
|
+
|
413
|
+
do_scan_connection_options(bucket, argc, argv);
|
414
|
+
do_connect(bucket);
|
415
|
+
|
416
|
+
return self;
|
417
|
+
}
|
418
|
+
|
419
|
+
/*
|
420
|
+
* Initialize copy
|
421
|
+
*
|
422
|
+
* Initializes copy of the object, used by {Couchbase::Bucket#dup}
|
423
|
+
*
|
424
|
+
* @param orig [Couchbase::Bucket] the source for copy
|
425
|
+
*
|
426
|
+
* @return [Couchbase::Bucket]
|
427
|
+
*/
|
428
|
+
VALUE
|
429
|
+
cb_bucket_init_copy(VALUE copy, VALUE orig)
|
430
|
+
{
|
431
|
+
struct bucket_st *copy_b;
|
432
|
+
struct bucket_st *orig_b;
|
433
|
+
|
434
|
+
if (copy == orig)
|
435
|
+
return copy;
|
436
|
+
|
437
|
+
if (TYPE(orig) != T_DATA || TYPE(copy) != T_DATA ||
|
438
|
+
RDATA(orig)->dfree != (RUBY_DATA_FUNC)cb_bucket_free) {
|
439
|
+
rb_raise(rb_eTypeError, "wrong argument type");
|
440
|
+
}
|
441
|
+
|
442
|
+
copy_b = DATA_PTR(copy);
|
443
|
+
orig_b = DATA_PTR(orig);
|
444
|
+
|
445
|
+
copy_b->self = copy_b->self;
|
446
|
+
copy_b->port = orig_b->port;
|
447
|
+
copy_b->authority = strdup(orig_b->authority);
|
448
|
+
copy_b->hostname = strdup(orig_b->hostname);
|
449
|
+
copy_b->pool = strdup(orig_b->pool);
|
450
|
+
copy_b->bucket = strdup(orig_b->bucket);
|
451
|
+
if (orig_b->username) {
|
452
|
+
copy_b->username = strdup(orig_b->username);
|
453
|
+
}
|
454
|
+
if (orig_b->password) {
|
455
|
+
copy_b->password = strdup(orig_b->password);
|
456
|
+
}
|
457
|
+
if (orig_b->key_prefix) {
|
458
|
+
copy_b->key_prefix = strdup(orig_b->key_prefix);
|
459
|
+
}
|
460
|
+
copy_b->async = orig_b->async;
|
461
|
+
copy_b->quiet = orig_b->quiet;
|
462
|
+
copy_b->default_format = orig_b->default_format;
|
463
|
+
copy_b->default_flags = orig_b->default_flags;
|
464
|
+
copy_b->default_ttl = orig_b->default_ttl;
|
465
|
+
copy_b->environment = orig_b->environment;
|
466
|
+
copy_b->timeout = orig_b->timeout;
|
467
|
+
copy_b->exception = Qnil;
|
468
|
+
if (orig_b->on_error_proc != Qnil) {
|
469
|
+
copy_b->on_error_proc = rb_funcall(orig_b->on_error_proc, id_dup, 0);
|
470
|
+
}
|
471
|
+
if (orig_b->key_prefix_val != Qnil) {
|
472
|
+
copy_b->key_prefix_val = rb_funcall(orig_b->key_prefix_val, id_dup, 0);
|
473
|
+
}
|
474
|
+
|
475
|
+
do_connect(copy_b);
|
476
|
+
|
477
|
+
return copy;
|
478
|
+
}
|
479
|
+
|
480
|
+
/*
|
481
|
+
* Reconnect the bucket
|
482
|
+
*
|
483
|
+
* @since 1.1.0
|
484
|
+
*
|
485
|
+
* Reconnect the bucket using initial configuration with optional
|
486
|
+
* redefinition.
|
487
|
+
*
|
488
|
+
* @overload reconnect(url, options = {})
|
489
|
+
* see {Bucket#initialize Bucket#initialize(url, options)}
|
490
|
+
* @return [Couchbase::Bucket]
|
491
|
+
*
|
492
|
+
* @overload reconnect(options = {})
|
493
|
+
* see {Bucket#initialize Bucket#initialize(options)}
|
494
|
+
* @return [Couchbase::Bucket]
|
495
|
+
*
|
496
|
+
* @example reconnect with current parameters
|
497
|
+
* c.reconnect
|
498
|
+
*
|
499
|
+
* @example reconnect the instance to another bucket
|
500
|
+
* c.reconnect(:bucket => 'new')
|
501
|
+
*/
|
502
|
+
VALUE
|
503
|
+
cb_bucket_reconnect(int argc, VALUE *argv, VALUE self)
|
504
|
+
{
|
505
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
506
|
+
|
507
|
+
do_scan_connection_options(bucket, argc, argv);
|
508
|
+
do_connect(bucket);
|
509
|
+
|
510
|
+
return self;
|
511
|
+
}
|
512
|
+
|
513
|
+
/* Document-method: connected?
|
514
|
+
* Check whether the instance connected to the cluster.
|
515
|
+
*
|
516
|
+
* @since 1.1.0
|
517
|
+
*
|
518
|
+
* @return [true, false] +true+ if the instance connected to the cluster
|
519
|
+
*/
|
520
|
+
VALUE
|
521
|
+
cb_bucket_connected_p(VALUE self)
|
522
|
+
{
|
523
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
524
|
+
return bucket->handle ? Qtrue : Qfalse;
|
525
|
+
}
|
526
|
+
|
527
|
+
/* Document-method: async?
|
528
|
+
* Check whether the connection asynchronous.
|
529
|
+
*
|
530
|
+
* @since 1.0.0
|
531
|
+
*
|
532
|
+
* By default all operations are synchronous and block waiting for
|
533
|
+
* results, but you can make them asynchronous and run event loop
|
534
|
+
* explicitly. (see {Bucket#run})
|
535
|
+
*
|
536
|
+
* @example Return value of #get operation depending on async flag
|
537
|
+
* connection = Connection.new
|
538
|
+
* connection.async? #=> false
|
539
|
+
*
|
540
|
+
* connection.run do |conn|
|
541
|
+
* conn.async? #=> true
|
542
|
+
* end
|
543
|
+
*
|
544
|
+
* @return [true, false] +true+ if the connection if asynchronous
|
545
|
+
*
|
546
|
+
* @see Bucket#run
|
547
|
+
*/
|
548
|
+
VALUE
|
549
|
+
cb_bucket_async_p(VALUE self)
|
550
|
+
{
|
551
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
552
|
+
return bucket->async ? Qtrue : Qfalse;
|
553
|
+
}
|
554
|
+
|
555
|
+
VALUE
|
556
|
+
cb_bucket_quiet_get(VALUE self)
|
557
|
+
{
|
558
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
559
|
+
return bucket->quiet ? Qtrue : Qfalse;
|
560
|
+
}
|
561
|
+
|
562
|
+
VALUE
|
563
|
+
cb_bucket_quiet_set(VALUE self, VALUE val)
|
564
|
+
{
|
565
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
566
|
+
VALUE new;
|
567
|
+
|
568
|
+
bucket->quiet = RTEST(val);
|
569
|
+
new = bucket->quiet ? Qtrue : Qfalse;
|
570
|
+
return new;
|
571
|
+
}
|
572
|
+
|
573
|
+
VALUE
|
574
|
+
cb_bucket_default_flags_get(VALUE self)
|
575
|
+
{
|
576
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
577
|
+
return ULONG2NUM(bucket->default_flags);
|
578
|
+
}
|
579
|
+
|
580
|
+
VALUE
|
581
|
+
cb_bucket_default_flags_set(VALUE self, VALUE val)
|
582
|
+
{
|
583
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
584
|
+
|
585
|
+
bucket->default_flags = (uint32_t)NUM2ULONG(val);
|
586
|
+
bucket->default_format = flags_get_format(bucket->default_flags);
|
587
|
+
return val;
|
588
|
+
}
|
589
|
+
|
590
|
+
VALUE
|
591
|
+
cb_bucket_default_format_get(VALUE self)
|
592
|
+
{
|
593
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
594
|
+
return bucket->default_format;
|
595
|
+
}
|
596
|
+
|
597
|
+
VALUE
|
598
|
+
cb_bucket_default_format_set(VALUE self, VALUE val)
|
599
|
+
{
|
600
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
601
|
+
|
602
|
+
if (TYPE(val) == T_FIXNUM) {
|
603
|
+
switch (FIX2INT(val)) {
|
604
|
+
case FMT_DOCUMENT:
|
605
|
+
val = sym_document;
|
606
|
+
break;
|
607
|
+
case FMT_MARSHAL:
|
608
|
+
val = sym_marshal;
|
609
|
+
break;
|
610
|
+
case FMT_PLAIN:
|
611
|
+
val = sym_plain;
|
612
|
+
break;
|
613
|
+
}
|
614
|
+
}
|
615
|
+
if (val == sym_document || val == sym_marshal || val == sym_plain) {
|
616
|
+
bucket->default_format = val;
|
617
|
+
bucket->default_flags = flags_set_format(bucket->default_flags, val);
|
618
|
+
}
|
619
|
+
|
620
|
+
return val;
|
621
|
+
}
|
622
|
+
|
623
|
+
VALUE
|
624
|
+
cb_bucket_on_error_set(VALUE self, VALUE val)
|
625
|
+
{
|
626
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
627
|
+
|
628
|
+
if (rb_respond_to(val, id_call)) {
|
629
|
+
bucket->on_error_proc = val;
|
630
|
+
} else {
|
631
|
+
bucket->on_error_proc = Qnil;
|
632
|
+
}
|
633
|
+
|
634
|
+
return bucket->on_error_proc;
|
635
|
+
}
|
636
|
+
|
637
|
+
VALUE
|
638
|
+
cb_bucket_on_error_get(VALUE self)
|
639
|
+
{
|
640
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
641
|
+
|
642
|
+
if (rb_block_given_p()) {
|
643
|
+
return cb_bucket_on_error_set(self, rb_block_proc());
|
644
|
+
} else {
|
645
|
+
return bucket->on_error_proc;
|
646
|
+
}
|
647
|
+
}
|
648
|
+
|
649
|
+
VALUE
|
650
|
+
cb_bucket_timeout_get(VALUE self)
|
651
|
+
{
|
652
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
653
|
+
return ULONG2NUM(bucket->timeout);
|
654
|
+
}
|
655
|
+
|
656
|
+
VALUE
|
657
|
+
cb_bucket_timeout_set(VALUE self, VALUE val)
|
658
|
+
{
|
659
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
660
|
+
VALUE tmval;
|
661
|
+
|
662
|
+
bucket->timeout = (uint32_t)NUM2ULONG(val);
|
663
|
+
lcb_set_timeout(bucket->handle, bucket->timeout);
|
664
|
+
tmval = ULONG2NUM(bucket->timeout);
|
665
|
+
|
666
|
+
return tmval;
|
667
|
+
}
|
668
|
+
|
669
|
+
VALUE
|
670
|
+
cb_bucket_key_prefix_get(VALUE self)
|
671
|
+
{
|
672
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
673
|
+
return bucket->key_prefix_val;
|
674
|
+
}
|
675
|
+
|
676
|
+
VALUE
|
677
|
+
cb_bucket_key_prefix_set(VALUE self, VALUE val)
|
678
|
+
{
|
679
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
680
|
+
|
681
|
+
bucket->key_prefix = strdup(StringValueCStr(val));
|
682
|
+
bucket->key_prefix_val = STR_NEW_CSTR(bucket->key_prefix);
|
683
|
+
|
684
|
+
return bucket->key_prefix_val;
|
685
|
+
}
|
686
|
+
|
687
|
+
/* Document-method: hostname
|
688
|
+
*
|
689
|
+
* @since 1.0.0
|
690
|
+
*
|
691
|
+
* @return [String] the host name of the management interface (default: "localhost")
|
692
|
+
*/
|
693
|
+
VALUE
|
694
|
+
cb_bucket_hostname_get(VALUE self)
|
695
|
+
{
|
696
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
697
|
+
if (bucket->handle) {
|
698
|
+
xfree(bucket->hostname);
|
699
|
+
bucket->hostname = strdup(lcb_get_host(bucket->handle));
|
700
|
+
if (bucket->hostname == NULL) {
|
701
|
+
rb_raise(eClientNoMemoryError, "failed to allocate memory for Bucket");
|
702
|
+
}
|
703
|
+
}
|
704
|
+
return STR_NEW_CSTR(bucket->hostname);
|
705
|
+
}
|
706
|
+
|
707
|
+
/* Document-method: port
|
708
|
+
*
|
709
|
+
* @since 1.0.0
|
710
|
+
*
|
711
|
+
* @return [Fixnum] the port number of the management interface (default: 8091)
|
712
|
+
*/
|
713
|
+
VALUE
|
714
|
+
cb_bucket_port_get(VALUE self)
|
715
|
+
{
|
716
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
717
|
+
if (bucket->handle) {
|
718
|
+
bucket->port = atoi(lcb_get_port(bucket->handle));
|
719
|
+
}
|
720
|
+
return UINT2NUM(bucket->port);
|
721
|
+
}
|
722
|
+
|
723
|
+
/* Document-method: authority
|
724
|
+
*
|
725
|
+
* @since 1.0.0
|
726
|
+
*
|
727
|
+
* @return [String] host with port
|
728
|
+
*/
|
729
|
+
VALUE
|
730
|
+
cb_bucket_authority_get(VALUE self)
|
731
|
+
{
|
732
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
733
|
+
size_t len;
|
734
|
+
|
735
|
+
(void)cb_bucket_hostname_get(self);
|
736
|
+
(void)cb_bucket_port_get(self);
|
737
|
+
len = strlen(bucket->hostname) + 10;
|
738
|
+
bucket->authority = xcalloc(len, sizeof(char));
|
739
|
+
if (bucket->authority == NULL) {
|
740
|
+
rb_raise(eClientNoMemoryError, "failed to allocate memory for Bucket");
|
741
|
+
}
|
742
|
+
snprintf(bucket->authority, len, "%s:%u", bucket->hostname, bucket->port);
|
743
|
+
return STR_NEW_CSTR(bucket->authority);
|
744
|
+
}
|
745
|
+
|
746
|
+
/* Document-method: bucket
|
747
|
+
*
|
748
|
+
* @since 1.0.0
|
749
|
+
*
|
750
|
+
* @return [String] the bucket name
|
751
|
+
*/
|
752
|
+
VALUE
|
753
|
+
cb_bucket_bucket_get(VALUE self)
|
754
|
+
{
|
755
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
756
|
+
return STR_NEW_CSTR(bucket->bucket);
|
757
|
+
}
|
758
|
+
|
759
|
+
/* Document-method: pool
|
760
|
+
*
|
761
|
+
* @since 1.0.0
|
762
|
+
*
|
763
|
+
* @return [String] the pool name (usually "default")
|
764
|
+
*/
|
765
|
+
VALUE
|
766
|
+
cb_bucket_pool_get(VALUE self)
|
767
|
+
{
|
768
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
769
|
+
return STR_NEW_CSTR(bucket->pool);
|
770
|
+
}
|
771
|
+
|
772
|
+
/* Document-method: username
|
773
|
+
*
|
774
|
+
* @since 1.0.0
|
775
|
+
*
|
776
|
+
* @return [String] the username for protected buckets (usually matches
|
777
|
+
* the bucket name)
|
778
|
+
*/
|
779
|
+
VALUE
|
780
|
+
cb_bucket_username_get(VALUE self)
|
781
|
+
{
|
782
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
783
|
+
return STR_NEW_CSTR(bucket->username);
|
784
|
+
}
|
785
|
+
|
786
|
+
/* Document-method: password
|
787
|
+
*
|
788
|
+
* @since 1.0.0
|
789
|
+
*
|
790
|
+
* @return [String] the password for protected buckets
|
791
|
+
*/
|
792
|
+
VALUE
|
793
|
+
cb_bucket_password_get(VALUE self)
|
794
|
+
{
|
795
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
796
|
+
return STR_NEW_CSTR(bucket->password);
|
797
|
+
}
|
798
|
+
|
799
|
+
/* Document-method: environment
|
800
|
+
*
|
801
|
+
* @since 1.2.0
|
802
|
+
*
|
803
|
+
* @see Bucket#initialize
|
804
|
+
*
|
805
|
+
* @return [Symbol] the environment (+:development+ or +:production+)
|
806
|
+
*/
|
807
|
+
VALUE
|
808
|
+
cb_bucket_environment_get(VALUE self)
|
809
|
+
{
|
810
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
811
|
+
return bucket->environment;
|
812
|
+
}
|
813
|
+
/* Document-method: num_replicas
|
814
|
+
*
|
815
|
+
* @since 1.2.0.dp6
|
816
|
+
*
|
817
|
+
* The numbers of the replicas for each node in the cluster
|
818
|
+
*
|
819
|
+
* @return [Fixnum]
|
820
|
+
*/
|
821
|
+
VALUE
|
822
|
+
cb_bucket_num_replicas_get(VALUE self)
|
823
|
+
{
|
824
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
825
|
+
int32_t nr = lcb_get_num_replicas(bucket->handle);
|
826
|
+
if (nr < 0) {
|
827
|
+
return Qnil;
|
828
|
+
} else {
|
829
|
+
return INT2FIX(nr);
|
830
|
+
}
|
831
|
+
}
|
832
|
+
/* Document-method: default_observe_timeout
|
833
|
+
*
|
834
|
+
* @since 1.2.0.dp6
|
835
|
+
*
|
836
|
+
* Get default timeout value for {Bucket#observe_and_wait} operation in
|
837
|
+
* microseconds
|
838
|
+
*
|
839
|
+
* @return [Fixnum]
|
840
|
+
*/
|
841
|
+
VALUE
|
842
|
+
cb_bucket_default_observe_timeout_get(VALUE self)
|
843
|
+
{
|
844
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
845
|
+
return INT2FIX(bucket->default_observe_timeout);
|
846
|
+
}
|
847
|
+
|
848
|
+
/* Document-method: default_observe_timeout=
|
849
|
+
*
|
850
|
+
* @since 1.2.0.dp6
|
851
|
+
*
|
852
|
+
* Set default timeout value for {Bucket#observe_and_wait} operation in
|
853
|
+
* microseconds
|
854
|
+
*
|
855
|
+
* @return [Fixnum]
|
856
|
+
*/
|
857
|
+
VALUE
|
858
|
+
cb_bucket_default_observe_timeout_set(VALUE self, VALUE val)
|
859
|
+
{
|
860
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
861
|
+
bucket->default_observe_timeout = FIX2INT(val);
|
862
|
+
return val;
|
863
|
+
}
|
864
|
+
/* Document-method: url
|
865
|
+
*
|
866
|
+
* @since 1.0.0
|
867
|
+
*
|
868
|
+
* @return [String] the address of the cluster management interface
|
869
|
+
*/
|
870
|
+
VALUE
|
871
|
+
cb_bucket_url_get(VALUE self)
|
872
|
+
{
|
873
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
874
|
+
VALUE str;
|
875
|
+
|
876
|
+
(void)cb_bucket_authority_get(self);
|
877
|
+
str = rb_str_buf_new2("http://");
|
878
|
+
rb_str_buf_cat2(str, bucket->authority);
|
879
|
+
rb_str_buf_cat2(str, "/pools/");
|
880
|
+
rb_str_buf_cat2(str, bucket->pool);
|
881
|
+
rb_str_buf_cat2(str, "/buckets/");
|
882
|
+
rb_str_buf_cat2(str, bucket->bucket);
|
883
|
+
rb_str_buf_cat2(str, "/");
|
884
|
+
return str;
|
885
|
+
}
|
886
|
+
|
887
|
+
/*
|
888
|
+
* Returns a string containing a human-readable representation of the
|
889
|
+
* {Bucket}.
|
890
|
+
*
|
891
|
+
* @since 1.0.0
|
892
|
+
*
|
893
|
+
* @return [String]
|
894
|
+
*/
|
895
|
+
VALUE
|
896
|
+
cb_bucket_inspect(VALUE self)
|
897
|
+
{
|
898
|
+
VALUE str;
|
899
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
900
|
+
char buf[200];
|
901
|
+
|
902
|
+
str = rb_str_buf_new2("#<");
|
903
|
+
rb_str_buf_cat2(str, rb_obj_classname(self));
|
904
|
+
snprintf(buf, 25, ":%p \"", (void *)self);
|
905
|
+
(void)cb_bucket_authority_get(self);
|
906
|
+
rb_str_buf_cat2(str, buf);
|
907
|
+
rb_str_buf_cat2(str, "http://");
|
908
|
+
rb_str_buf_cat2(str, bucket->authority);
|
909
|
+
rb_str_buf_cat2(str, "/pools/");
|
910
|
+
rb_str_buf_cat2(str, bucket->pool);
|
911
|
+
rb_str_buf_cat2(str, "/buckets/");
|
912
|
+
rb_str_buf_cat2(str, bucket->bucket);
|
913
|
+
rb_str_buf_cat2(str, "/");
|
914
|
+
snprintf(buf, 150, "\" default_format=:%s, default_flags=0x%x, quiet=%s, connected=%s, timeout=%u",
|
915
|
+
rb_id2name(SYM2ID(bucket->default_format)),
|
916
|
+
bucket->default_flags,
|
917
|
+
bucket->quiet ? "true" : "false",
|
918
|
+
bucket->handle ? "true" : "false",
|
919
|
+
bucket->timeout);
|
920
|
+
rb_str_buf_cat2(str, buf);
|
921
|
+
if (bucket->key_prefix) {
|
922
|
+
rb_str_buf_cat2(str, ", key_prefix=");
|
923
|
+
rb_str_append(str, rb_inspect(bucket->key_prefix_val));
|
924
|
+
}
|
925
|
+
rb_str_buf_cat2(str, ">");
|
926
|
+
|
927
|
+
return str;
|
928
|
+
}
|
929
|
+
|
930
|
+
static void
|
931
|
+
do_loop(struct bucket_st *bucket)
|
932
|
+
{
|
933
|
+
lcb_wait(bucket->handle);
|
934
|
+
bucket->nbytes = 0;
|
935
|
+
}
|
936
|
+
|
937
|
+
void
|
938
|
+
maybe_do_loop(struct bucket_st *bucket)
|
939
|
+
{
|
940
|
+
if (bucket->threshold != 0 && bucket->nbytes > bucket->threshold) {
|
941
|
+
do_loop(bucket);
|
942
|
+
}
|
943
|
+
}
|
944
|
+
|
945
|
+
static VALUE
|
946
|
+
do_run(VALUE *args)
|
947
|
+
{
|
948
|
+
VALUE self = args[0], opts = args[1], proc = args[2], exc;
|
949
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
950
|
+
|
951
|
+
if (bucket->handle == NULL) {
|
952
|
+
rb_raise(eConnectError, "closed connection");
|
953
|
+
}
|
954
|
+
if (bucket->async) {
|
955
|
+
rb_raise(eInvalidError, "nested #run");
|
956
|
+
}
|
957
|
+
bucket->threshold = 0;
|
958
|
+
if (opts != Qnil) {
|
959
|
+
VALUE arg;
|
960
|
+
Check_Type(opts, T_HASH);
|
961
|
+
arg = rb_hash_aref(opts, sym_send_threshold);
|
962
|
+
if (arg != Qnil) {
|
963
|
+
bucket->threshold = (uint32_t)NUM2ULONG(arg);
|
964
|
+
}
|
965
|
+
}
|
966
|
+
bucket->async = 1;
|
967
|
+
cb_proc_call(proc, 1, self);
|
968
|
+
do_loop(bucket);
|
969
|
+
if (bucket->exception != Qnil) {
|
970
|
+
exc = bucket->exception;
|
971
|
+
bucket->exception = Qnil;
|
972
|
+
rb_exc_raise(exc);
|
973
|
+
}
|
974
|
+
return Qnil;
|
975
|
+
}
|
976
|
+
|
977
|
+
static VALUE
|
978
|
+
ensure_run(VALUE *args)
|
979
|
+
{
|
980
|
+
VALUE self = args[0];
|
981
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
982
|
+
|
983
|
+
bucket->async = 0;
|
984
|
+
return Qnil;
|
985
|
+
}
|
986
|
+
|
987
|
+
/*
|
988
|
+
* Run the event loop.
|
989
|
+
*
|
990
|
+
* @since 1.0.0
|
991
|
+
*
|
992
|
+
* @param [Hash] options The options for operation for connection
|
993
|
+
* @option options [Fixnum] :send_threshold (0) if the internal command
|
994
|
+
* buffer will exceeds this value, then the library will start network
|
995
|
+
* interaction and block the current thread until all scheduled commands
|
996
|
+
* will be completed.
|
997
|
+
*
|
998
|
+
* @yieldparam [Bucket] bucket the bucket instance
|
999
|
+
*
|
1000
|
+
* @example Use block to run the loop
|
1001
|
+
* c = Couchbase.new
|
1002
|
+
* c.run do
|
1003
|
+
* c.get("foo") {|ret| puts ret.value}
|
1004
|
+
* end
|
1005
|
+
*
|
1006
|
+
* @example Use lambda to run the loop
|
1007
|
+
* c = Couchbase.new
|
1008
|
+
* operations = lambda do |c|
|
1009
|
+
* c.get("foo") {|ret| puts ret.value}
|
1010
|
+
* end
|
1011
|
+
* c.run(&operations)
|
1012
|
+
*
|
1013
|
+
* @example Use threshold to send out commands automatically
|
1014
|
+
* c = Couchbase.connect
|
1015
|
+
* sent = 0
|
1016
|
+
* c.run(:send_threshold => 8192) do # 8Kb
|
1017
|
+
* c.set("foo1", "x" * 100) {|r| sent += 1}
|
1018
|
+
* # 128 bytes buffered, sent is 0 now
|
1019
|
+
* c.set("foo2", "x" * 10000) {|r| sent += 1}
|
1020
|
+
* # 10028 bytes added, sent is 2 now
|
1021
|
+
* c.set("foo3", "x" * 100) {|r| sent += 1}
|
1022
|
+
* end
|
1023
|
+
* # all commands were executed and sent is 3 now
|
1024
|
+
*
|
1025
|
+
* @return [nil]
|
1026
|
+
*
|
1027
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
1028
|
+
*/
|
1029
|
+
VALUE
|
1030
|
+
cb_bucket_run(int argc, VALUE *argv, VALUE self)
|
1031
|
+
{
|
1032
|
+
VALUE args[3];
|
1033
|
+
|
1034
|
+
rb_need_block();
|
1035
|
+
args[0] = self;
|
1036
|
+
rb_scan_args(argc, argv, "01&", &args[1], &args[2]);
|
1037
|
+
rb_ensure(do_run, (VALUE)args, ensure_run, (VALUE)args);
|
1038
|
+
return Qnil;
|
1039
|
+
}
|
1040
|
+
|
1041
|
+
/*
|
1042
|
+
* Stop the event loop.
|
1043
|
+
*
|
1044
|
+
* @since 1.2.0
|
1045
|
+
*
|
1046
|
+
* @example Breakout the event loop when 5th request is completed
|
1047
|
+
* c = Couchbase.connect
|
1048
|
+
* c.run do
|
1049
|
+
* 10.times do |ii|
|
1050
|
+
* c.get("foo") do |ret|
|
1051
|
+
* puts ii
|
1052
|
+
* c.stop if ii == 5
|
1053
|
+
* end
|
1054
|
+
* end
|
1055
|
+
* end
|
1056
|
+
*
|
1057
|
+
* @return [nil]
|
1058
|
+
*/
|
1059
|
+
VALUE
|
1060
|
+
cb_bucket_stop(VALUE self)
|
1061
|
+
{
|
1062
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
1063
|
+
bucket->io->stop_event_loop(bucket->io);
|
1064
|
+
return Qnil;
|
1065
|
+
}
|
1066
|
+
|
1067
|
+
/*
|
1068
|
+
* Close the connection to the cluster
|
1069
|
+
*
|
1070
|
+
* @since 1.1.0
|
1071
|
+
*
|
1072
|
+
* @return [true]
|
1073
|
+
*
|
1074
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
1075
|
+
*/
|
1076
|
+
VALUE
|
1077
|
+
cb_bucket_disconnect(VALUE self)
|
1078
|
+
{
|
1079
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
1080
|
+
|
1081
|
+
if (bucket->handle) {
|
1082
|
+
lcb_destroy(bucket->handle);
|
1083
|
+
bucket->handle = NULL;
|
1084
|
+
bucket->io = NULL;
|
1085
|
+
return Qtrue;
|
1086
|
+
} else {
|
1087
|
+
rb_raise(eConnectError, "closed connection");
|
1088
|
+
return Qfalse;
|
1089
|
+
}
|
1090
|
+
}
|
1091
|
+
|
1092
|
+
|