couchbase 1.1.0-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 +14 -0
- data/.travis.yml +11 -0
- data/.yardopts +5 -0
- data/Gemfile +4 -0
- data/HISTORY.markdown +75 -0
- data/LICENSE +201 -0
- data/README.markdown +370 -0
- data/Rakefile +22 -0
- data/couchbase.gemspec +47 -0
- data/ext/couchbase_ext/couchbase_ext.c +3464 -0
- data/ext/couchbase_ext/extconf.rb +113 -0
- data/lib/couchbase.rb +81 -0
- data/lib/couchbase/bucket.rb +72 -0
- data/lib/couchbase/version.rb +21 -0
- data/tasks/benchmark.rake +6 -0
- data/tasks/compile.rake +124 -0
- data/tasks/doc.rake +27 -0
- data/tasks/test.rake +94 -0
- data/tasks/util.rake +21 -0
- data/test/profile/.gitignore +1 -0
- data/test/profile/Gemfile +6 -0
- data/test/profile/benchmark.rb +195 -0
- data/test/setup.rb +167 -0
- data/test/test_arithmetic.rb +109 -0
- data/test/test_async.rb +235 -0
- data/test/test_bucket.rb +227 -0
- data/test/test_cas.rb +59 -0
- data/test/test_couchbase.rb +28 -0
- data/test/test_delete.rb +63 -0
- data/test/test_errors.rb +82 -0
- data/test/test_flush.rb +49 -0
- data/test/test_format.rb +98 -0
- data/test/test_get.rb +311 -0
- data/test/test_stats.rb +57 -0
- data/test/test_store.rb +186 -0
- data/test/test_touch.rb +69 -0
- data/test/test_version.rb +52 -0
- metadata +197 -0
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Author:: Couchbase <info@couchbase.com>
|
2
|
+
# Copyright:: 2011, 2012 Couchbase, Inc.
|
3
|
+
# License:: Apache License, Version 2.0
|
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
|
+
require 'bundler/gem_tasks'
|
19
|
+
|
20
|
+
Dir['tasks/*.rake'].sort.each { |f| load f }
|
21
|
+
|
22
|
+
task :default => [:clobber, :compile, :test]
|
data/couchbase.gemspec
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# Author:: Couchbase <info@couchbase.com>
|
3
|
+
# Copyright:: 2011, 2012 Couchbase, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
$:.push File.expand_path('../lib', __FILE__)
|
20
|
+
require 'couchbase/version'
|
21
|
+
|
22
|
+
Gem::Specification.new do |s|
|
23
|
+
s.name = 'couchbase'
|
24
|
+
s.version = Couchbase::VERSION
|
25
|
+
s.author = 'Couchbase'
|
26
|
+
s.email = 'support@couchbase.com'
|
27
|
+
s.license = 'ASL-2'
|
28
|
+
s.homepage = 'http://couchbase.org'
|
29
|
+
s.summary = %q{Couchbase ruby driver}
|
30
|
+
s.description = %q{The official client library for use with Couchbase Server.}
|
31
|
+
|
32
|
+
s.files = `git ls-files`.split("\n")
|
33
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
34
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
35
|
+
s.extensions = `git ls-files -- ext/**/extconf.rb`.split("\n")
|
36
|
+
s.require_paths = ['lib']
|
37
|
+
|
38
|
+
s.add_runtime_dependency 'yajl-ruby', '~> 1.1.0'
|
39
|
+
|
40
|
+
s.add_development_dependency 'rake', '~> 0.8.7'
|
41
|
+
s.add_development_dependency 'minitest'
|
42
|
+
s.add_development_dependency 'rake-compiler', '>= 0.7.5'
|
43
|
+
s.add_development_dependency 'rdiscount'
|
44
|
+
s.add_development_dependency 'yard'
|
45
|
+
s.add_development_dependency 'mini_portile'
|
46
|
+
s.add_development_dependency RUBY_VERSION =~ /^1\.9/ ? 'ruby-debug19' : 'ruby-debug'
|
47
|
+
end
|
@@ -0,0 +1,3464 @@
|
|
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 <ruby.h>
|
19
|
+
#ifndef RUBY_ST_H
|
20
|
+
#include <st.h>
|
21
|
+
#endif
|
22
|
+
|
23
|
+
#include <time.h>
|
24
|
+
#include <libcouchbase/couchbase.h>
|
25
|
+
#include "couchbase_config.h"
|
26
|
+
|
27
|
+
#ifdef HAVE_STDARG_PROTOTYPES
|
28
|
+
#include <stdarg.h>
|
29
|
+
#define va_init_list(a,b) va_start(a,b)
|
30
|
+
#else
|
31
|
+
#include <varargs.h>
|
32
|
+
#define va_init_list(a,b) va_start(a)
|
33
|
+
#endif
|
34
|
+
|
35
|
+
#define debug_object(OBJ) \
|
36
|
+
rb_funcall(rb_stderr, rb_intern("print"), 1, rb_funcall(OBJ, rb_intern("object_id"), 0)); \
|
37
|
+
rb_funcall(rb_stderr, rb_intern("print"), 1, rb_str_new2(" ")); \
|
38
|
+
rb_funcall(rb_stderr, rb_intern("print"), 1, rb_funcall(OBJ, rb_intern("class"), 0)); \
|
39
|
+
rb_funcall(rb_stderr, rb_intern("print"), 1, rb_str_new2(" ")); \
|
40
|
+
rb_funcall(rb_stderr, rb_intern("puts"), 1, rb_funcall(OBJ, rb_intern("inspect"), 0));
|
41
|
+
|
42
|
+
#define FMT_MASK 0x3
|
43
|
+
#define FMT_DOCUMENT 0x0
|
44
|
+
#define FMT_MARSHAL 0x1
|
45
|
+
#define FMT_PLAIN 0x2
|
46
|
+
|
47
|
+
typedef struct
|
48
|
+
{
|
49
|
+
libcouchbase_t handle;
|
50
|
+
struct libcouchbase_io_opt_st *io;
|
51
|
+
uint16_t port;
|
52
|
+
char *authority;
|
53
|
+
char *hostname;
|
54
|
+
char *pool;
|
55
|
+
char *bucket;
|
56
|
+
char *username;
|
57
|
+
char *password;
|
58
|
+
int async;
|
59
|
+
int quiet;
|
60
|
+
long seqno;
|
61
|
+
VALUE default_format; /* should update +default_flags+ on change */
|
62
|
+
uint32_t default_flags;
|
63
|
+
time_t default_ttl;
|
64
|
+
uint32_t timeout;
|
65
|
+
VALUE exception; /* error delivered by error_callback */
|
66
|
+
VALUE on_error_proc; /* is using to deliver errors in async mode */
|
67
|
+
} bucket_t;
|
68
|
+
|
69
|
+
typedef struct
|
70
|
+
{
|
71
|
+
bucket_t* bucket;
|
72
|
+
int extended;
|
73
|
+
VALUE proc;
|
74
|
+
void *rv;
|
75
|
+
VALUE exception;
|
76
|
+
int quiet;
|
77
|
+
int arithm; /* incr: +1, decr: -1, other: 0 */
|
78
|
+
} context_t;
|
79
|
+
|
80
|
+
struct key_traits
|
81
|
+
{
|
82
|
+
VALUE keys_ary;
|
83
|
+
size_t nkeys;
|
84
|
+
char **keys;
|
85
|
+
libcouchbase_size_t *lens;
|
86
|
+
time_t *ttls;
|
87
|
+
int extended;
|
88
|
+
int explicit_ttl;
|
89
|
+
int quiet;
|
90
|
+
int mgat;
|
91
|
+
};
|
92
|
+
|
93
|
+
static VALUE mCouchbase, mError, mJSON, mURI, mMarshal, cBucket, cResult;
|
94
|
+
static VALUE object_space;
|
95
|
+
|
96
|
+
static ID sym_add,
|
97
|
+
sym_append,
|
98
|
+
sym_bucket,
|
99
|
+
sym_cas,
|
100
|
+
sym_create,
|
101
|
+
sym_decrement,
|
102
|
+
sym_default_flags,
|
103
|
+
sym_default_format,
|
104
|
+
sym_default_ttl,
|
105
|
+
sym_delete,
|
106
|
+
sym_document,
|
107
|
+
sym_extended,
|
108
|
+
sym_flags,
|
109
|
+
sym_flush,
|
110
|
+
sym_format,
|
111
|
+
sym_get,
|
112
|
+
sym_hostname,
|
113
|
+
sym_increment,
|
114
|
+
sym_initial,
|
115
|
+
sym_marshal,
|
116
|
+
sym_password,
|
117
|
+
sym_plain,
|
118
|
+
sym_pool,
|
119
|
+
sym_port,
|
120
|
+
sym_prepend,
|
121
|
+
sym_quiet,
|
122
|
+
sym_replace,
|
123
|
+
sym_set,
|
124
|
+
sym_stats,
|
125
|
+
sym_timeout,
|
126
|
+
sym_touch,
|
127
|
+
sym_ttl,
|
128
|
+
sym_username,
|
129
|
+
sym_version,
|
130
|
+
id_arity,
|
131
|
+
id_call,
|
132
|
+
id_dump,
|
133
|
+
id_flatten_bang,
|
134
|
+
id_has_key_p,
|
135
|
+
id_host,
|
136
|
+
id_iv_cas,
|
137
|
+
id_iv_error,
|
138
|
+
id_iv_flags,
|
139
|
+
id_iv_key,
|
140
|
+
id_iv_node,
|
141
|
+
id_iv_operation,
|
142
|
+
id_iv_value,
|
143
|
+
id_load,
|
144
|
+
id_match,
|
145
|
+
id_parse,
|
146
|
+
id_password,
|
147
|
+
id_path,
|
148
|
+
id_port,
|
149
|
+
id_scheme,
|
150
|
+
id_to_s,
|
151
|
+
id_user;
|
152
|
+
|
153
|
+
/* base error */
|
154
|
+
static VALUE eBaseError;
|
155
|
+
static VALUE eValueFormatError;
|
156
|
+
|
157
|
+
/* libcouchbase errors */
|
158
|
+
/*LIBCOUCHBASE_SUCCESS = 0x00*/
|
159
|
+
/*LIBCOUCHBASE_AUTH_CONTINUE = 0x01*/
|
160
|
+
static VALUE eAuthError; /*LIBCOUCHBASE_AUTH_ERROR = 0x02*/
|
161
|
+
static VALUE eDeltaBadvalError; /*LIBCOUCHBASE_DELTA_BADVAL = 0x03*/
|
162
|
+
static VALUE eTooBigError; /*LIBCOUCHBASE_E2BIG = 0x04*/
|
163
|
+
static VALUE eBusyError; /*LIBCOUCHBASE_EBUSY = 0x05*/
|
164
|
+
static VALUE eInternalError; /*LIBCOUCHBASE_EINTERNAL = 0x06*/
|
165
|
+
static VALUE eInvalidError; /*LIBCOUCHBASE_EINVAL = 0x07*/
|
166
|
+
static VALUE eNoMemoryError; /*LIBCOUCHBASE_ENOMEM = 0x08*/
|
167
|
+
static VALUE eRangeError; /*LIBCOUCHBASE_ERANGE = 0x09*/
|
168
|
+
static VALUE eLibcouchbaseError; /*LIBCOUCHBASE_ERROR = 0x0a*/
|
169
|
+
static VALUE eTmpFailError; /*LIBCOUCHBASE_ETMPFAIL = 0x0b*/
|
170
|
+
static VALUE eKeyExistsError; /*LIBCOUCHBASE_KEY_EEXISTS = 0x0c*/
|
171
|
+
static VALUE eNotFoundError; /*LIBCOUCHBASE_KEY_ENOENT = 0x0d*/
|
172
|
+
static VALUE eLibeventError; /*LIBCOUCHBASE_LIBEVENT_ERROR = 0x0e*/
|
173
|
+
static VALUE eNetworkError; /*LIBCOUCHBASE_NETWORK_ERROR = 0x0f*/
|
174
|
+
static VALUE eNotMyVbucketError; /*LIBCOUCHBASE_NOT_MY_VBUCKET = 0x10*/
|
175
|
+
static VALUE eNotStoredError; /*LIBCOUCHBASE_NOT_STORED = 0x11*/
|
176
|
+
static VALUE eNotSupportedError; /*LIBCOUCHBASE_NOT_SUPPORTED = 0x12*/
|
177
|
+
static VALUE eUnknownCommandError; /*LIBCOUCHBASE_UNKNOWN_COMMAND = 0x13*/
|
178
|
+
static VALUE eUnknownHostError; /*LIBCOUCHBASE_UNKNOWN_HOST = 0x14*/
|
179
|
+
static VALUE eProtocolError; /*LIBCOUCHBASE_PROTOCOL_ERROR = 0x15*/
|
180
|
+
static VALUE eTimeoutError; /*LIBCOUCHBASE_ETIMEDOUT = 0x16*/
|
181
|
+
static VALUE eConnectError; /*LIBCOUCHBASE_CONNECT_ERROR = 0x17*/
|
182
|
+
static VALUE eBucketNotFoundError; /*LIBCOUCHBASE_BUCKET_ENOENT = 0x18*/
|
183
|
+
|
184
|
+
static VALUE
|
185
|
+
cb_proc_call(VALUE recv, int argc, ...)
|
186
|
+
{
|
187
|
+
VALUE *argv;
|
188
|
+
va_list ar;
|
189
|
+
int arity;
|
190
|
+
int ii;
|
191
|
+
|
192
|
+
arity = FIX2INT(rb_funcall(recv, id_arity, 0));
|
193
|
+
if (arity > 0) {
|
194
|
+
va_init_list(ar, argc);
|
195
|
+
argv = ALLOCA_N(VALUE, argc);
|
196
|
+
for (ii = 0; ii < arity; ++ii) {
|
197
|
+
if (ii < argc) {
|
198
|
+
argv[ii] = va_arg(ar, VALUE);
|
199
|
+
} else {
|
200
|
+
argv[ii] = Qnil;
|
201
|
+
}
|
202
|
+
}
|
203
|
+
va_end(ar);
|
204
|
+
} else {
|
205
|
+
arity = 0;
|
206
|
+
argv = NULL;
|
207
|
+
}
|
208
|
+
return rb_funcall2(recv, id_call, arity, argv);
|
209
|
+
}
|
210
|
+
|
211
|
+
/* Helper to conver return code from libcouchbase to meaningful exception.
|
212
|
+
* Returns nil if the code considering successful and exception object
|
213
|
+
* otherwise. Store given string to exceptions as message, and also
|
214
|
+
* initialize +error+ attribute with given return code. */
|
215
|
+
static VALUE
|
216
|
+
cb_check_error(libcouchbase_error_t rc, const char *msg, VALUE key)
|
217
|
+
{
|
218
|
+
VALUE klass, exc, str;
|
219
|
+
char buf[300];
|
220
|
+
|
221
|
+
if (rc == LIBCOUCHBASE_SUCCESS || rc == LIBCOUCHBASE_AUTH_CONTINUE) {
|
222
|
+
return Qnil;
|
223
|
+
}
|
224
|
+
switch (rc) {
|
225
|
+
case LIBCOUCHBASE_AUTH_ERROR:
|
226
|
+
klass = eAuthError;
|
227
|
+
break;
|
228
|
+
case LIBCOUCHBASE_DELTA_BADVAL:
|
229
|
+
klass = eDeltaBadvalError;
|
230
|
+
break;
|
231
|
+
case LIBCOUCHBASE_E2BIG:
|
232
|
+
klass = eTooBigError;
|
233
|
+
break;
|
234
|
+
case LIBCOUCHBASE_EBUSY:
|
235
|
+
klass = eBusyError;
|
236
|
+
break;
|
237
|
+
case LIBCOUCHBASE_EINTERNAL:
|
238
|
+
klass = eInternalError;
|
239
|
+
break;
|
240
|
+
case LIBCOUCHBASE_EINVAL:
|
241
|
+
klass = eInvalidError;
|
242
|
+
break;
|
243
|
+
case LIBCOUCHBASE_ENOMEM:
|
244
|
+
klass = eNoMemoryError;
|
245
|
+
break;
|
246
|
+
case LIBCOUCHBASE_ERANGE:
|
247
|
+
klass = eRangeError;
|
248
|
+
break;
|
249
|
+
case LIBCOUCHBASE_ETMPFAIL:
|
250
|
+
klass = eTmpFailError;
|
251
|
+
break;
|
252
|
+
case LIBCOUCHBASE_KEY_EEXISTS:
|
253
|
+
klass = eKeyExistsError;
|
254
|
+
break;
|
255
|
+
case LIBCOUCHBASE_KEY_ENOENT:
|
256
|
+
klass = eNotFoundError;
|
257
|
+
break;
|
258
|
+
case LIBCOUCHBASE_LIBEVENT_ERROR:
|
259
|
+
klass = eLibeventError;
|
260
|
+
break;
|
261
|
+
case LIBCOUCHBASE_NETWORK_ERROR:
|
262
|
+
klass = eNetworkError;
|
263
|
+
break;
|
264
|
+
case LIBCOUCHBASE_NOT_MY_VBUCKET:
|
265
|
+
klass = eNotMyVbucketError;
|
266
|
+
break;
|
267
|
+
case LIBCOUCHBASE_NOT_STORED:
|
268
|
+
klass = eNotStoredError;
|
269
|
+
break;
|
270
|
+
case LIBCOUCHBASE_NOT_SUPPORTED:
|
271
|
+
klass = eNotSupportedError;
|
272
|
+
break;
|
273
|
+
case LIBCOUCHBASE_UNKNOWN_COMMAND:
|
274
|
+
klass = eUnknownCommandError;
|
275
|
+
break;
|
276
|
+
case LIBCOUCHBASE_UNKNOWN_HOST:
|
277
|
+
klass = eUnknownHostError;
|
278
|
+
break;
|
279
|
+
case LIBCOUCHBASE_PROTOCOL_ERROR:
|
280
|
+
klass = eProtocolError;
|
281
|
+
break;
|
282
|
+
case LIBCOUCHBASE_ETIMEDOUT:
|
283
|
+
klass = eTimeoutError;
|
284
|
+
break;
|
285
|
+
case LIBCOUCHBASE_CONNECT_ERROR:
|
286
|
+
klass = eConnectError;
|
287
|
+
break;
|
288
|
+
case LIBCOUCHBASE_BUCKET_ENOENT:
|
289
|
+
klass = eBucketNotFoundError;
|
290
|
+
break;
|
291
|
+
case LIBCOUCHBASE_ERROR:
|
292
|
+
/* fall through */
|
293
|
+
default:
|
294
|
+
klass = eLibcouchbaseError;
|
295
|
+
}
|
296
|
+
|
297
|
+
str = rb_str_buf_new2(msg ? msg : "");
|
298
|
+
rb_str_buf_cat2(str, " (");
|
299
|
+
if (key != Qnil) {
|
300
|
+
snprintf(buf, 300, "key=\"%s\", ", RSTRING_PTR(key));
|
301
|
+
rb_str_buf_cat2(str, buf);
|
302
|
+
}
|
303
|
+
snprintf(buf, 300, "error=0x%02x)", rc);
|
304
|
+
rb_str_buf_cat2(str, buf);
|
305
|
+
exc = rb_exc_new3(klass, str);
|
306
|
+
rb_ivar_set(exc, id_iv_error, INT2FIX(rc));
|
307
|
+
rb_ivar_set(exc, id_iv_key, key);
|
308
|
+
rb_ivar_set(exc, id_iv_cas, Qnil);
|
309
|
+
rb_ivar_set(exc, id_iv_operation, Qnil);
|
310
|
+
return exc;
|
311
|
+
}
|
312
|
+
|
313
|
+
static inline uint32_t
|
314
|
+
flags_set_format(uint32_t flags, ID format)
|
315
|
+
{
|
316
|
+
flags &= ~((uint32_t)FMT_MASK); /* clear format bits */
|
317
|
+
|
318
|
+
if (format == sym_document) {
|
319
|
+
return flags | FMT_DOCUMENT;
|
320
|
+
} else if (format == sym_marshal) {
|
321
|
+
return flags | FMT_MARSHAL;
|
322
|
+
} else if (format == sym_plain) {
|
323
|
+
return flags | FMT_PLAIN;
|
324
|
+
}
|
325
|
+
return flags; /* document is the default */
|
326
|
+
}
|
327
|
+
|
328
|
+
static inline ID
|
329
|
+
flags_get_format(uint32_t flags)
|
330
|
+
{
|
331
|
+
flags &= FMT_MASK; /* select format bits */
|
332
|
+
|
333
|
+
switch (flags) {
|
334
|
+
case FMT_DOCUMENT:
|
335
|
+
return sym_document;
|
336
|
+
case FMT_MARSHAL:
|
337
|
+
return sym_marshal;
|
338
|
+
case FMT_PLAIN:
|
339
|
+
/* fall through */
|
340
|
+
default:
|
341
|
+
/* all other formats treated as plain */
|
342
|
+
return sym_plain;
|
343
|
+
}
|
344
|
+
}
|
345
|
+
|
346
|
+
|
347
|
+
static VALUE
|
348
|
+
do_encode(VALUE *args)
|
349
|
+
{
|
350
|
+
VALUE val = args[0];
|
351
|
+
uint32_t flags = ((uint32_t)args[1] & FMT_MASK);
|
352
|
+
|
353
|
+
switch (flags) {
|
354
|
+
case FMT_DOCUMENT:
|
355
|
+
return rb_funcall(mJSON, id_dump, 1, val);
|
356
|
+
case FMT_MARSHAL:
|
357
|
+
return rb_funcall(mMarshal, id_dump, 1, val);
|
358
|
+
case FMT_PLAIN:
|
359
|
+
/* fall through */
|
360
|
+
default:
|
361
|
+
/* all other formats treated as plain */
|
362
|
+
return val;
|
363
|
+
}
|
364
|
+
}
|
365
|
+
|
366
|
+
static VALUE
|
367
|
+
do_decode(VALUE *args)
|
368
|
+
{
|
369
|
+
VALUE blob = args[0];
|
370
|
+
uint32_t flags = ((uint32_t)args[1] & FMT_MASK);
|
371
|
+
|
372
|
+
switch (flags) {
|
373
|
+
case FMT_DOCUMENT:
|
374
|
+
return rb_funcall(mJSON, id_load, 1, blob);
|
375
|
+
case FMT_MARSHAL:
|
376
|
+
return rb_funcall(mMarshal, id_load, 1, blob);
|
377
|
+
case FMT_PLAIN:
|
378
|
+
/* fall through */
|
379
|
+
default:
|
380
|
+
/* all other formats treated as plain */
|
381
|
+
return blob;
|
382
|
+
}
|
383
|
+
}
|
384
|
+
|
385
|
+
static VALUE
|
386
|
+
coding_failed(void)
|
387
|
+
{
|
388
|
+
return Qundef;
|
389
|
+
}
|
390
|
+
|
391
|
+
static VALUE
|
392
|
+
encode_value(VALUE val, uint32_t flags)
|
393
|
+
{
|
394
|
+
VALUE blob, args[2];
|
395
|
+
|
396
|
+
args[0] = val;
|
397
|
+
args[1] = (VALUE)flags;
|
398
|
+
blob = rb_rescue(do_encode, (VALUE)args, coding_failed, 0);
|
399
|
+
/* it must be bytestring after all */
|
400
|
+
if (TYPE(blob) != T_STRING) {
|
401
|
+
return Qundef;
|
402
|
+
}
|
403
|
+
return blob;
|
404
|
+
}
|
405
|
+
|
406
|
+
static VALUE
|
407
|
+
decode_value(VALUE blob, uint32_t flags)
|
408
|
+
{
|
409
|
+
VALUE val, args[2];
|
410
|
+
|
411
|
+
/* first it must be bytestring */
|
412
|
+
if (TYPE(blob) != T_STRING) {
|
413
|
+
return Qundef;
|
414
|
+
}
|
415
|
+
args[0] = blob;
|
416
|
+
args[1] = (VALUE)flags;
|
417
|
+
val = rb_rescue(do_decode, (VALUE)args, coding_failed, 0);
|
418
|
+
return val;
|
419
|
+
}
|
420
|
+
|
421
|
+
static VALUE
|
422
|
+
unify_key(VALUE key)
|
423
|
+
{
|
424
|
+
switch (TYPE(key)) {
|
425
|
+
case T_STRING:
|
426
|
+
return key;
|
427
|
+
case T_SYMBOL:
|
428
|
+
return rb_str_new2(rb_id2name(SYM2ID(key)));
|
429
|
+
default: /* call #to_str or raise error */
|
430
|
+
return StringValue(key);
|
431
|
+
}
|
432
|
+
}
|
433
|
+
|
434
|
+
static int
|
435
|
+
cb_extract_keys_i(VALUE key, VALUE value, VALUE arg)
|
436
|
+
{
|
437
|
+
struct key_traits *traits = (struct key_traits *)arg;
|
438
|
+
key = unify_key(key);
|
439
|
+
rb_ary_push(traits->keys_ary, key);
|
440
|
+
traits->keys[traits->nkeys] = RSTRING_PTR(key);
|
441
|
+
traits->lens[traits->nkeys] = RSTRING_LEN(key);
|
442
|
+
traits->ttls[traits->nkeys] = NUM2ULONG(value);
|
443
|
+
traits->nkeys++;
|
444
|
+
return ST_CONTINUE;
|
445
|
+
}
|
446
|
+
|
447
|
+
static long
|
448
|
+
cb_args_scan_keys(long argc, VALUE argv, bucket_t *bucket, struct key_traits *traits)
|
449
|
+
{
|
450
|
+
VALUE key, *keys_ptr, opts, ttl, ext;
|
451
|
+
long nn = 0, ii;
|
452
|
+
time_t exp;
|
453
|
+
|
454
|
+
traits->keys_ary = rb_ary_new();
|
455
|
+
traits->quiet = bucket->quiet;
|
456
|
+
traits->mgat = 0;
|
457
|
+
|
458
|
+
if (argc > 0) {
|
459
|
+
/* keys with custom options */
|
460
|
+
opts = RARRAY_PTR(argv)[argc-1];
|
461
|
+
exp = bucket->default_ttl;
|
462
|
+
ext = Qfalse;
|
463
|
+
if (argc > 1 && TYPE(opts) == T_HASH) {
|
464
|
+
(void)rb_ary_pop(argv);
|
465
|
+
if (RTEST(rb_funcall(opts, id_has_key_p, 1, sym_quiet))) {
|
466
|
+
traits->quiet = RTEST(rb_hash_aref(opts, sym_quiet));
|
467
|
+
}
|
468
|
+
ext = rb_hash_aref(opts, sym_extended);
|
469
|
+
ttl = rb_hash_aref(opts, sym_ttl);
|
470
|
+
if (ttl != Qnil) {
|
471
|
+
traits->explicit_ttl = 1;
|
472
|
+
exp = NUM2ULONG(ttl);
|
473
|
+
}
|
474
|
+
nn = RARRAY_LEN(argv);
|
475
|
+
} else {
|
476
|
+
nn = argc;
|
477
|
+
}
|
478
|
+
if (nn < 1) {
|
479
|
+
rb_raise(rb_eArgError, "must be at least one key");
|
480
|
+
}
|
481
|
+
keys_ptr = RARRAY_PTR(argv);
|
482
|
+
traits->extended = RTEST(ext) ? 1 : 0;
|
483
|
+
if (nn == 1 && TYPE(keys_ptr[0]) == T_HASH) {
|
484
|
+
/* hash of key-ttl pairs */
|
485
|
+
nn = RHASH_SIZE(keys_ptr[0]);
|
486
|
+
traits->keys = calloc(nn, sizeof(char *));
|
487
|
+
traits->lens = calloc(nn, sizeof(size_t));
|
488
|
+
traits->explicit_ttl = 1;
|
489
|
+
traits->mgat = 1;
|
490
|
+
traits->ttls = calloc(nn, sizeof(time_t));
|
491
|
+
rb_hash_foreach(keys_ptr[0], cb_extract_keys_i, (VALUE)traits);
|
492
|
+
} else {
|
493
|
+
/* the list of keys */
|
494
|
+
traits->nkeys = nn;
|
495
|
+
traits->keys = calloc(nn, sizeof(char *));
|
496
|
+
traits->lens = calloc(nn, sizeof(size_t));
|
497
|
+
traits->ttls = calloc(nn, sizeof(time_t));
|
498
|
+
for (ii = 0; ii < nn; ii++) {
|
499
|
+
key = unify_key(keys_ptr[ii]);
|
500
|
+
rb_ary_push(traits->keys_ary, key);
|
501
|
+
traits->keys[ii] = RSTRING_PTR(key);
|
502
|
+
traits->lens[ii] = RSTRING_LEN(key);
|
503
|
+
traits->ttls[ii] = exp;
|
504
|
+
}
|
505
|
+
}
|
506
|
+
}
|
507
|
+
|
508
|
+
return nn;
|
509
|
+
}
|
510
|
+
|
511
|
+
static void
|
512
|
+
error_callback(libcouchbase_t handle, libcouchbase_error_t error, const char *errinfo)
|
513
|
+
{
|
514
|
+
bucket_t *bucket = (bucket_t *)libcouchbase_get_cookie(handle);
|
515
|
+
|
516
|
+
bucket->io->stop_event_loop(bucket->io);
|
517
|
+
bucket->exception = cb_check_error(error, errinfo, Qnil);
|
518
|
+
}
|
519
|
+
|
520
|
+
static void
|
521
|
+
storage_callback(libcouchbase_t handle, const void *cookie,
|
522
|
+
libcouchbase_storage_t operation, libcouchbase_error_t error,
|
523
|
+
const void *key, libcouchbase_size_t nkey, libcouchbase_cas_t cas)
|
524
|
+
{
|
525
|
+
context_t *ctx = (context_t *)cookie;
|
526
|
+
bucket_t *bucket = ctx->bucket;
|
527
|
+
VALUE k, c, *rv = ctx->rv, exc, res;
|
528
|
+
ID o;
|
529
|
+
|
530
|
+
bucket->seqno--;
|
531
|
+
|
532
|
+
k = rb_str_new((const char*)key, nkey);
|
533
|
+
c = cas > 0 ? ULL2NUM(cas) : Qnil;
|
534
|
+
switch(operation) {
|
535
|
+
case LIBCOUCHBASE_ADD:
|
536
|
+
o = sym_add;
|
537
|
+
break;
|
538
|
+
case LIBCOUCHBASE_REPLACE:
|
539
|
+
o = sym_replace;
|
540
|
+
break;
|
541
|
+
case LIBCOUCHBASE_SET:
|
542
|
+
o = sym_set;
|
543
|
+
break;
|
544
|
+
case LIBCOUCHBASE_APPEND:
|
545
|
+
o = sym_append;
|
546
|
+
break;
|
547
|
+
case LIBCOUCHBASE_PREPEND:
|
548
|
+
o = sym_prepend;
|
549
|
+
break;
|
550
|
+
default:
|
551
|
+
o = Qnil;
|
552
|
+
}
|
553
|
+
exc = cb_check_error(error, "failed to store value", k);
|
554
|
+
if (exc != Qnil) {
|
555
|
+
rb_ivar_set(exc, id_iv_cas, c);
|
556
|
+
rb_ivar_set(exc, id_iv_operation, o);
|
557
|
+
if (NIL_P(ctx->exception)) {
|
558
|
+
ctx->exception = exc;
|
559
|
+
}
|
560
|
+
}
|
561
|
+
if (bucket->async) { /* asynchronous */
|
562
|
+
if (ctx->proc != Qnil) {
|
563
|
+
res = rb_class_new_instance(0, NULL, cResult);
|
564
|
+
rb_ivar_set(res, id_iv_error, exc);
|
565
|
+
rb_ivar_set(res, id_iv_key, k);
|
566
|
+
rb_ivar_set(res, id_iv_operation, o);
|
567
|
+
rb_ivar_set(res, id_iv_cas, c);
|
568
|
+
cb_proc_call(ctx->proc, 1, res);
|
569
|
+
}
|
570
|
+
} else { /* synchronous */
|
571
|
+
*rv = c;
|
572
|
+
}
|
573
|
+
|
574
|
+
if (bucket->seqno == 0) {
|
575
|
+
bucket->io->stop_event_loop(bucket->io);
|
576
|
+
rb_hash_delete(object_space, ctx->proc|1);
|
577
|
+
}
|
578
|
+
(void)handle;
|
579
|
+
}
|
580
|
+
|
581
|
+
static void
|
582
|
+
delete_callback(libcouchbase_t handle, const void *cookie,
|
583
|
+
libcouchbase_error_t error, const void *key,
|
584
|
+
libcouchbase_size_t nkey)
|
585
|
+
{
|
586
|
+
context_t *ctx = (context_t *)cookie;
|
587
|
+
bucket_t *bucket = ctx->bucket;
|
588
|
+
VALUE k, *rv = ctx->rv, exc = Qnil, res;
|
589
|
+
|
590
|
+
bucket->seqno--;
|
591
|
+
|
592
|
+
k = rb_str_new((const char*)key, nkey);
|
593
|
+
if (error != LIBCOUCHBASE_KEY_ENOENT || !ctx->quiet) {
|
594
|
+
exc = cb_check_error(error, "failed to remove value", k);
|
595
|
+
if (exc != Qnil) {
|
596
|
+
rb_ivar_set(exc, id_iv_operation, sym_delete);
|
597
|
+
if (NIL_P(ctx->exception)) {
|
598
|
+
ctx->exception = exc;
|
599
|
+
}
|
600
|
+
}
|
601
|
+
}
|
602
|
+
if (bucket->async) { /* asynchronous */
|
603
|
+
if (ctx->proc != Qnil) {
|
604
|
+
res = rb_class_new_instance(0, NULL, cResult);
|
605
|
+
rb_ivar_set(res, id_iv_error, exc);
|
606
|
+
rb_ivar_set(res, id_iv_operation, sym_delete);
|
607
|
+
rb_ivar_set(res, id_iv_key, k);
|
608
|
+
cb_proc_call(ctx->proc, 1, res);
|
609
|
+
}
|
610
|
+
} else { /* synchronous */
|
611
|
+
*rv = (error == LIBCOUCHBASE_SUCCESS) ? Qtrue : Qfalse;
|
612
|
+
}
|
613
|
+
if (bucket->seqno == 0) {
|
614
|
+
bucket->io->stop_event_loop(bucket->io);
|
615
|
+
rb_hash_delete(object_space, ctx->proc|1);
|
616
|
+
}
|
617
|
+
(void)handle;
|
618
|
+
}
|
619
|
+
|
620
|
+
static void
|
621
|
+
get_callback(libcouchbase_t handle, const void *cookie,
|
622
|
+
libcouchbase_error_t error, const void *key,
|
623
|
+
libcouchbase_size_t nkey, const void *bytes,
|
624
|
+
libcouchbase_size_t nbytes, libcouchbase_uint32_t flags,
|
625
|
+
libcouchbase_cas_t cas)
|
626
|
+
{
|
627
|
+
context_t *ctx = (context_t *)cookie;
|
628
|
+
bucket_t *bucket = ctx->bucket;
|
629
|
+
VALUE k, v, f, c, *rv = ctx->rv, exc = Qnil, res;
|
630
|
+
|
631
|
+
bucket->seqno--;
|
632
|
+
|
633
|
+
k = rb_str_new((const char*)key, nkey);
|
634
|
+
if (error != LIBCOUCHBASE_KEY_ENOENT || !ctx->quiet) {
|
635
|
+
exc = cb_check_error(error, "failed to get value", k);
|
636
|
+
if (exc != Qnil) {
|
637
|
+
rb_ivar_set(exc, id_iv_operation, sym_get);
|
638
|
+
if (NIL_P(ctx->exception)) {
|
639
|
+
ctx->exception = exc;
|
640
|
+
}
|
641
|
+
}
|
642
|
+
}
|
643
|
+
|
644
|
+
f = ULONG2NUM(flags);
|
645
|
+
c = ULL2NUM(cas);
|
646
|
+
if (nbytes != 0) {
|
647
|
+
v = decode_value(rb_str_new((const char*)bytes, nbytes), flags);
|
648
|
+
if (v == Qundef) {
|
649
|
+
ctx->exception = rb_exc_new2(eValueFormatError, "unable to convert value");
|
650
|
+
v = Qnil;
|
651
|
+
}
|
652
|
+
} else {
|
653
|
+
if (flags_get_format(flags) == sym_plain) {
|
654
|
+
v = rb_str_new2("");
|
655
|
+
} else {
|
656
|
+
v = Qnil;
|
657
|
+
}
|
658
|
+
}
|
659
|
+
if (bucket->async) { /* asynchronous */
|
660
|
+
if (ctx->proc != Qnil) {
|
661
|
+
res = rb_class_new_instance(0, NULL, cResult);
|
662
|
+
rb_ivar_set(res, id_iv_error, exc);
|
663
|
+
rb_ivar_set(res, id_iv_operation, sym_get);
|
664
|
+
rb_ivar_set(res, id_iv_key, k);
|
665
|
+
rb_ivar_set(res, id_iv_value, v);
|
666
|
+
rb_ivar_set(res, id_iv_flags, f);
|
667
|
+
rb_ivar_set(res, id_iv_cas, c);
|
668
|
+
cb_proc_call(ctx->proc, 1, res);
|
669
|
+
}
|
670
|
+
} else { /* synchronous */
|
671
|
+
if (NIL_P(exc) && error != LIBCOUCHBASE_KEY_ENOENT) {
|
672
|
+
if (ctx->extended) {
|
673
|
+
rb_hash_aset(*rv, k, rb_ary_new3(3, v, f, c));
|
674
|
+
} else {
|
675
|
+
rb_hash_aset(*rv, k, v);
|
676
|
+
}
|
677
|
+
}
|
678
|
+
}
|
679
|
+
|
680
|
+
if (bucket->seqno == 0) {
|
681
|
+
bucket->io->stop_event_loop(bucket->io);
|
682
|
+
rb_hash_delete(object_space, ctx->proc|1);
|
683
|
+
}
|
684
|
+
(void)handle;
|
685
|
+
}
|
686
|
+
|
687
|
+
static void
|
688
|
+
flush_callback(libcouchbase_t handle, const void* cookie,
|
689
|
+
const char* authority, libcouchbase_error_t error)
|
690
|
+
{
|
691
|
+
context_t *ctx = (context_t *)cookie;
|
692
|
+
bucket_t *bucket = ctx->bucket;
|
693
|
+
VALUE node, success = Qtrue, *rv = ctx->rv, exc, res;
|
694
|
+
|
695
|
+
node = authority ? rb_str_new2(authority) : Qnil;
|
696
|
+
exc = cb_check_error(error, "failed to flush bucket", node);
|
697
|
+
if (exc != Qnil) {
|
698
|
+
rb_ivar_set(exc, id_iv_operation, sym_flush);
|
699
|
+
if (NIL_P(ctx->exception)) {
|
700
|
+
ctx->exception = exc;
|
701
|
+
}
|
702
|
+
success = Qfalse;
|
703
|
+
}
|
704
|
+
|
705
|
+
if (authority) {
|
706
|
+
if (bucket->async) { /* asynchronous */
|
707
|
+
if (ctx->proc != Qnil) {
|
708
|
+
res = rb_class_new_instance(0, NULL, cResult);
|
709
|
+
rb_ivar_set(res, id_iv_error, exc);
|
710
|
+
rb_ivar_set(res, id_iv_operation, sym_flush);
|
711
|
+
rb_ivar_set(res, id_iv_node, node);
|
712
|
+
cb_proc_call(ctx->proc, 1, res);
|
713
|
+
}
|
714
|
+
} else { /* synchronous */
|
715
|
+
if (RTEST(*rv)) {
|
716
|
+
/* rewrite status for positive values only */
|
717
|
+
*rv = success;
|
718
|
+
}
|
719
|
+
}
|
720
|
+
} else {
|
721
|
+
bucket->seqno--;
|
722
|
+
if (bucket->seqno == 0) {
|
723
|
+
bucket->io->stop_event_loop(bucket->io);
|
724
|
+
rb_hash_delete(object_space, ctx->proc|1);
|
725
|
+
}
|
726
|
+
}
|
727
|
+
|
728
|
+
(void)handle;
|
729
|
+
}
|
730
|
+
|
731
|
+
static void
|
732
|
+
version_callback(libcouchbase_t handle, const void *cookie,
|
733
|
+
const char *authority, libcouchbase_error_t error,
|
734
|
+
const char *bytes, libcouchbase_size_t nbytes)
|
735
|
+
{
|
736
|
+
context_t *ctx = (context_t *)cookie;
|
737
|
+
bucket_t *bucket = ctx->bucket;
|
738
|
+
VALUE node, v, *rv = ctx->rv, exc, res;
|
739
|
+
|
740
|
+
node = authority ? rb_str_new2(authority) : Qnil;
|
741
|
+
exc = cb_check_error(error, "failed to get version", node);
|
742
|
+
if (exc != Qnil) {
|
743
|
+
rb_ivar_set(exc, id_iv_operation, sym_flush);
|
744
|
+
if (NIL_P(ctx->exception)) {
|
745
|
+
ctx->exception = exc;
|
746
|
+
}
|
747
|
+
}
|
748
|
+
|
749
|
+
if (authority) {
|
750
|
+
v = rb_str_new((const char*)bytes, nbytes);
|
751
|
+
if (bucket->async) { /* asynchronous */
|
752
|
+
if (ctx->proc != Qnil) {
|
753
|
+
res = rb_class_new_instance(0, NULL, cResult);
|
754
|
+
rb_ivar_set(res, id_iv_error, exc);
|
755
|
+
rb_ivar_set(res, id_iv_operation, sym_version);
|
756
|
+
rb_ivar_set(res, id_iv_node, node);
|
757
|
+
rb_ivar_set(res, id_iv_value, v);
|
758
|
+
cb_proc_call(ctx->proc, 1, res);
|
759
|
+
}
|
760
|
+
} else { /* synchronous */
|
761
|
+
if (NIL_P(exc)) {
|
762
|
+
rb_hash_aset(*rv, node, v);
|
763
|
+
}
|
764
|
+
}
|
765
|
+
} else {
|
766
|
+
bucket->seqno--;
|
767
|
+
if (bucket->seqno == 0) {
|
768
|
+
bucket->io->stop_event_loop(bucket->io);
|
769
|
+
rb_hash_delete(object_space, ctx->proc|1);
|
770
|
+
}
|
771
|
+
}
|
772
|
+
|
773
|
+
(void)handle;
|
774
|
+
}
|
775
|
+
|
776
|
+
static void
|
777
|
+
stat_callback(libcouchbase_t handle, const void* cookie,
|
778
|
+
const char* authority, libcouchbase_error_t error, const void* key,
|
779
|
+
libcouchbase_size_t nkey, const void* bytes,
|
780
|
+
libcouchbase_size_t nbytes)
|
781
|
+
{
|
782
|
+
context_t *ctx = (context_t *)cookie;
|
783
|
+
bucket_t *bucket = ctx->bucket;
|
784
|
+
VALUE stats, node, k, v, *rv = ctx->rv, exc = Qnil, res;
|
785
|
+
|
786
|
+
node = authority ? rb_str_new2(authority) : Qnil;
|
787
|
+
exc = cb_check_error(error, "failed to fetch stats", node);
|
788
|
+
if (exc != Qnil) {
|
789
|
+
rb_ivar_set(exc, id_iv_operation, sym_stats);
|
790
|
+
if (NIL_P(ctx->exception)) {
|
791
|
+
ctx->exception = exc;
|
792
|
+
}
|
793
|
+
}
|
794
|
+
if (authority) {
|
795
|
+
k = rb_str_new((const char*)key, nkey);
|
796
|
+
v = rb_str_new((const char*)bytes, nbytes);
|
797
|
+
if (bucket->async) { /* asynchronous */
|
798
|
+
if (ctx->proc != Qnil) {
|
799
|
+
res = rb_class_new_instance(0, NULL, cResult);
|
800
|
+
rb_ivar_set(res, id_iv_error, exc);
|
801
|
+
rb_ivar_set(res, id_iv_operation, sym_stats);
|
802
|
+
rb_ivar_set(res, id_iv_node, node);
|
803
|
+
rb_ivar_set(res, id_iv_key, k);
|
804
|
+
rb_ivar_set(res, id_iv_value, v);
|
805
|
+
cb_proc_call(ctx->proc, 1, res);
|
806
|
+
}
|
807
|
+
} else { /* synchronous */
|
808
|
+
if (NIL_P(exc)) {
|
809
|
+
stats = rb_hash_aref(*rv, k);
|
810
|
+
if (NIL_P(stats)) {
|
811
|
+
stats = rb_hash_new();
|
812
|
+
rb_hash_aset(*rv, k, stats);
|
813
|
+
}
|
814
|
+
rb_hash_aset(stats, node, v);
|
815
|
+
}
|
816
|
+
}
|
817
|
+
} else {
|
818
|
+
bucket->seqno--;
|
819
|
+
if (bucket->seqno == 0) {
|
820
|
+
bucket->io->stop_event_loop(bucket->io);
|
821
|
+
rb_hash_delete(object_space, ctx->proc|1);
|
822
|
+
}
|
823
|
+
}
|
824
|
+
(void)handle;
|
825
|
+
}
|
826
|
+
|
827
|
+
static void
|
828
|
+
touch_callback(libcouchbase_t handle, const void *cookie,
|
829
|
+
libcouchbase_error_t error, const void *key,
|
830
|
+
libcouchbase_size_t nkey)
|
831
|
+
{
|
832
|
+
context_t *ctx = (context_t *)cookie;
|
833
|
+
bucket_t *bucket = ctx->bucket;
|
834
|
+
VALUE k, success, *rv = ctx->rv, exc = Qnil, res;
|
835
|
+
|
836
|
+
bucket->seqno--;
|
837
|
+
k = rb_str_new((const char*)key, nkey);
|
838
|
+
if (error != LIBCOUCHBASE_KEY_ENOENT || !ctx->quiet) {
|
839
|
+
exc = cb_check_error(error, "failed to touch value", k);
|
840
|
+
if (exc != Qnil) {
|
841
|
+
rb_ivar_set(exc, id_iv_operation, sym_touch);
|
842
|
+
if (NIL_P(ctx->exception)) {
|
843
|
+
ctx->exception = exc;
|
844
|
+
}
|
845
|
+
}
|
846
|
+
}
|
847
|
+
|
848
|
+
if (bucket->async) { /* asynchronous */
|
849
|
+
if (ctx->proc != Qnil) {
|
850
|
+
res = rb_class_new_instance(0, NULL, cResult);
|
851
|
+
rb_ivar_set(res, id_iv_error, exc);
|
852
|
+
rb_ivar_set(res, id_iv_operation, sym_touch);
|
853
|
+
rb_ivar_set(res, id_iv_key, k);
|
854
|
+
cb_proc_call(ctx->proc, 1, res);
|
855
|
+
}
|
856
|
+
} else { /* synchronous */
|
857
|
+
if (NIL_P(exc)) {
|
858
|
+
success = (error == LIBCOUCHBASE_KEY_ENOENT) ? Qfalse : Qtrue;
|
859
|
+
rb_hash_aset(*rv, k, success);
|
860
|
+
}
|
861
|
+
}
|
862
|
+
if (bucket->seqno == 0) {
|
863
|
+
bucket->io->stop_event_loop(bucket->io);
|
864
|
+
rb_hash_delete(object_space, ctx->proc|1);
|
865
|
+
}
|
866
|
+
(void)handle;
|
867
|
+
}
|
868
|
+
|
869
|
+
static void
|
870
|
+
arithmetic_callback(libcouchbase_t handle, const void *cookie,
|
871
|
+
libcouchbase_error_t error, const void *key,
|
872
|
+
libcouchbase_size_t nkey, libcouchbase_uint64_t value,
|
873
|
+
libcouchbase_cas_t cas)
|
874
|
+
{
|
875
|
+
context_t *ctx = (context_t *)cookie;
|
876
|
+
bucket_t *bucket = ctx->bucket;
|
877
|
+
VALUE c, k, v, *rv = ctx->rv, exc, res;
|
878
|
+
ID o;
|
879
|
+
|
880
|
+
bucket->seqno--;
|
881
|
+
|
882
|
+
k = rb_str_new((const char*)key, nkey);
|
883
|
+
c = cas > 0 ? ULL2NUM(cas) : Qnil;
|
884
|
+
o = ctx->arithm > 0 ? sym_increment : sym_decrement;
|
885
|
+
exc = cb_check_error(error, "failed to perform arithmetic operation", k);
|
886
|
+
if (exc != Qnil) {
|
887
|
+
rb_ivar_set(exc, id_iv_cas, c);
|
888
|
+
rb_ivar_set(exc, id_iv_operation, o);
|
889
|
+
if (bucket->async) {
|
890
|
+
if (bucket->on_error_proc != Qnil) {
|
891
|
+
cb_proc_call(bucket->on_error_proc, 3, o, k, exc);
|
892
|
+
} else {
|
893
|
+
if (NIL_P(bucket->exception)) {
|
894
|
+
bucket->exception = exc;
|
895
|
+
}
|
896
|
+
}
|
897
|
+
}
|
898
|
+
if (NIL_P(ctx->exception)) {
|
899
|
+
ctx->exception = exc;
|
900
|
+
}
|
901
|
+
}
|
902
|
+
v = ULL2NUM(value);
|
903
|
+
if (bucket->async) { /* asynchronous */
|
904
|
+
if (ctx->proc != Qnil) {
|
905
|
+
res = rb_class_new_instance(0, NULL, cResult);
|
906
|
+
rb_ivar_set(res, id_iv_error, exc);
|
907
|
+
rb_ivar_set(res, id_iv_operation, o);
|
908
|
+
rb_ivar_set(res, id_iv_key, k);
|
909
|
+
rb_ivar_set(res, id_iv_value, v);
|
910
|
+
rb_ivar_set(res, id_iv_cas, c);
|
911
|
+
cb_proc_call(ctx->proc, 1, res);
|
912
|
+
}
|
913
|
+
} else { /* synchronous */
|
914
|
+
if (NIL_P(exc)) {
|
915
|
+
if (ctx->extended) {
|
916
|
+
*rv = rb_ary_new3(2, v, c);
|
917
|
+
} else {
|
918
|
+
*rv = v;
|
919
|
+
}
|
920
|
+
}
|
921
|
+
}
|
922
|
+
if (bucket->seqno == 0) {
|
923
|
+
bucket->io->stop_event_loop(bucket->io);
|
924
|
+
rb_hash_delete(object_space, ctx->proc|1);
|
925
|
+
}
|
926
|
+
(void)handle;
|
927
|
+
}
|
928
|
+
|
929
|
+
static int
|
930
|
+
cb_first_value_i(VALUE key, VALUE value, VALUE arg)
|
931
|
+
{
|
932
|
+
VALUE *val = (VALUE *)arg;
|
933
|
+
|
934
|
+
*val = value;
|
935
|
+
(void)key;
|
936
|
+
return ST_STOP;
|
937
|
+
}
|
938
|
+
|
939
|
+
/*
|
940
|
+
* @private
|
941
|
+
* @return [Fixnum] number of scheduled operations
|
942
|
+
*/
|
943
|
+
static VALUE
|
944
|
+
cb_bucket_seqno(VALUE self)
|
945
|
+
{
|
946
|
+
bucket_t *bucket = DATA_PTR(self);
|
947
|
+
|
948
|
+
return LONG2FIX(bucket->seqno);
|
949
|
+
}
|
950
|
+
|
951
|
+
void
|
952
|
+
cb_bucket_free(void *ptr)
|
953
|
+
{
|
954
|
+
bucket_t *bucket = ptr;
|
955
|
+
|
956
|
+
if (bucket) {
|
957
|
+
if (bucket->handle) {
|
958
|
+
libcouchbase_destroy(bucket->handle);
|
959
|
+
}
|
960
|
+
free(bucket->authority);
|
961
|
+
free(bucket->hostname);
|
962
|
+
free(bucket->pool);
|
963
|
+
free(bucket->bucket);
|
964
|
+
free(bucket->username);
|
965
|
+
free(bucket->password);
|
966
|
+
free(bucket);
|
967
|
+
}
|
968
|
+
}
|
969
|
+
|
970
|
+
void
|
971
|
+
cb_bucket_mark(void *ptr)
|
972
|
+
{
|
973
|
+
bucket_t *bucket = ptr;
|
974
|
+
|
975
|
+
if (bucket) {
|
976
|
+
rb_gc_mark(bucket->exception);
|
977
|
+
rb_gc_mark(bucket->on_error_proc);
|
978
|
+
}
|
979
|
+
}
|
980
|
+
|
981
|
+
static void
|
982
|
+
do_scan_connection_options(bucket_t *bucket, int argc, VALUE *argv)
|
983
|
+
{
|
984
|
+
VALUE uri, opts, arg;
|
985
|
+
size_t len;
|
986
|
+
|
987
|
+
if (rb_scan_args(argc, argv, "02", &uri, &opts) > 0) {
|
988
|
+
if (TYPE(uri) == T_HASH && argc == 1) {
|
989
|
+
opts = uri;
|
990
|
+
uri = Qnil;
|
991
|
+
}
|
992
|
+
if (uri != Qnil) {
|
993
|
+
const char path_re[] = "^(/pools/([A-Za-z0-9_.-]+)(/buckets/([A-Za-z0-9_.-]+))?)?";
|
994
|
+
VALUE match, uri_obj, re;
|
995
|
+
|
996
|
+
Check_Type(uri, T_STRING);
|
997
|
+
uri_obj = rb_funcall(mURI, id_parse, 1, uri);
|
998
|
+
|
999
|
+
arg = rb_funcall(uri_obj, id_scheme, 0);
|
1000
|
+
if (arg == Qnil || rb_str_cmp(arg, rb_str_new2("http"))) {
|
1001
|
+
rb_raise(rb_eArgError, "invalid URI: invalid scheme");
|
1002
|
+
}
|
1003
|
+
|
1004
|
+
arg = rb_funcall(uri_obj, id_user, 0);
|
1005
|
+
if (arg != Qnil) {
|
1006
|
+
free(bucket->username);
|
1007
|
+
bucket->username = strdup(RSTRING_PTR(arg));
|
1008
|
+
if (bucket->username == NULL) {
|
1009
|
+
rb_raise(eNoMemoryError, "failed to allocate memory for Bucket");
|
1010
|
+
}
|
1011
|
+
}
|
1012
|
+
|
1013
|
+
arg = rb_funcall(uri_obj, id_password, 0);
|
1014
|
+
if (arg != Qnil) {
|
1015
|
+
free(bucket->password);
|
1016
|
+
bucket->password = strdup(RSTRING_PTR(arg));
|
1017
|
+
if (bucket->password == NULL) {
|
1018
|
+
rb_raise(eNoMemoryError, "failed to allocate memory for Bucket");
|
1019
|
+
}
|
1020
|
+
}
|
1021
|
+
arg = rb_funcall(uri_obj, id_host, 0);
|
1022
|
+
if (arg != Qnil) {
|
1023
|
+
free(bucket->hostname);
|
1024
|
+
bucket->hostname = strdup(RSTRING_PTR(arg));
|
1025
|
+
if (bucket->hostname == NULL) {
|
1026
|
+
rb_raise(eNoMemoryError, "failed to allocate memory for Bucket");
|
1027
|
+
}
|
1028
|
+
} else {
|
1029
|
+
rb_raise(rb_eArgError, "invalid URI: missing hostname");
|
1030
|
+
}
|
1031
|
+
|
1032
|
+
arg = rb_funcall(uri_obj, id_port, 0);
|
1033
|
+
bucket->port = NIL_P(arg) ? 8091 : (uint16_t)NUM2UINT(arg);
|
1034
|
+
|
1035
|
+
arg = rb_funcall(uri_obj, id_path, 0);
|
1036
|
+
re = rb_reg_new(path_re, sizeof(path_re) - 1, 0);
|
1037
|
+
match = rb_funcall(re, id_match, 1, arg);
|
1038
|
+
arg = rb_reg_nth_match(2, match);
|
1039
|
+
free(bucket->pool);
|
1040
|
+
bucket->pool = strdup(NIL_P(arg) ? "default" : RSTRING_PTR(arg));
|
1041
|
+
arg = rb_reg_nth_match(4, match);
|
1042
|
+
free(bucket->bucket);
|
1043
|
+
bucket->bucket = strdup(NIL_P(arg) ? "default" : RSTRING_PTR(arg));
|
1044
|
+
}
|
1045
|
+
if (TYPE(opts) == T_HASH) {
|
1046
|
+
arg = rb_hash_aref(opts, sym_hostname);
|
1047
|
+
if (arg != Qnil) {
|
1048
|
+
if (bucket->hostname) {
|
1049
|
+
free(bucket->hostname);
|
1050
|
+
}
|
1051
|
+
bucket->hostname = strdup(StringValueCStr(arg));
|
1052
|
+
}
|
1053
|
+
arg = rb_hash_aref(opts, sym_pool);
|
1054
|
+
if (arg != Qnil) {
|
1055
|
+
if (bucket->pool) {
|
1056
|
+
free(bucket->pool);
|
1057
|
+
}
|
1058
|
+
bucket->pool = strdup(StringValueCStr(arg));
|
1059
|
+
}
|
1060
|
+
arg = rb_hash_aref(opts, sym_bucket);
|
1061
|
+
if (arg != Qnil) {
|
1062
|
+
if (bucket->bucket) {
|
1063
|
+
free(bucket->bucket);
|
1064
|
+
}
|
1065
|
+
bucket->bucket = strdup(StringValueCStr(arg));
|
1066
|
+
}
|
1067
|
+
arg = rb_hash_aref(opts, sym_username);
|
1068
|
+
if (arg != Qnil) {
|
1069
|
+
if (bucket->username) {
|
1070
|
+
free(bucket->username);
|
1071
|
+
}
|
1072
|
+
bucket->username = strdup(StringValueCStr(arg));
|
1073
|
+
}
|
1074
|
+
arg = rb_hash_aref(opts, sym_password);
|
1075
|
+
if (arg != Qnil) {
|
1076
|
+
if (bucket->password) {
|
1077
|
+
free(bucket->password);
|
1078
|
+
}
|
1079
|
+
bucket->password = strdup(StringValueCStr(arg));
|
1080
|
+
}
|
1081
|
+
arg = rb_hash_aref(opts, sym_port);
|
1082
|
+
if (arg != Qnil) {
|
1083
|
+
bucket->port = (uint16_t)NUM2UINT(arg);
|
1084
|
+
}
|
1085
|
+
if (RTEST(rb_funcall(opts, id_has_key_p, 1, sym_quiet))) {
|
1086
|
+
bucket->quiet = RTEST(rb_hash_aref(opts, sym_quiet));
|
1087
|
+
}
|
1088
|
+
arg = rb_hash_aref(opts, sym_timeout);
|
1089
|
+
if (arg != Qnil) {
|
1090
|
+
bucket->timeout = (uint32_t)NUM2ULONG(arg);
|
1091
|
+
}
|
1092
|
+
arg = rb_hash_aref(opts, sym_default_ttl);
|
1093
|
+
if (arg != Qnil) {
|
1094
|
+
bucket->default_ttl = (uint32_t)NUM2ULONG(arg);
|
1095
|
+
}
|
1096
|
+
arg = rb_hash_aref(opts, sym_default_flags);
|
1097
|
+
if (arg != Qnil) {
|
1098
|
+
bucket->default_flags = (uint32_t)NUM2ULONG(arg);
|
1099
|
+
}
|
1100
|
+
arg = rb_hash_aref(opts, sym_default_format);
|
1101
|
+
if (arg != Qnil) {
|
1102
|
+
if (TYPE(arg) == T_FIXNUM) {
|
1103
|
+
switch (FIX2INT(arg)) {
|
1104
|
+
case FMT_DOCUMENT:
|
1105
|
+
arg = sym_document;
|
1106
|
+
break;
|
1107
|
+
case FMT_MARSHAL:
|
1108
|
+
arg = sym_marshal;
|
1109
|
+
break;
|
1110
|
+
case FMT_PLAIN:
|
1111
|
+
arg = sym_plain;
|
1112
|
+
break;
|
1113
|
+
}
|
1114
|
+
}
|
1115
|
+
if (arg == sym_document || arg == sym_marshal || arg == sym_plain) {
|
1116
|
+
bucket->default_format = arg;
|
1117
|
+
bucket->default_flags = flags_set_format(bucket->default_flags, arg);
|
1118
|
+
}
|
1119
|
+
}
|
1120
|
+
} else {
|
1121
|
+
opts = Qnil;
|
1122
|
+
}
|
1123
|
+
}
|
1124
|
+
len = strlen(bucket->hostname) + 10;
|
1125
|
+
if (bucket->authority) {
|
1126
|
+
free(bucket->authority);
|
1127
|
+
}
|
1128
|
+
bucket->authority = calloc(len, sizeof(char));
|
1129
|
+
if (bucket->authority == NULL) {
|
1130
|
+
rb_raise(eNoMemoryError, "failed to allocate memory for Bucket");
|
1131
|
+
}
|
1132
|
+
snprintf(bucket->authority, len, "%s:%u", bucket->hostname, bucket->port);
|
1133
|
+
}
|
1134
|
+
|
1135
|
+
static void
|
1136
|
+
do_connect(bucket_t *bucket)
|
1137
|
+
{
|
1138
|
+
libcouchbase_error_t err;
|
1139
|
+
|
1140
|
+
if (bucket->handle) {
|
1141
|
+
libcouchbase_destroy(bucket->handle);
|
1142
|
+
bucket->handle = NULL;
|
1143
|
+
bucket->io = NULL;
|
1144
|
+
}
|
1145
|
+
bucket->io = libcouchbase_create_io_ops(LIBCOUCHBASE_IO_OPS_DEFAULT, NULL, &err);
|
1146
|
+
if (bucket->io == NULL && err != LIBCOUCHBASE_SUCCESS) {
|
1147
|
+
rb_exc_raise(cb_check_error(err, "failed to create IO instance", Qnil));
|
1148
|
+
}
|
1149
|
+
bucket->handle = libcouchbase_create(bucket->authority,
|
1150
|
+
bucket->username, bucket->password, bucket->bucket, bucket->io);
|
1151
|
+
if (bucket->handle == NULL) {
|
1152
|
+
rb_raise(eLibcouchbaseError, "failed to create libcouchbase instance");
|
1153
|
+
}
|
1154
|
+
libcouchbase_set_cookie(bucket->handle, bucket);
|
1155
|
+
(void)libcouchbase_set_error_callback(bucket->handle, error_callback);
|
1156
|
+
(void)libcouchbase_set_storage_callback(bucket->handle, storage_callback);
|
1157
|
+
(void)libcouchbase_set_get_callback(bucket->handle, get_callback);
|
1158
|
+
(void)libcouchbase_set_touch_callback(bucket->handle, touch_callback);
|
1159
|
+
(void)libcouchbase_set_remove_callback(bucket->handle, delete_callback);
|
1160
|
+
(void)libcouchbase_set_stat_callback(bucket->handle, stat_callback);
|
1161
|
+
(void)libcouchbase_set_flush_callback(bucket->handle, flush_callback);
|
1162
|
+
(void)libcouchbase_set_arithmetic_callback(bucket->handle, arithmetic_callback);
|
1163
|
+
(void)libcouchbase_set_version_callback(bucket->handle, version_callback);
|
1164
|
+
|
1165
|
+
err = libcouchbase_connect(bucket->handle);
|
1166
|
+
if (err != LIBCOUCHBASE_SUCCESS) {
|
1167
|
+
libcouchbase_destroy(bucket->handle);
|
1168
|
+
bucket->handle = NULL;
|
1169
|
+
bucket->io = NULL;
|
1170
|
+
rb_exc_raise(cb_check_error(err, "failed to connect libcouchbase instance to server", Qnil));
|
1171
|
+
}
|
1172
|
+
bucket->exception = Qnil;
|
1173
|
+
libcouchbase_wait(bucket->handle);
|
1174
|
+
if (bucket->exception != Qnil) {
|
1175
|
+
libcouchbase_destroy(bucket->handle);
|
1176
|
+
bucket->handle = NULL;
|
1177
|
+
bucket->io = NULL;
|
1178
|
+
rb_exc_raise(bucket->exception);
|
1179
|
+
}
|
1180
|
+
|
1181
|
+
if (bucket->timeout > 0) {
|
1182
|
+
libcouchbase_set_timeout(bucket->handle, bucket->timeout);
|
1183
|
+
} else {
|
1184
|
+
bucket->timeout = libcouchbase_get_timeout(bucket->handle);
|
1185
|
+
}
|
1186
|
+
}
|
1187
|
+
|
1188
|
+
/*
|
1189
|
+
* Create and initialize new Bucket.
|
1190
|
+
*
|
1191
|
+
* @return [Bucket] new instance
|
1192
|
+
*
|
1193
|
+
* @see Bucket#initialize
|
1194
|
+
*/
|
1195
|
+
static VALUE
|
1196
|
+
cb_bucket_new(int argc, VALUE *argv, VALUE klass)
|
1197
|
+
{
|
1198
|
+
VALUE obj;
|
1199
|
+
bucket_t *bucket;
|
1200
|
+
|
1201
|
+
/* allocate new bucket struct and set it to zero */
|
1202
|
+
obj = Data_Make_Struct(klass, bucket_t, cb_bucket_mark, cb_bucket_free,
|
1203
|
+
bucket);
|
1204
|
+
rb_obj_call_init(obj, argc, argv);
|
1205
|
+
return obj;
|
1206
|
+
}
|
1207
|
+
|
1208
|
+
/*
|
1209
|
+
* Initialize new Bucket.
|
1210
|
+
*
|
1211
|
+
* @overload initialize(url, options = {})
|
1212
|
+
* Initialize bucket using URI of the cluster and options. It is possible
|
1213
|
+
* to override some parts of URI using the options keys (e.g. :host or
|
1214
|
+
* :port)
|
1215
|
+
*
|
1216
|
+
* @param [String] url The full URL of management API of the cluster.
|
1217
|
+
* @param [Hash] options The options for connection. See options definition
|
1218
|
+
* below.
|
1219
|
+
*
|
1220
|
+
* @overload initialize(options = {})
|
1221
|
+
* Initialize bucket using options only.
|
1222
|
+
*
|
1223
|
+
* @param [Hash] options The options for operation for connection
|
1224
|
+
* @option options [String] :host ("localhost") the hostname or IP address
|
1225
|
+
* of the node
|
1226
|
+
* @option options [Fixnum] :port (8091) the port of the managemenent API
|
1227
|
+
* @option options [String] :pool ("default") the pool name
|
1228
|
+
* @option options [String] :bucket ("default") the bucket name
|
1229
|
+
* @option options [Fixnum] :default_ttl (0) the TTL used by default during
|
1230
|
+
* storing key-value pairs.
|
1231
|
+
* @option options [Fixnum] :default_flags (0) the default flags.
|
1232
|
+
* @option options [Symbol] :default_format (:document) the format, which
|
1233
|
+
* will be used for values by default. Note that changing format will
|
1234
|
+
* amend flags. (see {Bucket#default_format})
|
1235
|
+
* @option options [String] :username (nil) the user name to connect to the
|
1236
|
+
* cluster. Used to authenticate on management API.
|
1237
|
+
* @option options [String] :password (nil) the password of the user.
|
1238
|
+
* @option options [Boolean] :quiet (true) the flag controlling if raising
|
1239
|
+
* exception when the client executes operations on unexising keys. If it
|
1240
|
+
* is +true+ it will raise {Couchbase::Error::NotFound} exceptions. The
|
1241
|
+
* default behaviour is to return +nil+ value silently (might be useful in
|
1242
|
+
* Rails cache).
|
1243
|
+
*
|
1244
|
+
* @example Initialize connection using default options
|
1245
|
+
* Couchbase.new
|
1246
|
+
*
|
1247
|
+
* @example Select custom bucket
|
1248
|
+
* Couchbase.new(:bucket => 'foo')
|
1249
|
+
* Couchbase.new('http://localhost:8091/pools/default/buckets/foo')
|
1250
|
+
*
|
1251
|
+
* @example Connect to protected bucket
|
1252
|
+
* Couchbase.new(:bucket => 'protected', :username => 'protected', :password => 'secret')
|
1253
|
+
* Couchbase.new('http://localhost:8091/pools/default/buckets/protected',
|
1254
|
+
* :username => 'protected', :password => 'secret')
|
1255
|
+
*
|
1256
|
+
* @return [Bucket]
|
1257
|
+
*/
|
1258
|
+
static VALUE
|
1259
|
+
cb_bucket_init(int argc, VALUE *argv, VALUE self)
|
1260
|
+
{
|
1261
|
+
bucket_t *bucket = DATA_PTR(self);
|
1262
|
+
|
1263
|
+
bucket->exception = Qnil;
|
1264
|
+
bucket->hostname = strdup("localhost");
|
1265
|
+
bucket->port = 8091;
|
1266
|
+
bucket->pool = strdup("default");
|
1267
|
+
bucket->bucket = strdup("default");
|
1268
|
+
bucket->async = 0;
|
1269
|
+
bucket->quiet = 1;
|
1270
|
+
bucket->default_ttl = 0;
|
1271
|
+
bucket->default_flags = 0;
|
1272
|
+
bucket->default_format = sym_document;
|
1273
|
+
bucket->on_error_proc = Qnil;
|
1274
|
+
bucket->timeout = 0;
|
1275
|
+
|
1276
|
+
do_scan_connection_options(bucket, argc, argv);
|
1277
|
+
do_connect(bucket);
|
1278
|
+
|
1279
|
+
return self;
|
1280
|
+
}
|
1281
|
+
|
1282
|
+
/*
|
1283
|
+
* Reconnect the bucket
|
1284
|
+
*
|
1285
|
+
* Reconnect the bucket using initial configuration with optional
|
1286
|
+
* redefinition.
|
1287
|
+
*
|
1288
|
+
* @overload reconnect(url, options = {})
|
1289
|
+
* see {Bucket#initialize Bucket#initialize(url, options)}
|
1290
|
+
*
|
1291
|
+
* @overload reconnect(options = {})
|
1292
|
+
* see {Bucket#initialize Bucket#initialize(options)}
|
1293
|
+
*
|
1294
|
+
* @example reconnect with current parameters
|
1295
|
+
* c.reconnect
|
1296
|
+
*
|
1297
|
+
* @example reconnect the instance to another bucket
|
1298
|
+
* c.reconnect(:bucket => 'new')
|
1299
|
+
*/
|
1300
|
+
static VALUE
|
1301
|
+
cb_bucket_reconnect(int argc, VALUE *argv, VALUE self)
|
1302
|
+
{
|
1303
|
+
bucket_t *bucket = DATA_PTR(self);
|
1304
|
+
|
1305
|
+
do_scan_connection_options(bucket, argc, argv);
|
1306
|
+
do_connect(bucket);
|
1307
|
+
|
1308
|
+
return self;
|
1309
|
+
}
|
1310
|
+
|
1311
|
+
/* Document-method: connected?
|
1312
|
+
* Check whether the instance connected to the cluster.
|
1313
|
+
*
|
1314
|
+
* @return [Boolean] +true+ if the instance connected to the cluster
|
1315
|
+
*/
|
1316
|
+
static VALUE
|
1317
|
+
cb_bucket_connected_p(VALUE self)
|
1318
|
+
{
|
1319
|
+
bucket_t *bucket = DATA_PTR(self);
|
1320
|
+
return bucket->handle ? Qtrue : Qfalse;
|
1321
|
+
}
|
1322
|
+
|
1323
|
+
/* Document-method: async?
|
1324
|
+
* Check whether the connection asynchronous.
|
1325
|
+
*
|
1326
|
+
* By default all operations are synchronous and block waiting for
|
1327
|
+
* results, but you can make them asynchronous and run event loop
|
1328
|
+
* explicitly. (see {Bucket#run})
|
1329
|
+
*
|
1330
|
+
* @example Return value of #get operation depending on async flag
|
1331
|
+
* connection = Connection.new
|
1332
|
+
* connection.async? #=> false
|
1333
|
+
*
|
1334
|
+
* connection.run do |conn|
|
1335
|
+
* conn.async? #=> true
|
1336
|
+
* end
|
1337
|
+
*
|
1338
|
+
* @return [Boolean] +true+ if the connection if asynchronous
|
1339
|
+
*
|
1340
|
+
* @see Bucket#run
|
1341
|
+
*/
|
1342
|
+
static VALUE
|
1343
|
+
cb_bucket_async_p(VALUE self)
|
1344
|
+
{
|
1345
|
+
bucket_t *bucket = DATA_PTR(self);
|
1346
|
+
return bucket->async ? Qtrue : Qfalse;
|
1347
|
+
}
|
1348
|
+
|
1349
|
+
static VALUE
|
1350
|
+
cb_bucket_quiet_get(VALUE self)
|
1351
|
+
{
|
1352
|
+
bucket_t *bucket = DATA_PTR(self);
|
1353
|
+
return bucket->quiet ? Qtrue : Qfalse;
|
1354
|
+
}
|
1355
|
+
|
1356
|
+
static VALUE
|
1357
|
+
cb_bucket_quiet_set(VALUE self, VALUE val)
|
1358
|
+
{
|
1359
|
+
bucket_t *bucket = DATA_PTR(self);
|
1360
|
+
VALUE new;
|
1361
|
+
|
1362
|
+
bucket->quiet = RTEST(val);
|
1363
|
+
new = bucket->quiet ? Qtrue : Qfalse;
|
1364
|
+
return new;
|
1365
|
+
}
|
1366
|
+
|
1367
|
+
static VALUE
|
1368
|
+
cb_bucket_default_flags_get(VALUE self)
|
1369
|
+
{
|
1370
|
+
bucket_t *bucket = DATA_PTR(self);
|
1371
|
+
return ULONG2NUM(bucket->default_flags);
|
1372
|
+
}
|
1373
|
+
|
1374
|
+
static VALUE
|
1375
|
+
cb_bucket_default_flags_set(VALUE self, VALUE val)
|
1376
|
+
{
|
1377
|
+
bucket_t *bucket = DATA_PTR(self);
|
1378
|
+
|
1379
|
+
bucket->default_flags = (uint32_t)NUM2ULONG(val);
|
1380
|
+
bucket->default_format = flags_get_format(bucket->default_flags);
|
1381
|
+
return val;
|
1382
|
+
}
|
1383
|
+
|
1384
|
+
static VALUE
|
1385
|
+
cb_bucket_default_format_get(VALUE self)
|
1386
|
+
{
|
1387
|
+
bucket_t *bucket = DATA_PTR(self);
|
1388
|
+
return bucket->default_format;
|
1389
|
+
}
|
1390
|
+
|
1391
|
+
static VALUE
|
1392
|
+
cb_bucket_default_format_set(VALUE self, VALUE val)
|
1393
|
+
{
|
1394
|
+
bucket_t *bucket = DATA_PTR(self);
|
1395
|
+
|
1396
|
+
if (TYPE(val) == T_FIXNUM) {
|
1397
|
+
switch (FIX2INT(val)) {
|
1398
|
+
case FMT_DOCUMENT:
|
1399
|
+
val = sym_document;
|
1400
|
+
break;
|
1401
|
+
case FMT_MARSHAL:
|
1402
|
+
val = sym_marshal;
|
1403
|
+
break;
|
1404
|
+
case FMT_PLAIN:
|
1405
|
+
val = sym_plain;
|
1406
|
+
break;
|
1407
|
+
}
|
1408
|
+
}
|
1409
|
+
if (val == sym_document || val == sym_marshal || val == sym_plain) {
|
1410
|
+
bucket->default_format = val;
|
1411
|
+
bucket->default_flags = flags_set_format(bucket->default_flags, val);
|
1412
|
+
}
|
1413
|
+
|
1414
|
+
return val;
|
1415
|
+
}
|
1416
|
+
|
1417
|
+
static VALUE
|
1418
|
+
cb_bucket_on_error_set(VALUE self, VALUE val)
|
1419
|
+
{
|
1420
|
+
bucket_t *bucket = DATA_PTR(self);
|
1421
|
+
|
1422
|
+
if (rb_respond_to(val, id_call)) {
|
1423
|
+
bucket->on_error_proc = val;
|
1424
|
+
} else {
|
1425
|
+
bucket->on_error_proc = Qnil;
|
1426
|
+
}
|
1427
|
+
|
1428
|
+
return bucket->on_error_proc;
|
1429
|
+
}
|
1430
|
+
|
1431
|
+
static VALUE
|
1432
|
+
cb_bucket_on_error_get(VALUE self)
|
1433
|
+
{
|
1434
|
+
bucket_t *bucket = DATA_PTR(self);
|
1435
|
+
|
1436
|
+
if (rb_block_given_p()) {
|
1437
|
+
return cb_bucket_on_error_set(self, rb_block_proc());
|
1438
|
+
} else {
|
1439
|
+
return bucket->on_error_proc;
|
1440
|
+
}
|
1441
|
+
}
|
1442
|
+
|
1443
|
+
static VALUE
|
1444
|
+
cb_bucket_timeout_get(VALUE self)
|
1445
|
+
{
|
1446
|
+
bucket_t *bucket = DATA_PTR(self);
|
1447
|
+
return ULONG2NUM(bucket->timeout);
|
1448
|
+
}
|
1449
|
+
|
1450
|
+
static VALUE
|
1451
|
+
cb_bucket_timeout_set(VALUE self, VALUE val)
|
1452
|
+
{
|
1453
|
+
bucket_t *bucket = DATA_PTR(self);
|
1454
|
+
VALUE tmval;
|
1455
|
+
|
1456
|
+
bucket->timeout = (uint32_t)NUM2ULONG(val);
|
1457
|
+
libcouchbase_set_timeout(bucket->handle, bucket->timeout);
|
1458
|
+
tmval = ULONG2NUM(bucket->timeout);
|
1459
|
+
|
1460
|
+
return tmval;
|
1461
|
+
}
|
1462
|
+
|
1463
|
+
/* Document-method: hostname
|
1464
|
+
* @return [String] the host name of the management interface (default: "localhost")
|
1465
|
+
*/
|
1466
|
+
static VALUE
|
1467
|
+
cb_bucket_hostname_get(VALUE self)
|
1468
|
+
{
|
1469
|
+
bucket_t *bucket = DATA_PTR(self);
|
1470
|
+
if (bucket->handle) {
|
1471
|
+
if (bucket->hostname) {
|
1472
|
+
free(bucket->hostname);
|
1473
|
+
bucket->hostname = NULL;
|
1474
|
+
}
|
1475
|
+
bucket->hostname = strdup(libcouchbase_get_host(bucket->handle));
|
1476
|
+
if (bucket->hostname == NULL) {
|
1477
|
+
rb_raise(eNoMemoryError, "failed to allocate memory for Bucket");
|
1478
|
+
}
|
1479
|
+
}
|
1480
|
+
return rb_str_new2(bucket->hostname);
|
1481
|
+
}
|
1482
|
+
|
1483
|
+
/* Document-method: port
|
1484
|
+
* @return [Fixnum] the port number of the management interface (default: 8091)
|
1485
|
+
*/
|
1486
|
+
static VALUE
|
1487
|
+
cb_bucket_port_get(VALUE self)
|
1488
|
+
{
|
1489
|
+
bucket_t *bucket = DATA_PTR(self);
|
1490
|
+
if (bucket->handle) {
|
1491
|
+
bucket->port = atoi(libcouchbase_get_port(bucket->handle));
|
1492
|
+
}
|
1493
|
+
return UINT2NUM(bucket->port);
|
1494
|
+
}
|
1495
|
+
|
1496
|
+
/* Document-method: authority
|
1497
|
+
* @return [String] host with port
|
1498
|
+
*/
|
1499
|
+
static VALUE
|
1500
|
+
cb_bucket_authority_get(VALUE self)
|
1501
|
+
{
|
1502
|
+
bucket_t *bucket = DATA_PTR(self);
|
1503
|
+
size_t len;
|
1504
|
+
|
1505
|
+
(void)cb_bucket_hostname_get(self);
|
1506
|
+
(void)cb_bucket_port_get(self);
|
1507
|
+
len = strlen(bucket->hostname) + 10;
|
1508
|
+
bucket->authority = calloc(len, sizeof(char));
|
1509
|
+
if (bucket->authority == NULL) {
|
1510
|
+
rb_raise(eNoMemoryError, "failed to allocate memory for Bucket");
|
1511
|
+
}
|
1512
|
+
snprintf(bucket->authority, len, "%s:%u", bucket->hostname, bucket->port);
|
1513
|
+
return rb_str_new2(bucket->authority);
|
1514
|
+
}
|
1515
|
+
|
1516
|
+
/* Document-method: bucket
|
1517
|
+
* @return [String] the bucket name
|
1518
|
+
*/
|
1519
|
+
static VALUE
|
1520
|
+
cb_bucket_bucket_get(VALUE self)
|
1521
|
+
{
|
1522
|
+
bucket_t *bucket = DATA_PTR(self);
|
1523
|
+
return rb_str_new2(bucket->bucket);
|
1524
|
+
}
|
1525
|
+
|
1526
|
+
/* Document-method: pool
|
1527
|
+
* @return [String] the pool name (usually "default")
|
1528
|
+
*/
|
1529
|
+
static VALUE
|
1530
|
+
cb_bucket_pool_get(VALUE self)
|
1531
|
+
{
|
1532
|
+
bucket_t *bucket = DATA_PTR(self);
|
1533
|
+
return rb_str_new2(bucket->pool);
|
1534
|
+
}
|
1535
|
+
|
1536
|
+
/* Document-method: username
|
1537
|
+
* @return [String] the username for protected buckets (usually matches
|
1538
|
+
* the bucket name)
|
1539
|
+
*/
|
1540
|
+
static VALUE
|
1541
|
+
cb_bucket_username_get(VALUE self)
|
1542
|
+
{
|
1543
|
+
bucket_t *bucket = DATA_PTR(self);
|
1544
|
+
return rb_str_new2(bucket->username);
|
1545
|
+
}
|
1546
|
+
|
1547
|
+
/* Document-method: password
|
1548
|
+
* @return [String] the password for protected buckets
|
1549
|
+
*/
|
1550
|
+
static VALUE
|
1551
|
+
cb_bucket_password_get(VALUE self)
|
1552
|
+
{
|
1553
|
+
bucket_t *bucket = DATA_PTR(self);
|
1554
|
+
return rb_str_new2(bucket->password);
|
1555
|
+
}
|
1556
|
+
|
1557
|
+
/* Document-method: url
|
1558
|
+
* @return [String] the address of the cluster management interface
|
1559
|
+
*/
|
1560
|
+
static VALUE
|
1561
|
+
cb_bucket_url_get(VALUE self)
|
1562
|
+
{
|
1563
|
+
bucket_t *bucket = DATA_PTR(self);
|
1564
|
+
VALUE str;
|
1565
|
+
|
1566
|
+
(void)cb_bucket_authority_get(self);
|
1567
|
+
str = rb_str_buf_new2("http://");
|
1568
|
+
rb_str_buf_cat2(str, bucket->authority);
|
1569
|
+
rb_str_buf_cat2(str, "/pools/");
|
1570
|
+
rb_str_buf_cat2(str, bucket->pool);
|
1571
|
+
rb_str_buf_cat2(str, "/buckets/");
|
1572
|
+
rb_str_buf_cat2(str, bucket->bucket);
|
1573
|
+
rb_str_buf_cat2(str, "/");
|
1574
|
+
return str;
|
1575
|
+
}
|
1576
|
+
|
1577
|
+
/*
|
1578
|
+
* Returns a string containing a human-readable representation of the
|
1579
|
+
* Bucket.
|
1580
|
+
*
|
1581
|
+
* @return [String]
|
1582
|
+
*/
|
1583
|
+
static VALUE
|
1584
|
+
cb_bucket_inspect(VALUE self)
|
1585
|
+
{
|
1586
|
+
VALUE str;
|
1587
|
+
bucket_t *bucket = DATA_PTR(self);
|
1588
|
+
char buf[200];
|
1589
|
+
|
1590
|
+
str = rb_str_buf_new2("#<");
|
1591
|
+
rb_str_buf_cat2(str, rb_obj_classname(self));
|
1592
|
+
snprintf(buf, 25, ":%p \"", (void *)self);
|
1593
|
+
(void)cb_bucket_authority_get(self);
|
1594
|
+
rb_str_buf_cat2(str, buf);
|
1595
|
+
rb_str_buf_cat2(str, "http://");
|
1596
|
+
rb_str_buf_cat2(str, bucket->authority);
|
1597
|
+
rb_str_buf_cat2(str, "/pools/");
|
1598
|
+
rb_str_buf_cat2(str, bucket->pool);
|
1599
|
+
rb_str_buf_cat2(str, "/buckets/");
|
1600
|
+
rb_str_buf_cat2(str, bucket->bucket);
|
1601
|
+
rb_str_buf_cat2(str, "/");
|
1602
|
+
snprintf(buf, 150, "\" default_format=:%s, default_flags=0x%x, quiet=%s, connected=%s, timeout=%u>",
|
1603
|
+
rb_id2name(SYM2ID(bucket->default_format)),
|
1604
|
+
bucket->default_flags,
|
1605
|
+
bucket->quiet ? "true" : "false",
|
1606
|
+
bucket->handle ? "true" : "false",
|
1607
|
+
bucket->timeout);
|
1608
|
+
rb_str_buf_cat2(str, buf);
|
1609
|
+
|
1610
|
+
return str;
|
1611
|
+
}
|
1612
|
+
|
1613
|
+
/*
|
1614
|
+
* Delete the specified key
|
1615
|
+
*
|
1616
|
+
* @overload delete(key, options = {})
|
1617
|
+
* @param key [String, Symbol] Key used to reference the value.
|
1618
|
+
* @param options [Hash] Options for operation.
|
1619
|
+
* @option options [Boolean] :quiet (self.quiet) If set to +true+, the
|
1620
|
+
* operation won't raise error for missing key, it will return +nil+.
|
1621
|
+
* Otherwise it will raise error in synchronous mode. In asynchronous
|
1622
|
+
* mode this option ignored.
|
1623
|
+
* @option options [Fixnum] :cas The CAS value for an object. This value
|
1624
|
+
* created on the server and is guaranteed to be unique for each value of
|
1625
|
+
* a given key. This value is used to provide simple optimistic
|
1626
|
+
* concurrency control when multiple clients or threads try to
|
1627
|
+
* update/delete an item simultaneously.
|
1628
|
+
*
|
1629
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
1630
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
1631
|
+
* @raise [Couchbase::Error::KeyExists] on CAS mismatch
|
1632
|
+
* @raise [Couchbase::Error::NotFound] if key is missing in verbose mode
|
1633
|
+
*
|
1634
|
+
* @example Delete the key in quiet mode (default)
|
1635
|
+
* c.set("foo", "bar")
|
1636
|
+
* c.delete("foo") #=> true
|
1637
|
+
* c.delete("foo") #=> false
|
1638
|
+
*
|
1639
|
+
* @example Delete the key verbosely
|
1640
|
+
* c.set("foo", "bar")
|
1641
|
+
* c.delete("foo", :quiet => false) #=> true
|
1642
|
+
* c.delete("foo", :quiet => false) #=> will raise Couchbase::Error::NotFound
|
1643
|
+
*
|
1644
|
+
* @example Delete the key with version check
|
1645
|
+
* ver = c.set("foo", "bar") #=> 5992859822302167040
|
1646
|
+
* c.delete("foo", :cas => 123456) #=> will raise Couchbase::Error::KeyExists
|
1647
|
+
* c.delete("foo", :cas => ver) #=> true
|
1648
|
+
*/
|
1649
|
+
static VALUE
|
1650
|
+
cb_bucket_delete(int argc, VALUE *argv, VALUE self)
|
1651
|
+
{
|
1652
|
+
bucket_t *bucket = DATA_PTR(self);
|
1653
|
+
context_t *ctx;
|
1654
|
+
VALUE k, c, rv, proc, exc, opts;
|
1655
|
+
char *key;
|
1656
|
+
size_t nkey;
|
1657
|
+
libcouchbase_cas_t cas = 0;
|
1658
|
+
libcouchbase_error_t err;
|
1659
|
+
long seqno;
|
1660
|
+
|
1661
|
+
if (bucket->handle == NULL) {
|
1662
|
+
rb_raise(eConnectError, "closed connection");
|
1663
|
+
}
|
1664
|
+
rb_scan_args(argc, argv, "11&", &k, &opts, &proc);
|
1665
|
+
if (!bucket->async && proc != Qnil) {
|
1666
|
+
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
1667
|
+
}
|
1668
|
+
k = unify_key(k);
|
1669
|
+
key = RSTRING_PTR(k);
|
1670
|
+
nkey = RSTRING_LEN(k);
|
1671
|
+
ctx = calloc(1, sizeof(context_t));
|
1672
|
+
ctx->quiet = bucket->quiet;
|
1673
|
+
if (ctx == NULL) {
|
1674
|
+
rb_raise(eNoMemoryError, "failed to allocate memory for context");
|
1675
|
+
}
|
1676
|
+
if (opts != Qnil) {
|
1677
|
+
if (TYPE(opts) == T_BIGNUM || TYPE(opts) == T_FIXNUM) {
|
1678
|
+
cas = NUM2ULL(opts);
|
1679
|
+
} else {
|
1680
|
+
Check_Type(opts, T_HASH);
|
1681
|
+
if ((c = rb_hash_aref(opts, sym_cas)) != Qnil) {
|
1682
|
+
cas = NUM2ULL(c);
|
1683
|
+
}
|
1684
|
+
if (RTEST(rb_funcall(opts, id_has_key_p, 1, sym_quiet))) {
|
1685
|
+
ctx->quiet = RTEST(rb_hash_aref(opts, sym_quiet));
|
1686
|
+
}
|
1687
|
+
}
|
1688
|
+
}
|
1689
|
+
ctx->proc = proc;
|
1690
|
+
rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
|
1691
|
+
rv = rb_ary_new();
|
1692
|
+
ctx->rv = &rv;
|
1693
|
+
ctx->bucket = bucket;
|
1694
|
+
ctx->exception = Qnil;
|
1695
|
+
seqno = bucket->seqno;
|
1696
|
+
bucket->seqno++;
|
1697
|
+
err = libcouchbase_remove(bucket->handle, (const void *)ctx,
|
1698
|
+
(const void *)key, nkey, cas);
|
1699
|
+
exc = cb_check_error(err, "failed to schedule delete request", Qnil);
|
1700
|
+
if (exc != Qnil) {
|
1701
|
+
free(ctx);
|
1702
|
+
rb_exc_raise(exc);
|
1703
|
+
}
|
1704
|
+
if (bucket->async) {
|
1705
|
+
return Qnil;
|
1706
|
+
} else {
|
1707
|
+
if (bucket->seqno - seqno > 0) {
|
1708
|
+
/* we have some operations pending */
|
1709
|
+
bucket->io->run_event_loop(bucket->io);
|
1710
|
+
}
|
1711
|
+
exc = ctx->exception;
|
1712
|
+
free(ctx);
|
1713
|
+
if (exc != Qnil) {
|
1714
|
+
rb_exc_raise(exc);
|
1715
|
+
}
|
1716
|
+
return rv;
|
1717
|
+
}
|
1718
|
+
}
|
1719
|
+
|
1720
|
+
static inline VALUE
|
1721
|
+
cb_bucket_store(libcouchbase_storage_t cmd, int argc, VALUE *argv, VALUE self)
|
1722
|
+
{
|
1723
|
+
bucket_t *bucket = DATA_PTR(self);
|
1724
|
+
context_t *ctx;
|
1725
|
+
VALUE k, v, arg, opts, rv, proc, exc, fmt;
|
1726
|
+
char *key, *bytes;
|
1727
|
+
size_t nkey, nbytes;
|
1728
|
+
uint32_t flags;
|
1729
|
+
time_t exp = 0;
|
1730
|
+
libcouchbase_cas_t cas = 0;
|
1731
|
+
libcouchbase_error_t err;
|
1732
|
+
long seqno;
|
1733
|
+
|
1734
|
+
if (bucket->handle == NULL) {
|
1735
|
+
rb_raise(eConnectError, "closed connection");
|
1736
|
+
}
|
1737
|
+
rb_scan_args(argc, argv, "21&", &k, &v, &opts, &proc);
|
1738
|
+
if (!bucket->async && proc != Qnil) {
|
1739
|
+
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
1740
|
+
}
|
1741
|
+
k = unify_key(k);
|
1742
|
+
flags = bucket->default_flags;
|
1743
|
+
if (opts != Qnil) {
|
1744
|
+
Check_Type(opts, T_HASH);
|
1745
|
+
arg = rb_hash_aref(opts, sym_flags);
|
1746
|
+
if (arg != Qnil) {
|
1747
|
+
flags = (uint32_t)NUM2ULONG(arg);
|
1748
|
+
}
|
1749
|
+
arg = rb_hash_aref(opts, sym_ttl);
|
1750
|
+
if (arg != Qnil) {
|
1751
|
+
exp = NUM2ULONG(arg);
|
1752
|
+
}
|
1753
|
+
arg = rb_hash_aref(opts, sym_cas);
|
1754
|
+
if (arg != Qnil) {
|
1755
|
+
cas = NUM2ULL(arg);
|
1756
|
+
}
|
1757
|
+
fmt = rb_hash_aref(opts, sym_format);
|
1758
|
+
if (fmt != Qnil) { /* rewrite format bits */
|
1759
|
+
flags = flags_set_format(flags, fmt);
|
1760
|
+
}
|
1761
|
+
}
|
1762
|
+
key = RSTRING_PTR(k);
|
1763
|
+
nkey = RSTRING_LEN(k);
|
1764
|
+
v = encode_value(v, flags);
|
1765
|
+
if (v == Qundef) {
|
1766
|
+
rb_raise(eValueFormatError,
|
1767
|
+
"unable to convert value for key '%s'", key);
|
1768
|
+
}
|
1769
|
+
bytes = RSTRING_PTR(v);
|
1770
|
+
nbytes = RSTRING_LEN(v);
|
1771
|
+
ctx = calloc(1, sizeof(context_t));
|
1772
|
+
if (ctx == NULL) {
|
1773
|
+
rb_raise(eNoMemoryError, "failed to allocate memory for context");
|
1774
|
+
}
|
1775
|
+
rv = Qnil;
|
1776
|
+
ctx->rv = &rv;
|
1777
|
+
ctx->bucket = bucket;
|
1778
|
+
ctx->proc = proc;
|
1779
|
+
rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
|
1780
|
+
ctx->exception = Qnil;
|
1781
|
+
seqno = bucket->seqno;
|
1782
|
+
bucket->seqno++;
|
1783
|
+
err = libcouchbase_store(bucket->handle, (const void *)ctx, cmd,
|
1784
|
+
(const void *)key, nkey, bytes, nbytes, flags, exp, cas);
|
1785
|
+
exc = cb_check_error(err, "failed to schedule set request", Qnil);
|
1786
|
+
if (exc != Qnil) {
|
1787
|
+
free(ctx);
|
1788
|
+
rb_exc_raise(exc);
|
1789
|
+
}
|
1790
|
+
if (bucket->async) {
|
1791
|
+
return Qnil;
|
1792
|
+
} else {
|
1793
|
+
if (bucket->seqno - seqno > 0) {
|
1794
|
+
/* we have some operations pending */
|
1795
|
+
bucket->io->run_event_loop(bucket->io);
|
1796
|
+
}
|
1797
|
+
exc = ctx->exception;
|
1798
|
+
free(ctx);
|
1799
|
+
if (exc != Qnil) {
|
1800
|
+
rb_exc_raise(exc);
|
1801
|
+
}
|
1802
|
+
if (bucket->exception != Qnil) {
|
1803
|
+
rb_exc_raise(bucket->exception);
|
1804
|
+
}
|
1805
|
+
return rv;
|
1806
|
+
}
|
1807
|
+
}
|
1808
|
+
|
1809
|
+
static inline VALUE
|
1810
|
+
cb_bucket_arithmetic(int sign, int argc, VALUE *argv, VALUE self)
|
1811
|
+
{
|
1812
|
+
bucket_t *bucket = DATA_PTR(self);
|
1813
|
+
context_t *ctx;
|
1814
|
+
VALUE k, d, arg, opts, rv, proc, exc;
|
1815
|
+
char *key;
|
1816
|
+
size_t nkey;
|
1817
|
+
time_t exp;
|
1818
|
+
uint64_t delta = 0, initial = 0;
|
1819
|
+
int create = 0;
|
1820
|
+
libcouchbase_error_t err;
|
1821
|
+
long seqno;
|
1822
|
+
|
1823
|
+
if (bucket->handle == NULL) {
|
1824
|
+
rb_raise(eConnectError, "closed connection");
|
1825
|
+
}
|
1826
|
+
rb_scan_args(argc, argv, "12&", &k, &d, &opts, &proc);
|
1827
|
+
if (!bucket->async && proc != Qnil) {
|
1828
|
+
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
1829
|
+
}
|
1830
|
+
k = unify_key(k);
|
1831
|
+
ctx = calloc(1, sizeof(context_t));
|
1832
|
+
if (ctx == NULL) {
|
1833
|
+
rb_raise(eNoMemoryError, "failed to allocate memory for context");
|
1834
|
+
}
|
1835
|
+
if (argc == 2 && TYPE(d) == T_HASH) {
|
1836
|
+
opts = d;
|
1837
|
+
d = Qnil;
|
1838
|
+
}
|
1839
|
+
exp = bucket->default_ttl;
|
1840
|
+
if (opts != Qnil) {
|
1841
|
+
Check_Type(opts, T_HASH);
|
1842
|
+
create = RTEST(rb_hash_aref(opts, sym_create));
|
1843
|
+
ctx->extended = RTEST(rb_hash_aref(opts, sym_extended));
|
1844
|
+
arg = rb_hash_aref(opts, sym_ttl);
|
1845
|
+
if (arg != Qnil) {
|
1846
|
+
exp = NUM2ULONG(arg);
|
1847
|
+
}
|
1848
|
+
arg = rb_hash_aref(opts, sym_initial);
|
1849
|
+
if (arg != Qnil) {
|
1850
|
+
initial = NUM2ULL(arg);
|
1851
|
+
create = 1;
|
1852
|
+
}
|
1853
|
+
}
|
1854
|
+
key = RSTRING_PTR(k);
|
1855
|
+
nkey = RSTRING_LEN(k);
|
1856
|
+
if (NIL_P(d)) {
|
1857
|
+
delta = 1 * sign;
|
1858
|
+
} else {
|
1859
|
+
delta = NUM2ULL(d) * sign;
|
1860
|
+
}
|
1861
|
+
rv = Qnil;
|
1862
|
+
ctx->rv = &rv;
|
1863
|
+
ctx->bucket = bucket;
|
1864
|
+
ctx->proc = proc;
|
1865
|
+
rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
|
1866
|
+
ctx->exception = Qnil;
|
1867
|
+
ctx->arithm = sign;
|
1868
|
+
seqno = bucket->seqno;
|
1869
|
+
bucket->seqno++;
|
1870
|
+
err = libcouchbase_arithmetic(bucket->handle, (const void *)ctx,
|
1871
|
+
(const void *)key, nkey, delta, exp, create, initial);
|
1872
|
+
exc = cb_check_error(err, "failed to schedule arithmetic request", k);
|
1873
|
+
if (exc != Qnil) {
|
1874
|
+
free(ctx);
|
1875
|
+
rb_exc_raise(exc);
|
1876
|
+
}
|
1877
|
+
if (bucket->async) {
|
1878
|
+
return Qnil;
|
1879
|
+
} else {
|
1880
|
+
if (bucket->seqno - seqno > 0) {
|
1881
|
+
/* we have some operations pending */
|
1882
|
+
bucket->io->run_event_loop(bucket->io);
|
1883
|
+
}
|
1884
|
+
exc = ctx->exception;
|
1885
|
+
free(ctx);
|
1886
|
+
if (exc != Qnil) {
|
1887
|
+
rb_exc_raise(exc);
|
1888
|
+
}
|
1889
|
+
return rv;
|
1890
|
+
}
|
1891
|
+
}
|
1892
|
+
|
1893
|
+
/*
|
1894
|
+
* Increment the value of an existing numeric key
|
1895
|
+
*
|
1896
|
+
* The increment methods enable you to increase a given stored integer
|
1897
|
+
* value. These are the incremental equivalent of the decrement operations
|
1898
|
+
* and work on the same basis; updating the value of a key if it can be
|
1899
|
+
* parsed to an integer. The update operation occurs on the server and is
|
1900
|
+
* provided at the protocol level. This simplifies what would otherwise be a
|
1901
|
+
* two-stage get and set operation.
|
1902
|
+
*
|
1903
|
+
* @note that server values stored and transmitted as unsigned numbers,
|
1904
|
+
* therefore if you try to store negative number and then increment or
|
1905
|
+
* decrement it will cause overflow. (see "Integer overflow" example
|
1906
|
+
* below)
|
1907
|
+
*
|
1908
|
+
* @overload incr(key, delta = 1, options = {})
|
1909
|
+
* @param key [String, Symbol] Key used to reference the value.
|
1910
|
+
* @param delta [Fixnum] Integer (up to 64 bits) value to increment
|
1911
|
+
* @param options [Hash] Options for operation.
|
1912
|
+
* @option options [Boolean] :create (false) If set to +true+, it will
|
1913
|
+
* initialize the key with zero value and zero flags (use +:initial+
|
1914
|
+
* option to set another initial value). Note: it won't increment the
|
1915
|
+
* missing value.
|
1916
|
+
* @option options [Fixnum] :initial (0) Integer (up to 64 bits) value for
|
1917
|
+
* missing key initialization. This option imply +:create+ option is
|
1918
|
+
* +true+.
|
1919
|
+
* @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
|
1920
|
+
* Values larger than 30*24*60*60 seconds (30 days) are interpreted as
|
1921
|
+
* absolute times (from the epoch). This option ignored for existent
|
1922
|
+
* keys.
|
1923
|
+
* @option options [Boolean] :extended (false) If set to +true+, the
|
1924
|
+
* operation will return tuple +[value, cas]+, otherwise (by default) it
|
1925
|
+
* returns just value.
|
1926
|
+
*
|
1927
|
+
* @yieldparam ret [Result] the result of operation in asynchronous mode
|
1928
|
+
* (valid attributes: +error+, +operation+, +key+, +value+, +cas+).
|
1929
|
+
*
|
1930
|
+
* @return [Fixnum] the actual value of the key.
|
1931
|
+
*
|
1932
|
+
* @raise [Couchbase::Error::NotFound] if key is missing and +:create+
|
1933
|
+
* option isn't +true+.
|
1934
|
+
*
|
1935
|
+
* @raise [Couchbase::Error::DeltaBadval] if the key contains non-numeric
|
1936
|
+
* value
|
1937
|
+
*
|
1938
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
1939
|
+
*
|
1940
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
1941
|
+
*
|
1942
|
+
* @example Increment key by one
|
1943
|
+
* c.incr("foo")
|
1944
|
+
*
|
1945
|
+
* @example Increment key by 50
|
1946
|
+
* c.incr("foo", 50)
|
1947
|
+
*
|
1948
|
+
* @example Increment key by one <b>OR</b> initialize with zero
|
1949
|
+
* c.incr("foo", :create => true) #=> will return old+1 or 0
|
1950
|
+
*
|
1951
|
+
* @example Increment key by one <b>OR</b> initialize with three
|
1952
|
+
* c.incr("foo", 50, :initial => 3) #=> will return old+50 or 3
|
1953
|
+
*
|
1954
|
+
* @example Increment key and get its CAS value
|
1955
|
+
* val, cas = c.incr("foo", :extended => true)
|
1956
|
+
*
|
1957
|
+
* @example Integer overflow
|
1958
|
+
* c.set("foo", -100)
|
1959
|
+
* c.get("foo") #=> -100
|
1960
|
+
* c.incr("foo") #=> 18446744073709551517
|
1961
|
+
*
|
1962
|
+
* @example Asynchronous invocation
|
1963
|
+
* c.run do
|
1964
|
+
* c.incr("foo") do |ret|
|
1965
|
+
* ret.operation #=> :increment
|
1966
|
+
* ret.success? #=> true
|
1967
|
+
* ret.key #=> "foo"
|
1968
|
+
* ret.value
|
1969
|
+
* ret.cas
|
1970
|
+
* end
|
1971
|
+
* end
|
1972
|
+
*
|
1973
|
+
*/
|
1974
|
+
static VALUE
|
1975
|
+
cb_bucket_incr(int argc, VALUE *argv, VALUE self)
|
1976
|
+
{
|
1977
|
+
return cb_bucket_arithmetic(+1, argc, argv, self);
|
1978
|
+
}
|
1979
|
+
|
1980
|
+
/*
|
1981
|
+
* Decrement the value of an existing numeric key
|
1982
|
+
*
|
1983
|
+
* The decrement methods reduce the value of a given key if the
|
1984
|
+
* corresponding value can be parsed to an integer value. These operations
|
1985
|
+
* are provided at a protocol level to eliminate the need to get, update,
|
1986
|
+
* and reset a simple integer value in the database. It supports the use of
|
1987
|
+
* an explicit offset value that will be used to reduce the stored value in
|
1988
|
+
* the database.
|
1989
|
+
*
|
1990
|
+
* @note that server values stored and transmitted as unsigned numbers,
|
1991
|
+
* therefore if you try to decrement negative or zero key, you will always
|
1992
|
+
* get zero.
|
1993
|
+
*
|
1994
|
+
* @overload decr(key, delta = 1, options = {})
|
1995
|
+
* @param key [String, Symbol] Key used to reference the value.
|
1996
|
+
* @param delta [Fixnum] Integer (up to 64 bits) value to decrement
|
1997
|
+
* @param options [Hash] Options for operation.
|
1998
|
+
* @option options [Boolean] :create (false) If set to +true+, it will
|
1999
|
+
* initialize the key with zero value and zero flags (use +:initial+
|
2000
|
+
* option to set another initial value). Note: it won't decrement the
|
2001
|
+
* missing value.
|
2002
|
+
* @option options [Fixnum] :initial (0) Integer (up to 64 bits) value for
|
2003
|
+
* missing key initialization. This option imply +:create+ option is
|
2004
|
+
* +true+.
|
2005
|
+
* @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
|
2006
|
+
* Values larger than 30*24*60*60 seconds (30 days) are interpreted as
|
2007
|
+
* absolute times (from the epoch). This option ignored for existent
|
2008
|
+
* keys.
|
2009
|
+
* @option options [Boolean] :extended (false) If set to +true+, the
|
2010
|
+
* operation will return tuple +[value, cas]+, otherwise (by default) it
|
2011
|
+
* returns just value.
|
2012
|
+
*
|
2013
|
+
* @yieldparam ret [Result] the result of operation in asynchronous mode
|
2014
|
+
* (valid attributes: +error+, +operation+, +key+, +value+, +cas+).
|
2015
|
+
*
|
2016
|
+
* @return [Fixnum] the actual value of the key.
|
2017
|
+
*
|
2018
|
+
* @raise [Couchbase::Error::NotFound] if key is missing and +:create+
|
2019
|
+
* option isn't +true+.
|
2020
|
+
*
|
2021
|
+
* @raise [Couchbase::Error::DeltaBadval] if the key contains non-numeric
|
2022
|
+
* value
|
2023
|
+
*
|
2024
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2025
|
+
*
|
2026
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2027
|
+
*
|
2028
|
+
* @example Decrement key by one
|
2029
|
+
* c.decr("foo")
|
2030
|
+
*
|
2031
|
+
* @example Decrement key by 50
|
2032
|
+
* c.decr("foo", 50)
|
2033
|
+
*
|
2034
|
+
* @example Decrement key by one <b>OR</b> initialize with zero
|
2035
|
+
* c.decr("foo", :create => true) #=> will return old-1 or 0
|
2036
|
+
*
|
2037
|
+
* @example Decrement key by one <b>OR</b> initialize with three
|
2038
|
+
* c.decr("foo", 50, :initial => 3) #=> will return old-50 or 3
|
2039
|
+
*
|
2040
|
+
* @example Decrement key and get its CAS value
|
2041
|
+
* val, cas = c.decr("foo", :extended => true)
|
2042
|
+
*
|
2043
|
+
* @example Decrementing zero
|
2044
|
+
* c.set("foo", 0)
|
2045
|
+
* c.decrement("foo", 100500) #=> 0
|
2046
|
+
*
|
2047
|
+
* @example Decrementing negative value
|
2048
|
+
* c.set("foo", -100)
|
2049
|
+
* c.decrement("foo", 100500) #=> 0
|
2050
|
+
*
|
2051
|
+
* @example Asynchronous invocation
|
2052
|
+
* c.run do
|
2053
|
+
* c.decr("foo") do |ret|
|
2054
|
+
* ret.operation #=> :decrement
|
2055
|
+
* ret.success? #=> true
|
2056
|
+
* ret.key #=> "foo"
|
2057
|
+
* ret.value
|
2058
|
+
* ret.cas
|
2059
|
+
* end
|
2060
|
+
* end
|
2061
|
+
*
|
2062
|
+
*/
|
2063
|
+
static VALUE
|
2064
|
+
cb_bucket_decr(int argc, VALUE *argv, VALUE self)
|
2065
|
+
{
|
2066
|
+
return cb_bucket_arithmetic(-1, argc, argv, self);
|
2067
|
+
}
|
2068
|
+
|
2069
|
+
/*
|
2070
|
+
* Obtain an object stored in Couchbase by given key.
|
2071
|
+
*
|
2072
|
+
* @overload get(*keys, options = {})
|
2073
|
+
* @param keys [String, Symbol, Array] One or several keys to fetch
|
2074
|
+
* @param options [Hash] Options for operation.
|
2075
|
+
* @option options [Boolean] :extended (false) If set to +true+, the
|
2076
|
+
* operation will return tuple +[value, flags, cas]+, otherwise (by
|
2077
|
+
* default) it returns just value.
|
2078
|
+
* @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
|
2079
|
+
* Values larger than 30*24*60*60 seconds (30 days) are interpreted as
|
2080
|
+
* absolute times (from the epoch).
|
2081
|
+
* @option options [Boolean] :quiet (self.quiet) If set to +true+, the
|
2082
|
+
* operation won't raise error for missing key, it will return +nil+.
|
2083
|
+
* Otherwise it will raise error in synchronous mode. In asynchronous
|
2084
|
+
* mode this option ignored.
|
2085
|
+
*
|
2086
|
+
* @yieldparam ret [Result] the result of operation in asynchronous mode
|
2087
|
+
* (valid attributes: +error+, +operation+, +key+, +value+, +flags+,
|
2088
|
+
* +cas+).
|
2089
|
+
*
|
2090
|
+
* @return [Object, Array, Hash] the value(s) (or tuples in extended mode)
|
2091
|
+
* assiciated with the key.
|
2092
|
+
*
|
2093
|
+
* @raise [Couchbase::Error::NotFound] if the key is missing in the
|
2094
|
+
* bucket.
|
2095
|
+
*
|
2096
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2097
|
+
*
|
2098
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2099
|
+
*
|
2100
|
+
* @example Get single value in quite mode (the default)
|
2101
|
+
* c.get("foo") #=> the associated value or nil
|
2102
|
+
*
|
2103
|
+
* @example Use alternative hash-like syntax
|
2104
|
+
* c["foo"] #=> the associated value or nil
|
2105
|
+
*
|
2106
|
+
* @example Get single value in verbose mode
|
2107
|
+
* c.get("missing-foo", :quiet => false) #=> raises Couchbase::NotFound
|
2108
|
+
*
|
2109
|
+
* @example Get and touch single value. The key won't be accessible after 10 seconds
|
2110
|
+
* c.get("foo", :ttl => 10)
|
2111
|
+
*
|
2112
|
+
* @example Extended get
|
2113
|
+
* val, flags, cas = c.get("foo", :extended => true)
|
2114
|
+
*
|
2115
|
+
* @example Get multiple keys
|
2116
|
+
* c.get("foo", "bar", "baz") #=> [val1, val2, val3]
|
2117
|
+
*
|
2118
|
+
* @example Extended get multiple keys
|
2119
|
+
* c.get("foo", "bar", :extended => true)
|
2120
|
+
* #=> {"foo" => [val1, flags1, cas1], "bar" => [val2, flags2, cas2]}
|
2121
|
+
*
|
2122
|
+
* @example Asynchronous get
|
2123
|
+
* c.run do
|
2124
|
+
* c.get("foo", "bar", "baz") do |res|
|
2125
|
+
* ret.operation #=> :get
|
2126
|
+
* ret.success? #=> true
|
2127
|
+
* ret.key #=> "foo", "bar" or "baz" in separate calls
|
2128
|
+
* ret.value
|
2129
|
+
* ret.flags
|
2130
|
+
* ret.cas
|
2131
|
+
* end
|
2132
|
+
* end
|
2133
|
+
*
|
2134
|
+
* @overload get(keys, options = {})
|
2135
|
+
* When the method receive hash map, it will behave like it receive list
|
2136
|
+
* of keys (+keys.keys+), but also touch each key setting expiry time to
|
2137
|
+
* the corresponding value. But unlike usual get this command always
|
2138
|
+
* return hash map +{key => value}+ or +{key => [value, flags, cas]}+.
|
2139
|
+
*
|
2140
|
+
* @param keys [Hash] Map key-ttl
|
2141
|
+
* @param options [Hash] Options for operation. (see options definition
|
2142
|
+
* above)
|
2143
|
+
*
|
2144
|
+
* @return [Hash] the values (or tuples in extended mode) assiciated with
|
2145
|
+
* the keys.
|
2146
|
+
*
|
2147
|
+
* @example Get and touch multiple keys
|
2148
|
+
* c.get("foo" => 10, "bar" => 20) #=> {"foo" => val1, "bar" => val2}
|
2149
|
+
*
|
2150
|
+
* @example Extended get and touch multiple keys
|
2151
|
+
* c.get({"foo" => 10, "bar" => 20}, :extended => true)
|
2152
|
+
* #=> {"foo" => [val1, flags1, cas1], "bar" => [val2, flags2, cas2]}
|
2153
|
+
*/
|
2154
|
+
static VALUE
|
2155
|
+
cb_bucket_get(int argc, VALUE *argv, VALUE self)
|
2156
|
+
{
|
2157
|
+
bucket_t *bucket = DATA_PTR(self);
|
2158
|
+
context_t *ctx;
|
2159
|
+
VALUE args, rv, proc, exc, keys;
|
2160
|
+
long nn;
|
2161
|
+
libcouchbase_error_t err;
|
2162
|
+
struct key_traits *traits;
|
2163
|
+
int extended, mgat;
|
2164
|
+
long seqno;
|
2165
|
+
|
2166
|
+
if (bucket->handle == NULL) {
|
2167
|
+
rb_raise(eConnectError, "closed connection");
|
2168
|
+
}
|
2169
|
+
rb_scan_args(argc, argv, "0*&", &args, &proc);
|
2170
|
+
if (!bucket->async && proc != Qnil) {
|
2171
|
+
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
2172
|
+
}
|
2173
|
+
rb_funcall(args, id_flatten_bang, 0);
|
2174
|
+
traits = calloc(1, sizeof(struct key_traits));
|
2175
|
+
nn = cb_args_scan_keys(RARRAY_LEN(args), args, bucket, traits);
|
2176
|
+
ctx = calloc(1, sizeof(context_t));
|
2177
|
+
if (ctx == NULL) {
|
2178
|
+
rb_raise(eNoMemoryError, "failed to allocate memory for context");
|
2179
|
+
}
|
2180
|
+
mgat = traits->mgat;
|
2181
|
+
keys = traits->keys_ary;
|
2182
|
+
ctx->proc = proc;
|
2183
|
+
rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
|
2184
|
+
ctx->bucket = bucket;
|
2185
|
+
ctx->extended = traits->extended;
|
2186
|
+
ctx->quiet = traits->quiet;
|
2187
|
+
rv = rb_hash_new();
|
2188
|
+
ctx->rv = &rv;
|
2189
|
+
ctx->exception = Qnil;
|
2190
|
+
seqno = bucket->seqno;
|
2191
|
+
bucket->seqno += nn;
|
2192
|
+
err = libcouchbase_mget(bucket->handle, (const void *)ctx,
|
2193
|
+
traits->nkeys, (const void * const *)traits->keys,
|
2194
|
+
traits->lens, (traits->explicit_ttl) ? traits->ttls : NULL);
|
2195
|
+
free(traits->keys);
|
2196
|
+
free(traits->lens);
|
2197
|
+
free(traits->ttls);
|
2198
|
+
free(traits);
|
2199
|
+
exc = cb_check_error(err, "failed to schedule get request", Qnil);
|
2200
|
+
if (exc != Qnil) {
|
2201
|
+
free(ctx);
|
2202
|
+
rb_exc_raise(exc);
|
2203
|
+
}
|
2204
|
+
if (bucket->async) {
|
2205
|
+
return Qnil;
|
2206
|
+
} else {
|
2207
|
+
if (bucket->seqno - seqno > 0) {
|
2208
|
+
/* we have some operations pending */
|
2209
|
+
bucket->io->run_event_loop(bucket->io);
|
2210
|
+
}
|
2211
|
+
exc = ctx->exception;
|
2212
|
+
extended = ctx->extended;
|
2213
|
+
free(ctx);
|
2214
|
+
if (exc != Qnil) {
|
2215
|
+
rb_exc_raise(exc);
|
2216
|
+
}
|
2217
|
+
if (bucket->exception != Qnil) {
|
2218
|
+
rb_exc_raise(bucket->exception);
|
2219
|
+
}
|
2220
|
+
if (mgat || (extended && nn > 1)) {
|
2221
|
+
return rv; /* return as a hash {key => [value, flags, cas], ...} */
|
2222
|
+
}
|
2223
|
+
if (nn > 1) {
|
2224
|
+
long ii;
|
2225
|
+
VALUE *keys_ptr, ret;
|
2226
|
+
ret = rb_ary_new();
|
2227
|
+
keys_ptr = RARRAY_PTR(keys);
|
2228
|
+
for (ii = 0; ii < nn; ii++) {
|
2229
|
+
rb_ary_push(ret, rb_hash_aref(rv, keys_ptr[ii]));
|
2230
|
+
}
|
2231
|
+
return ret; /* return as an array [value1, value2, ...] */
|
2232
|
+
} else {
|
2233
|
+
VALUE vv = Qnil;
|
2234
|
+
rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
|
2235
|
+
return vv;
|
2236
|
+
}
|
2237
|
+
}
|
2238
|
+
}
|
2239
|
+
|
2240
|
+
/*
|
2241
|
+
* Update the expiry time of an item
|
2242
|
+
*
|
2243
|
+
* The +touch+ method allow you to update the expiration time on a given
|
2244
|
+
* key. This can be useful for situations where you want to prevent an item
|
2245
|
+
* from expiring without resetting the associated value. For example, for a
|
2246
|
+
* session database you might want to keep the session alive in the database
|
2247
|
+
* each time the user accesses a web page without explicitly updating the
|
2248
|
+
* session value, keeping the user's session active and available.
|
2249
|
+
*
|
2250
|
+
* @overload touch(key, options = {})
|
2251
|
+
* @param key [String, Symbol] Key used to reference the value.
|
2252
|
+
* @param options [Hash] Options for operation.
|
2253
|
+
* @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
|
2254
|
+
* Values larger than 30*24*60*60 seconds (30 days) are interpreted as
|
2255
|
+
* absolute times (from the epoch).
|
2256
|
+
*
|
2257
|
+
* @yieldparam ret [Result] the result of operation in asynchronous mode
|
2258
|
+
* (valid attributes: +error+, +operation+, +key+).
|
2259
|
+
*
|
2260
|
+
* @return [Boolean] +true+ if the operation was successful and +false+
|
2261
|
+
* otherwise.
|
2262
|
+
*
|
2263
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2264
|
+
*
|
2265
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2266
|
+
*
|
2267
|
+
* @example Touch value using +default_ttl+
|
2268
|
+
* c.touch("foo")
|
2269
|
+
*
|
2270
|
+
* @example Touch value using custom TTL (10 seconds)
|
2271
|
+
* c.touch("foo", :ttl => 10)
|
2272
|
+
*
|
2273
|
+
* @overload touch(keys)
|
2274
|
+
* @param keys [Hash] The Hash where keys represent the keys in the
|
2275
|
+
* database, values -- the expiry times for corresponding key. See
|
2276
|
+
* description of +:ttl+ argument above for more information about TTL
|
2277
|
+
* values.
|
2278
|
+
*
|
2279
|
+
* @yieldparam ret [Result] the result of operation for each key in
|
2280
|
+
* asynchronous mode (valid attributes: +error+, +operation+, +key+).
|
2281
|
+
*
|
2282
|
+
* @return [Hash] Mapping keys to result of touch operation (+true+ if the
|
2283
|
+
* operation was successful and +false+ otherwise)
|
2284
|
+
*
|
2285
|
+
* @example Touch several values
|
2286
|
+
* c.touch("foo" => 10, :bar => 20) #=> {"foo" => true, "bar" => true}
|
2287
|
+
*
|
2288
|
+
* @example Touch several values in async mode
|
2289
|
+
* c.run do
|
2290
|
+
* c.touch("foo" => 10, :bar => 20) do |ret|
|
2291
|
+
* ret.operation #=> :touch
|
2292
|
+
* ret.success? #=> true
|
2293
|
+
* ret.key #=> "foo" and "bar" in separate calls
|
2294
|
+
* end
|
2295
|
+
* end
|
2296
|
+
*
|
2297
|
+
* @example Touch single value
|
2298
|
+
* c.touch("foo" => 10) #=> true
|
2299
|
+
*
|
2300
|
+
*/
|
2301
|
+
static VALUE
|
2302
|
+
cb_bucket_touch(int argc, VALUE *argv, VALUE self)
|
2303
|
+
{
|
2304
|
+
bucket_t *bucket = DATA_PTR(self);
|
2305
|
+
context_t *ctx;
|
2306
|
+
VALUE args, rv, proc, exc;
|
2307
|
+
size_t nn;
|
2308
|
+
libcouchbase_error_t err;
|
2309
|
+
struct key_traits *traits;
|
2310
|
+
long seqno;
|
2311
|
+
|
2312
|
+
if (bucket->handle == NULL) {
|
2313
|
+
rb_raise(eConnectError, "closed connection");
|
2314
|
+
}
|
2315
|
+
rb_scan_args(argc, argv, "0*&", &args, &proc);
|
2316
|
+
if (!bucket->async && proc != Qnil) {
|
2317
|
+
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
2318
|
+
}
|
2319
|
+
rb_funcall(args, id_flatten_bang, 0);
|
2320
|
+
traits = calloc(1, sizeof(struct key_traits));
|
2321
|
+
nn = cb_args_scan_keys(RARRAY_LEN(args), args, bucket, traits);
|
2322
|
+
ctx = calloc(1, sizeof(context_t));
|
2323
|
+
if (ctx == NULL) {
|
2324
|
+
rb_raise(eNoMemoryError, "failed to allocate memory for context");
|
2325
|
+
}
|
2326
|
+
ctx->proc = proc;
|
2327
|
+
rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
|
2328
|
+
ctx->bucket = bucket;
|
2329
|
+
rv = rb_hash_new();
|
2330
|
+
ctx->rv = &rv;
|
2331
|
+
ctx->exception = Qnil;
|
2332
|
+
seqno = bucket->seqno;
|
2333
|
+
bucket->seqno += nn;
|
2334
|
+
err = libcouchbase_mtouch(bucket->handle, (const void *)ctx,
|
2335
|
+
traits->nkeys, (const void * const *)traits->keys,
|
2336
|
+
traits->lens, traits->ttls);
|
2337
|
+
free(traits->keys);
|
2338
|
+
free(traits->lens);
|
2339
|
+
free(traits);
|
2340
|
+
exc = cb_check_error(err, "failed to schedule touch request", Qnil);
|
2341
|
+
if (exc != Qnil) {
|
2342
|
+
free(ctx);
|
2343
|
+
rb_exc_raise(exc);
|
2344
|
+
}
|
2345
|
+
if (bucket->async) {
|
2346
|
+
return Qnil;
|
2347
|
+
} else {
|
2348
|
+
if (bucket->seqno - seqno > 0) {
|
2349
|
+
/* we have some operations pending */
|
2350
|
+
bucket->io->run_event_loop(bucket->io);
|
2351
|
+
}
|
2352
|
+
exc = ctx->exception;
|
2353
|
+
free(ctx);
|
2354
|
+
if (exc != Qnil) {
|
2355
|
+
rb_exc_raise(exc);
|
2356
|
+
}
|
2357
|
+
if (bucket->exception != Qnil) {
|
2358
|
+
rb_exc_raise(bucket->exception);
|
2359
|
+
}
|
2360
|
+
if (nn > 1) {
|
2361
|
+
return rv; /* return as a hash {key => true, ...} */
|
2362
|
+
} else {
|
2363
|
+
VALUE vv = Qnil;
|
2364
|
+
rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
|
2365
|
+
return vv;
|
2366
|
+
}
|
2367
|
+
}
|
2368
|
+
}
|
2369
|
+
|
2370
|
+
/*
|
2371
|
+
* Deletes all values from a server
|
2372
|
+
*
|
2373
|
+
* @overload flush
|
2374
|
+
* @yieldparam [Result] ret the object with +error+, +node+ and +operation+
|
2375
|
+
* attributes.
|
2376
|
+
*
|
2377
|
+
* @return [Boolean] +true+ on success
|
2378
|
+
*
|
2379
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2380
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2381
|
+
*
|
2382
|
+
* @example Simple flush the bucket
|
2383
|
+
* c.flush #=> true
|
2384
|
+
*
|
2385
|
+
* @example Asynchronous flush
|
2386
|
+
* c.run do
|
2387
|
+
* c.flush do |ret|
|
2388
|
+
* ret.operation #=> :flush
|
2389
|
+
* ret.success? #=> true
|
2390
|
+
* ret.node #=> "localhost:11211"
|
2391
|
+
* end
|
2392
|
+
* end
|
2393
|
+
*/
|
2394
|
+
static VALUE
|
2395
|
+
cb_bucket_flush(VALUE self)
|
2396
|
+
{
|
2397
|
+
bucket_t *bucket = DATA_PTR(self);
|
2398
|
+
context_t *ctx;
|
2399
|
+
VALUE rv, exc;
|
2400
|
+
libcouchbase_error_t err;
|
2401
|
+
long seqno;
|
2402
|
+
|
2403
|
+
if (bucket->handle == NULL) {
|
2404
|
+
rb_raise(eConnectError, "closed connection");
|
2405
|
+
}
|
2406
|
+
if (!bucket->async && rb_block_given_p()) {
|
2407
|
+
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
2408
|
+
}
|
2409
|
+
ctx = calloc(1, sizeof(context_t));
|
2410
|
+
if (ctx == NULL) {
|
2411
|
+
rb_raise(eNoMemoryError, "failed to allocate memory for context");
|
2412
|
+
}
|
2413
|
+
rv = Qtrue; /* optimistic by default */
|
2414
|
+
ctx->rv = &rv;
|
2415
|
+
ctx->bucket = bucket;
|
2416
|
+
ctx->exception = Qnil;
|
2417
|
+
if (rb_block_given_p()) {
|
2418
|
+
ctx->proc = rb_block_proc();
|
2419
|
+
} else {
|
2420
|
+
ctx->proc = Qnil;
|
2421
|
+
}
|
2422
|
+
rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
|
2423
|
+
seqno = bucket->seqno;
|
2424
|
+
bucket->seqno++;
|
2425
|
+
err = libcouchbase_flush(bucket->handle, (const void *)ctx);
|
2426
|
+
exc = cb_check_error(err, "failed to schedule flush request", Qnil);
|
2427
|
+
if (exc != Qnil) {
|
2428
|
+
free(ctx);
|
2429
|
+
rb_exc_raise(exc);
|
2430
|
+
}
|
2431
|
+
if (bucket->async) {
|
2432
|
+
return Qnil;
|
2433
|
+
} else {
|
2434
|
+
if (bucket->seqno - seqno > 0) {
|
2435
|
+
/* we have some operations pending */
|
2436
|
+
bucket->io->run_event_loop(bucket->io);
|
2437
|
+
}
|
2438
|
+
exc = ctx->exception;
|
2439
|
+
free(ctx);
|
2440
|
+
if (exc != Qnil) {
|
2441
|
+
rb_exc_raise(exc);
|
2442
|
+
}
|
2443
|
+
return rv;
|
2444
|
+
}
|
2445
|
+
}
|
2446
|
+
|
2447
|
+
/*
|
2448
|
+
* Returns versions of the server for each node in the cluster
|
2449
|
+
*
|
2450
|
+
* @overload version
|
2451
|
+
* @yieldparam [Result] ret the object with +error+, +node+, +operation+
|
2452
|
+
* and +value+ attributes.
|
2453
|
+
*
|
2454
|
+
* @return [Hash] node-version pairs
|
2455
|
+
*
|
2456
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2457
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2458
|
+
*
|
2459
|
+
* @example Synchronous version request
|
2460
|
+
* c.version #=> will render version
|
2461
|
+
*
|
2462
|
+
* @example Asynchronous version request
|
2463
|
+
* c.run do
|
2464
|
+
* c.version do |ret|
|
2465
|
+
* ret.operation #=> :version
|
2466
|
+
* ret.success? #=> true
|
2467
|
+
* ret.node #=> "localhost:11211"
|
2468
|
+
* ret.value #=> will render version
|
2469
|
+
* end
|
2470
|
+
* end
|
2471
|
+
*/
|
2472
|
+
static VALUE
|
2473
|
+
cb_bucket_version(VALUE self)
|
2474
|
+
{
|
2475
|
+
bucket_t *bucket = DATA_PTR(self);
|
2476
|
+
context_t *ctx;
|
2477
|
+
VALUE rv, exc;
|
2478
|
+
libcouchbase_error_t err;
|
2479
|
+
long seqno;
|
2480
|
+
|
2481
|
+
if (bucket->handle == NULL) {
|
2482
|
+
rb_raise(eConnectError, "closed connection");
|
2483
|
+
}
|
2484
|
+
if (!bucket->async && rb_block_given_p()) {
|
2485
|
+
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
2486
|
+
}
|
2487
|
+
ctx = calloc(1, sizeof(context_t));
|
2488
|
+
if (ctx == NULL) {
|
2489
|
+
rb_raise(eNoMemoryError, "failed to allocate memory for context");
|
2490
|
+
}
|
2491
|
+
rv = rb_hash_new();
|
2492
|
+
ctx->rv = &rv;
|
2493
|
+
ctx->bucket = bucket;
|
2494
|
+
ctx->exception = Qnil;
|
2495
|
+
if (rb_block_given_p()) {
|
2496
|
+
ctx->proc = rb_block_proc();
|
2497
|
+
} else {
|
2498
|
+
ctx->proc = Qnil;
|
2499
|
+
}
|
2500
|
+
rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
|
2501
|
+
seqno = bucket->seqno;
|
2502
|
+
bucket->seqno++;
|
2503
|
+
err = libcouchbase_server_versions(bucket->handle, (const void *)ctx);
|
2504
|
+
exc = cb_check_error(err, "failed to schedule version request", Qnil);
|
2505
|
+
if (exc != Qnil) {
|
2506
|
+
free(ctx);
|
2507
|
+
rb_exc_raise(exc);
|
2508
|
+
}
|
2509
|
+
if (bucket->async) {
|
2510
|
+
return Qnil;
|
2511
|
+
} else {
|
2512
|
+
if (bucket->seqno - seqno > 0) {
|
2513
|
+
/* we have some operations pending */
|
2514
|
+
bucket->io->run_event_loop(bucket->io);
|
2515
|
+
}
|
2516
|
+
exc = ctx->exception;
|
2517
|
+
free(ctx);
|
2518
|
+
if (exc != Qnil) {
|
2519
|
+
rb_exc_raise(exc);
|
2520
|
+
}
|
2521
|
+
return rv;
|
2522
|
+
}
|
2523
|
+
}
|
2524
|
+
|
2525
|
+
/*
|
2526
|
+
* Request server statistics.
|
2527
|
+
*
|
2528
|
+
* Fetches stats from each node in cluster. Without a key specified the
|
2529
|
+
* server will respond with a "default" set of statistical information. In
|
2530
|
+
* asynchronous mode each statistic is returned in separate call where the
|
2531
|
+
* Result object yielded (+#key+ contains the name of the statistical item
|
2532
|
+
* and the +#value+ contains the value, the +#node+ will indicate the server
|
2533
|
+
* address). In synchronous mode it returns the hash of stats keys and
|
2534
|
+
* node-value pairs as a value.
|
2535
|
+
*
|
2536
|
+
* @overload stats(arg = nil)
|
2537
|
+
* @param [String] arg argument to STATS query
|
2538
|
+
* @yieldparam [Result] ret the object with +node+, +key+ and +value+
|
2539
|
+
* attributes.
|
2540
|
+
*
|
2541
|
+
* @example Found how many items in the bucket
|
2542
|
+
* total = 0
|
2543
|
+
* c.stats["total_items"].each do |key, value|
|
2544
|
+
* total += value.to_i
|
2545
|
+
* end
|
2546
|
+
*
|
2547
|
+
* @example Found total items number asynchronously
|
2548
|
+
* total = 0
|
2549
|
+
* c.run do
|
2550
|
+
* c.stats do |ret|
|
2551
|
+
* if ret.key == "total_items"
|
2552
|
+
* total += ret.value.to_i
|
2553
|
+
* end
|
2554
|
+
* end
|
2555
|
+
* end
|
2556
|
+
*
|
2557
|
+
* @example Get memory stats (works on couchbase buckets)
|
2558
|
+
* c.stats(:memory) #=> {"mem_used"=>{...}, ...}
|
2559
|
+
*
|
2560
|
+
* @return [Hash] where keys are stat keys, values are host-value pairs
|
2561
|
+
*
|
2562
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2563
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2564
|
+
*/
|
2565
|
+
static VALUE
|
2566
|
+
cb_bucket_stats(int argc, VALUE *argv, VALUE self)
|
2567
|
+
{
|
2568
|
+
bucket_t *bucket = DATA_PTR(self);
|
2569
|
+
context_t *ctx;
|
2570
|
+
VALUE rv, exc, arg, proc;
|
2571
|
+
char *key;
|
2572
|
+
size_t nkey;
|
2573
|
+
libcouchbase_error_t err;
|
2574
|
+
long seqno;
|
2575
|
+
|
2576
|
+
if (bucket->handle == NULL) {
|
2577
|
+
rb_raise(eConnectError, "closed connection");
|
2578
|
+
}
|
2579
|
+
rb_scan_args(argc, argv, "01&", &arg, &proc);
|
2580
|
+
if (!bucket->async && proc != Qnil) {
|
2581
|
+
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
2582
|
+
}
|
2583
|
+
|
2584
|
+
ctx = calloc(1, sizeof(context_t));
|
2585
|
+
if (ctx == NULL) {
|
2586
|
+
rb_raise(eNoMemoryError, "failed to allocate memory for context");
|
2587
|
+
}
|
2588
|
+
rv = rb_hash_new();
|
2589
|
+
ctx->rv = &rv;
|
2590
|
+
ctx->bucket = bucket;
|
2591
|
+
ctx->proc = proc;
|
2592
|
+
rb_hash_aset(object_space, ctx->proc|1, ctx->proc);
|
2593
|
+
ctx->exception = Qnil;
|
2594
|
+
if (arg != Qnil) {
|
2595
|
+
arg = unify_key(arg);
|
2596
|
+
key = RSTRING_PTR(arg);
|
2597
|
+
nkey = RSTRING_LEN(arg);
|
2598
|
+
} else {
|
2599
|
+
key = NULL;
|
2600
|
+
nkey = 0;
|
2601
|
+
}
|
2602
|
+
seqno = bucket->seqno;
|
2603
|
+
bucket->seqno++;
|
2604
|
+
err = libcouchbase_server_stats(bucket->handle, (const void *)ctx,
|
2605
|
+
key, nkey);
|
2606
|
+
exc = cb_check_error(err, "failed to schedule stat request", Qnil);
|
2607
|
+
if (exc != Qnil) {
|
2608
|
+
free(ctx);
|
2609
|
+
rb_exc_raise(exc);
|
2610
|
+
}
|
2611
|
+
if (bucket->async) {
|
2612
|
+
return Qnil;
|
2613
|
+
} else {
|
2614
|
+
if (bucket->seqno - seqno > 0) {
|
2615
|
+
/* we have some operations pending */
|
2616
|
+
bucket->io->run_event_loop(bucket->io);
|
2617
|
+
}
|
2618
|
+
exc = ctx->exception;
|
2619
|
+
free(ctx);
|
2620
|
+
if (exc != Qnil) {
|
2621
|
+
rb_exc_raise(exc);
|
2622
|
+
}
|
2623
|
+
if (bucket->exception != Qnil) {
|
2624
|
+
rb_exc_raise(bucket->exception);
|
2625
|
+
}
|
2626
|
+
return rv;
|
2627
|
+
}
|
2628
|
+
|
2629
|
+
return Qnil;
|
2630
|
+
}
|
2631
|
+
|
2632
|
+
static VALUE
|
2633
|
+
do_run(VALUE *args)
|
2634
|
+
{
|
2635
|
+
VALUE self = args[0], proc = args[1], exc;
|
2636
|
+
bucket_t *bucket = DATA_PTR(self);
|
2637
|
+
time_t tm;
|
2638
|
+
uint32_t old_tmo, new_tmo, diff;
|
2639
|
+
|
2640
|
+
if (bucket->handle == NULL) {
|
2641
|
+
rb_raise(eConnectError, "closed connection");
|
2642
|
+
}
|
2643
|
+
if (bucket->async) {
|
2644
|
+
rb_raise(eInvalidError, "nested #run");
|
2645
|
+
}
|
2646
|
+
bucket->seqno = 0;
|
2647
|
+
bucket->async = 1;
|
2648
|
+
|
2649
|
+
tm = time(NULL);
|
2650
|
+
cb_proc_call(proc, 1, self);
|
2651
|
+
if (bucket->seqno > 0) {
|
2652
|
+
old_tmo = libcouchbase_get_timeout(bucket->handle);
|
2653
|
+
diff = (uint32_t)(time(NULL) - tm + 1);
|
2654
|
+
diff *= 1000000;
|
2655
|
+
new_tmo = bucket->timeout += diff;
|
2656
|
+
libcouchbase_set_timeout(bucket->handle, bucket->timeout);
|
2657
|
+
bucket->io->run_event_loop(bucket->io);
|
2658
|
+
/* restore timeout if it wasn't changed */
|
2659
|
+
if (bucket->timeout == new_tmo) {
|
2660
|
+
libcouchbase_set_timeout(bucket->handle, old_tmo);
|
2661
|
+
}
|
2662
|
+
if (bucket->exception != Qnil) {
|
2663
|
+
exc = bucket->exception;
|
2664
|
+
bucket->exception = Qnil;
|
2665
|
+
rb_exc_raise(exc);
|
2666
|
+
}
|
2667
|
+
}
|
2668
|
+
return Qnil;
|
2669
|
+
}
|
2670
|
+
|
2671
|
+
static VALUE
|
2672
|
+
ensure_run(VALUE *args)
|
2673
|
+
{
|
2674
|
+
VALUE self = args[0];
|
2675
|
+
bucket_t *bucket = DATA_PTR(self);
|
2676
|
+
|
2677
|
+
bucket->async = 0;
|
2678
|
+
return Qnil;
|
2679
|
+
}
|
2680
|
+
|
2681
|
+
/*
|
2682
|
+
* Run the event loop.
|
2683
|
+
*
|
2684
|
+
* @yieldparam [Bucket] bucket the bucket instance
|
2685
|
+
*
|
2686
|
+
* @example Use block to run the loop
|
2687
|
+
* c = Couchbase.new
|
2688
|
+
* c.run do
|
2689
|
+
* c.get("foo") {|ret| puts ret.value}
|
2690
|
+
* end
|
2691
|
+
*
|
2692
|
+
* @example Use lambda to run the loop
|
2693
|
+
* c = Couchbase.new
|
2694
|
+
* operations = lambda do |c|
|
2695
|
+
* c.get("foo") {|ret| puts ret.value}
|
2696
|
+
* end
|
2697
|
+
* c.run(&operations)
|
2698
|
+
*
|
2699
|
+
* @return [nil]
|
2700
|
+
*
|
2701
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2702
|
+
*/
|
2703
|
+
static VALUE
|
2704
|
+
cb_bucket_run(VALUE self)
|
2705
|
+
{
|
2706
|
+
VALUE args[2];
|
2707
|
+
|
2708
|
+
rb_need_block();
|
2709
|
+
args[0] = self;
|
2710
|
+
args[1] = rb_block_proc();
|
2711
|
+
rb_ensure(do_run, (VALUE)args, ensure_run, (VALUE)args);
|
2712
|
+
return Qnil;
|
2713
|
+
}
|
2714
|
+
|
2715
|
+
/*
|
2716
|
+
* Unconditionally store the object in the Couchbase
|
2717
|
+
*
|
2718
|
+
* @overload set(key, value, options = {})
|
2719
|
+
*
|
2720
|
+
* @param key [String, Symbol] Key used to reference the value.
|
2721
|
+
* @param value [Object] Value to be stored
|
2722
|
+
* @param options [Hash] Options for operation.
|
2723
|
+
* @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
|
2724
|
+
* Values larger than 30*24*60*60 seconds (30 days) are interpreted as
|
2725
|
+
* absolute times (from the epoch).
|
2726
|
+
* @option options [Fixnum] :flags (self.default_flags) Flags for storage
|
2727
|
+
* options. Flags are ignored by the server but preserved for use by the
|
2728
|
+
* client. For more info see {Bucket#default_flags}.
|
2729
|
+
* @option options [Symbol] :format (self.default_format) The
|
2730
|
+
* representation for storing the value in the bucket. For more info see
|
2731
|
+
* {Bucket#default_format}.
|
2732
|
+
* @option options [Fixnum] :cas The CAS value for an object. This value
|
2733
|
+
* created on the server and is guaranteed to be unique for each value of
|
2734
|
+
* a given key. This value is used to provide simple optimistic
|
2735
|
+
* concurrency control when multiple clients or threads try to update an
|
2736
|
+
* item simultaneously.
|
2737
|
+
*
|
2738
|
+
* @yieldparam ret [Result] the result of operation in asynchronous mode
|
2739
|
+
* (valid attributes: +error+, +operation+, +key+).
|
2740
|
+
*
|
2741
|
+
* @return [Fixnum] The CAS value of the object.
|
2742
|
+
*
|
2743
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect}).
|
2744
|
+
* @raise [Couchbase::Error::KeyExists] if the key already exists on the
|
2745
|
+
* server.
|
2746
|
+
* @raise [Couchbase::Error::ValueFormat] if the value cannot be serialized
|
2747
|
+
* with chosen encoder, e.g. if you try to store the Hash in +:plain+
|
2748
|
+
* mode.
|
2749
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2750
|
+
*
|
2751
|
+
* @example Store the key which will be expired in 2 seconds using relative TTL.
|
2752
|
+
* c.set("foo", "bar", :ttl => 2)
|
2753
|
+
*
|
2754
|
+
* @example Store the key which will be expired in 2 seconds using absolute TTL.
|
2755
|
+
* c.set("foo", "bar", :ttl => Time.now.to_i + 2)
|
2756
|
+
*
|
2757
|
+
* @example Force JSON document format for value
|
2758
|
+
* c.set("foo", {"bar" => "baz}, :format => :document)
|
2759
|
+
*
|
2760
|
+
* @example Use hash-like syntax to store the value
|
2761
|
+
* c.set["foo"] = {"bar" => "baz}
|
2762
|
+
*
|
2763
|
+
* @example Use extended hash-like syntax
|
2764
|
+
* c["foo", {:flags => 0x1000, :format => :plain}] = "bar"
|
2765
|
+
* c["foo", :flags => 0x1000] = "bar" # for ruby 1.9.x only
|
2766
|
+
*
|
2767
|
+
* @example Set application specific flags (note that it will be OR-ed with format flags)
|
2768
|
+
* c.set("foo", "bar", :flags => 0x1000)
|
2769
|
+
*
|
2770
|
+
* @example Perform optimistic locking by specifying last known CAS version
|
2771
|
+
* c.set("foo", "bar", :cas => 8835713818674332672)
|
2772
|
+
*
|
2773
|
+
* @example Perform asynchronous call
|
2774
|
+
* c.run do
|
2775
|
+
* c.set("foo", "bar") do |ret|
|
2776
|
+
* ret.operation #=> :set
|
2777
|
+
* ret.success? #=> true
|
2778
|
+
* ret.key #=> "foo"
|
2779
|
+
* ret.cas
|
2780
|
+
* end
|
2781
|
+
* end
|
2782
|
+
*/
|
2783
|
+
static VALUE
|
2784
|
+
cb_bucket_set(int argc, VALUE *argv, VALUE self)
|
2785
|
+
{
|
2786
|
+
return cb_bucket_store(LIBCOUCHBASE_SET, argc, argv, self);
|
2787
|
+
}
|
2788
|
+
|
2789
|
+
/*
|
2790
|
+
* Add the item to the database, but fail if the object exists already
|
2791
|
+
*
|
2792
|
+
* @overload add(key, value, options = {})
|
2793
|
+
* @param key [String, Symbol] Key used to reference the value.
|
2794
|
+
* @param value [Object] Value to be stored
|
2795
|
+
* @param options [Hash] Options for operation.
|
2796
|
+
* @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
|
2797
|
+
* Values larger than 30*24*60*60 seconds (30 days) are interpreted as
|
2798
|
+
* absolute times (from the epoch).
|
2799
|
+
* @option options [Fixnum] :flags (self.default_flags) Flags for storage
|
2800
|
+
* options. Flags are ignored by the server but preserved for use by the
|
2801
|
+
* client. For more info see {Bucket#default_flags}.
|
2802
|
+
* @option options [Symbol] :format (self.default_format) The
|
2803
|
+
* representation for storing the value in the bucket. For more info see
|
2804
|
+
* {Bucket#default_format}.
|
2805
|
+
* @option options [Fixnum] :cas The CAS value for an object. This value
|
2806
|
+
* created on the server and is guaranteed to be unique for each value of
|
2807
|
+
* a given key. This value is used to provide simple optimistic
|
2808
|
+
* concurrency control when multiple clients or threads try to update an
|
2809
|
+
* item simultaneously.
|
2810
|
+
*
|
2811
|
+
* @yieldparam ret [Result] the result of operation in asynchronous mode
|
2812
|
+
* (valid attributes: +error+, +operation+, +key+).
|
2813
|
+
*
|
2814
|
+
* @return [Fixnum] The CAS value of the object.
|
2815
|
+
*
|
2816
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2817
|
+
* @raise [Couchbase::Error::KeyExists] if the key already exists on the
|
2818
|
+
* server
|
2819
|
+
* @raise [Couchbase::Error::ValueFormat] if the value cannot be serialized
|
2820
|
+
* with chosen encoder, e.g. if you try to store the Hash in +:plain+
|
2821
|
+
* mode.
|
2822
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2823
|
+
*
|
2824
|
+
* @example Add the same key twice
|
2825
|
+
* c.add("foo", "bar") #=> stored successully
|
2826
|
+
* c.add("foo", "baz") #=> will raise Couchbase::Error::KeyExists: failed to store value (key="foo", error=0x0c)
|
2827
|
+
*/
|
2828
|
+
static VALUE
|
2829
|
+
cb_bucket_add(int argc, VALUE *argv, VALUE self)
|
2830
|
+
{
|
2831
|
+
return cb_bucket_store(LIBCOUCHBASE_ADD, argc, argv, self);
|
2832
|
+
}
|
2833
|
+
|
2834
|
+
/*
|
2835
|
+
* Replace the existing object in the database
|
2836
|
+
*
|
2837
|
+
* @overload replace(key, value, options = {})
|
2838
|
+
* @param key [String, Symbol] Key used to reference the value.
|
2839
|
+
* @param value [Object] Value to be stored
|
2840
|
+
* @param options [Hash] Options for operation.
|
2841
|
+
* @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
|
2842
|
+
* Values larger than 30*24*60*60 seconds (30 days) are interpreted as
|
2843
|
+
* absolute times (from the epoch).
|
2844
|
+
* @option options [Fixnum] :flags (self.default_flags) Flags for storage
|
2845
|
+
* options. Flags are ignored by the server but preserved for use by the
|
2846
|
+
* client. For more info see {Bucket#default_flags}.
|
2847
|
+
* @option options [Symbol] :format (self.default_format) The
|
2848
|
+
* representation for storing the value in the bucket. For more info see
|
2849
|
+
* {Bucket#default_format}.
|
2850
|
+
*
|
2851
|
+
* @return [Fixnum] The CAS value of the object.
|
2852
|
+
*
|
2853
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2854
|
+
* @raise [Couchbase::Error::NotFound] if the key doesn't exists
|
2855
|
+
* @raise [Couchbase::Error::KeyExists] on CAS mismatch
|
2856
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2857
|
+
*
|
2858
|
+
* @example Replacing missing key
|
2859
|
+
* c.replace("foo", "baz") #=> will raise Couchbase::Error::NotFound: failed to store value (key="foo", error=0x0d)
|
2860
|
+
*/
|
2861
|
+
static VALUE
|
2862
|
+
cb_bucket_replace(int argc, VALUE *argv, VALUE self)
|
2863
|
+
{
|
2864
|
+
return cb_bucket_store(LIBCOUCHBASE_REPLACE, argc, argv, self);
|
2865
|
+
}
|
2866
|
+
|
2867
|
+
/*
|
2868
|
+
* Append this object to the existing object
|
2869
|
+
*
|
2870
|
+
* @note This operation is kind of data-aware from server point of view.
|
2871
|
+
* This mean that the server treats value as binary stream and just
|
2872
|
+
* perform concatenation, therefore it won't work with +:marshal+ and
|
2873
|
+
* +:document+ formats, because of lack of knowledge how to merge values
|
2874
|
+
* in these formats. See Bucket#cas for workaround.
|
2875
|
+
*
|
2876
|
+
* @overload append(key, value, options = {})
|
2877
|
+
* @param key [String, Symbol] Key used to reference the value.
|
2878
|
+
* @param value [Object] Value to be stored
|
2879
|
+
* @param options [Hash] Options for operation.
|
2880
|
+
* @option options [Fixnum] :cas The CAS value for an object. This value
|
2881
|
+
* created on the server and is guaranteed to be unique for each value of
|
2882
|
+
* a given key. This value is used to provide simple optimistic
|
2883
|
+
* concurrency control when multiple clients or threads try to update an
|
2884
|
+
* item simultaneously.
|
2885
|
+
* @option options [Symbol] :format (self.default_format) The
|
2886
|
+
* representation for storing the value in the bucket. For more info see
|
2887
|
+
* {Bucket#default_format}.
|
2888
|
+
*
|
2889
|
+
* @return [Fixnum] The CAS value of the object.
|
2890
|
+
*
|
2891
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2892
|
+
* @raise [Couchbase::Error::KeyExists] on CAS mismatch
|
2893
|
+
* @raise [Couchbase::Error::NotStored] if the key doesn't exist
|
2894
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2895
|
+
*
|
2896
|
+
* @example Simple append
|
2897
|
+
* c.set("foo", "aaa")
|
2898
|
+
* c.append("foo", "bbb")
|
2899
|
+
* c.get("foo") #=> "aaabbb"
|
2900
|
+
*
|
2901
|
+
* @example Implementing sets using append
|
2902
|
+
* def set_add(key, *values)
|
2903
|
+
* encoded = values.flatten.map{|v| "+#{v} "}.join
|
2904
|
+
* append(key, encoded)
|
2905
|
+
* end
|
2906
|
+
*
|
2907
|
+
* def set_remove(key, *values)
|
2908
|
+
* encoded = values.flatten.map{|v| "-#{v} "}.join
|
2909
|
+
* append(key, encoded)
|
2910
|
+
* end
|
2911
|
+
*
|
2912
|
+
* def set_get(key)
|
2913
|
+
* encoded = get(key)
|
2914
|
+
* ret = Set.new
|
2915
|
+
* encoded.split(' ').each do |v|
|
2916
|
+
* op, val = v[0], v[1..-1]
|
2917
|
+
* case op
|
2918
|
+
* when "-"
|
2919
|
+
* ret.delete(val)
|
2920
|
+
* when "+"
|
2921
|
+
* ret.add(val)
|
2922
|
+
* end
|
2923
|
+
* end
|
2924
|
+
* ret
|
2925
|
+
* end
|
2926
|
+
*
|
2927
|
+
* @example Using optimistic locking. The operation will fail on CAS mismatch
|
2928
|
+
* ver = c.set("foo", "aaa")
|
2929
|
+
* c.append("foo", "bbb", :cas => ver)
|
2930
|
+
*/
|
2931
|
+
static VALUE
|
2932
|
+
cb_bucket_append(int argc, VALUE *argv, VALUE self)
|
2933
|
+
{
|
2934
|
+
return cb_bucket_store(LIBCOUCHBASE_APPEND, argc, argv, self);
|
2935
|
+
}
|
2936
|
+
|
2937
|
+
/*
|
2938
|
+
* Prepend this object to the existing object
|
2939
|
+
*
|
2940
|
+
* @note This operation is kind of data-aware from server point of view.
|
2941
|
+
* This mean that the server treats value as binary stream and just
|
2942
|
+
* perform concatenation, therefore it won't work with +:marshal+ and
|
2943
|
+
* +:document+ formats, because of lack of knowledge how to merge values
|
2944
|
+
* in these formats. See Bucket#cas for workaround.
|
2945
|
+
*
|
2946
|
+
* @overload prepend(key, value, options = {})
|
2947
|
+
* @param key [String, Symbol] Key used to reference the value.
|
2948
|
+
* @param value [Object] Value to be stored
|
2949
|
+
* @param options [Hash] Options for operation.
|
2950
|
+
* @option options [Fixnum] :cas The CAS value for an object. This value
|
2951
|
+
* created on the server and is guaranteed to be unique for each value of
|
2952
|
+
* a given key. This value is used to provide simple optimistic
|
2953
|
+
* concurrency control when multiple clients or threads try to update an
|
2954
|
+
* item simultaneously.
|
2955
|
+
* @option options [Symbol] :format (self.default_format) The
|
2956
|
+
* representation for storing the value in the bucket. For more info see
|
2957
|
+
* {Bucket#default_format}.
|
2958
|
+
*
|
2959
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
2960
|
+
* @raise [Couchbase::Error::KeyExists] on CAS mismatch
|
2961
|
+
* @raise [Couchbase::Error::NotStored] if the key doesn't exist
|
2962
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
2963
|
+
*
|
2964
|
+
* @example Simple prepend example
|
2965
|
+
* c.set("foo", "aaa")
|
2966
|
+
* c.prepend("foo", "bbb")
|
2967
|
+
* c.get("foo") #=> "bbbaaa"
|
2968
|
+
*
|
2969
|
+
* @example Using explicit format option
|
2970
|
+
* c.default_format #=> :document
|
2971
|
+
* c.set("foo", {"y" => "z"})
|
2972
|
+
* c.prepend("foo", '[', :format => :plain)
|
2973
|
+
* c.append("foo", ', {"z": "y"}]', :format => :plain)
|
2974
|
+
* c.get("foo") #=> [{"y"=>"z"}, {"z"=>"y"}]
|
2975
|
+
*
|
2976
|
+
* @example Using optimistic locking. The operation will fail on CAS mismatch
|
2977
|
+
* ver = c.set("foo", "aaa")
|
2978
|
+
* c.prepend("foo", "bbb", :cas => ver)
|
2979
|
+
*/
|
2980
|
+
static VALUE
|
2981
|
+
cb_bucket_prepend(int argc, VALUE *argv, VALUE self)
|
2982
|
+
{
|
2983
|
+
return cb_bucket_store(LIBCOUCHBASE_PREPEND, argc, argv, self);
|
2984
|
+
}
|
2985
|
+
|
2986
|
+
static VALUE
|
2987
|
+
cb_bucket_aset(int argc, VALUE *argv, VALUE self)
|
2988
|
+
{
|
2989
|
+
VALUE temp;
|
2990
|
+
|
2991
|
+
if (argc == 3) {
|
2992
|
+
/* swap opts and value, because value goes last for []= */
|
2993
|
+
temp = argv[2];
|
2994
|
+
argv[2] = argv[1];
|
2995
|
+
argv[1] = temp;
|
2996
|
+
}
|
2997
|
+
return cb_bucket_set(argc, argv, self);
|
2998
|
+
}
|
2999
|
+
|
3000
|
+
/*
|
3001
|
+
* Close the connection to the cluster
|
3002
|
+
*
|
3003
|
+
* @return [true]
|
3004
|
+
*
|
3005
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
3006
|
+
*/
|
3007
|
+
static VALUE
|
3008
|
+
cb_bucket_disconnect(VALUE self)
|
3009
|
+
{
|
3010
|
+
bucket_t *bucket = DATA_PTR(self);
|
3011
|
+
|
3012
|
+
if (bucket->handle) {
|
3013
|
+
libcouchbase_destroy(bucket->handle);
|
3014
|
+
bucket->handle = NULL;
|
3015
|
+
bucket->io = NULL;
|
3016
|
+
return Qtrue;
|
3017
|
+
} else {
|
3018
|
+
rb_raise(eConnectError, "closed connection");
|
3019
|
+
}
|
3020
|
+
}
|
3021
|
+
|
3022
|
+
/*
|
3023
|
+
* Check if result of operation was successful.
|
3024
|
+
*
|
3025
|
+
* @return [Boolean] +false+ if there is an +error+ object attached,
|
3026
|
+
* +false+ otherwise.
|
3027
|
+
*/
|
3028
|
+
static VALUE
|
3029
|
+
cb_result_success_p(VALUE self)
|
3030
|
+
{
|
3031
|
+
return RTEST(rb_ivar_get(self, id_iv_error)) ? Qfalse : Qtrue;
|
3032
|
+
}
|
3033
|
+
|
3034
|
+
/*
|
3035
|
+
* Returns a string containing a human-readable representation of the Result.
|
3036
|
+
*
|
3037
|
+
* @return [String]
|
3038
|
+
*/
|
3039
|
+
static VALUE
|
3040
|
+
cb_result_inspect(VALUE self)
|
3041
|
+
{
|
3042
|
+
VALUE str, attr, error;
|
3043
|
+
char buf[100];
|
3044
|
+
|
3045
|
+
str = rb_str_buf_new2("#<");
|
3046
|
+
rb_str_buf_cat2(str, rb_obj_classname(self));
|
3047
|
+
snprintf(buf, 100, ":%p", (void *)self);
|
3048
|
+
rb_str_buf_cat2(str, buf);
|
3049
|
+
|
3050
|
+
attr = rb_ivar_get(self, id_iv_error);
|
3051
|
+
if (RTEST(attr)) {
|
3052
|
+
error = rb_ivar_get(attr, id_iv_error);
|
3053
|
+
} else {
|
3054
|
+
error = INT2FIX(0);
|
3055
|
+
}
|
3056
|
+
rb_str_buf_cat2(str, " error=0x");
|
3057
|
+
rb_str_append(str, rb_funcall(error, id_to_s, 1, INT2FIX(16)));
|
3058
|
+
|
3059
|
+
attr = rb_ivar_get(self, id_iv_key);
|
3060
|
+
if (RTEST(attr)) {
|
3061
|
+
rb_str_buf_cat2(str, " key=");
|
3062
|
+
rb_str_append(str, rb_inspect(attr));
|
3063
|
+
}
|
3064
|
+
|
3065
|
+
attr = rb_ivar_get(self, id_iv_cas);
|
3066
|
+
if (RTEST(attr)) {
|
3067
|
+
rb_str_buf_cat2(str, " cas=");
|
3068
|
+
rb_str_append(str, rb_inspect(attr));
|
3069
|
+
}
|
3070
|
+
|
3071
|
+
attr = rb_ivar_get(self, id_iv_flags);
|
3072
|
+
if (RTEST(attr)) {
|
3073
|
+
rb_str_buf_cat2(str, " flags=0x");
|
3074
|
+
rb_str_append(str, rb_funcall(attr, id_to_s, 1, INT2FIX(16)));
|
3075
|
+
}
|
3076
|
+
|
3077
|
+
attr = rb_ivar_get(self, id_iv_node);
|
3078
|
+
if (RTEST(attr)) {
|
3079
|
+
rb_str_buf_cat2(str, " node=");
|
3080
|
+
rb_str_append(str, rb_inspect(attr));
|
3081
|
+
}
|
3082
|
+
rb_str_buf_cat2(str, ">");
|
3083
|
+
|
3084
|
+
return str;
|
3085
|
+
}
|
3086
|
+
|
3087
|
+
/* Ruby Extension initializer */
|
3088
|
+
void
|
3089
|
+
Init_couchbase_ext(void)
|
3090
|
+
{
|
3091
|
+
mJSON = rb_const_get(rb_cObject, rb_intern("JSON"));
|
3092
|
+
mURI = rb_const_get(rb_cObject, rb_intern("URI"));
|
3093
|
+
mMarshal = rb_const_get(rb_cObject, rb_intern("Marshal"));
|
3094
|
+
mCouchbase = rb_define_module("Couchbase");
|
3095
|
+
|
3096
|
+
mError = rb_define_module_under(mCouchbase, "Error");
|
3097
|
+
/* Document-class: Couchbase::Error::Base
|
3098
|
+
* The base error class */
|
3099
|
+
eBaseError = rb_define_class_under(mError, "Base", rb_eRuntimeError);
|
3100
|
+
/* Document-class: Couchbase::Error::Auth
|
3101
|
+
* Authentication error */
|
3102
|
+
eAuthError = rb_define_class_under(mError, "Auth", eBaseError);
|
3103
|
+
/* Document-class: Couchbase::Error::BucketNotFound
|
3104
|
+
* The given bucket not found in the cluster */
|
3105
|
+
eBucketNotFoundError = rb_define_class_under(mError, "BucketNotFound", eBaseError);
|
3106
|
+
/* Document-class: Couchbase::Error::Busy
|
3107
|
+
* The cluster is too busy now. Try again later */
|
3108
|
+
eBusyError = rb_define_class_under(mError, "Busy", eBaseError);
|
3109
|
+
/* Document-class: Couchbase::Error::DeltaBadval
|
3110
|
+
* The given value is not a number */
|
3111
|
+
eDeltaBadvalError = rb_define_class_under(mError, "DeltaBadval", eBaseError);
|
3112
|
+
/* Document-class: Couchbase::Error::Internal
|
3113
|
+
* Internal error */
|
3114
|
+
eInternalError = rb_define_class_under(mError, "Internal", eBaseError);
|
3115
|
+
/* Document-class: Couchbase::Error::Invalid
|
3116
|
+
* Invalid arguments */
|
3117
|
+
eInvalidError = rb_define_class_under(mError, "Invalid", eBaseError);
|
3118
|
+
/* Document-class: Couchbase::Error::KeyExists
|
3119
|
+
* Key already exists */
|
3120
|
+
eKeyExistsError = rb_define_class_under(mError, "KeyExists", eBaseError);
|
3121
|
+
/* Document-class: Couchbase::Error::Libcouchbase
|
3122
|
+
* Generic error */
|
3123
|
+
eLibcouchbaseError = rb_define_class_under(mError, "Libcouchbase", eBaseError);
|
3124
|
+
/* Document-class: Couchbase::Error::Libevent
|
3125
|
+
* Problem using libevent */
|
3126
|
+
eLibeventError = rb_define_class_under(mError, "Libevent", eBaseError);
|
3127
|
+
/* Document-class: Couchbase::Error::Network
|
3128
|
+
* Network error */
|
3129
|
+
eNetworkError = rb_define_class_under(mError, "Network", eBaseError);
|
3130
|
+
/* Document-class: Couchbase::Error::NoMemory
|
3131
|
+
* Out of memory error */
|
3132
|
+
eNoMemoryError = rb_define_class_under(mError, "NoMemory", eBaseError);
|
3133
|
+
/* Document-class: Couchbase::Error::NotFound
|
3134
|
+
* No such key */
|
3135
|
+
eNotFoundError = rb_define_class_under(mError, "NotFound", eBaseError);
|
3136
|
+
/* Document-class: Couchbase::Error::NotMyVbucket
|
3137
|
+
* The vbucket is not located on this server */
|
3138
|
+
eNotMyVbucketError = rb_define_class_under(mError, "NotMyVbucket", eBaseError);
|
3139
|
+
/* Document-class: Couchbase::Error::NotStored
|
3140
|
+
* Not stored */
|
3141
|
+
eNotStoredError = rb_define_class_under(mError, "NotStored", eBaseError);
|
3142
|
+
/* Document-class: Couchbase::Error::NotSupported
|
3143
|
+
* Not supported */
|
3144
|
+
eNotSupportedError = rb_define_class_under(mError, "NotSupported", eBaseError);
|
3145
|
+
/* Document-class: Couchbase::Error::Range
|
3146
|
+
* Invalid range */
|
3147
|
+
eRangeError = rb_define_class_under(mError, "Range", eBaseError);
|
3148
|
+
/* Document-class: Couchbase::Error::TemporaryFail
|
3149
|
+
* Temporary failure. Try again later */
|
3150
|
+
eTmpFailError = rb_define_class_under(mError, "TemporaryFail", eBaseError);
|
3151
|
+
/* Document-class: Couchbase::Error::TooBig
|
3152
|
+
* Object too big */
|
3153
|
+
eTooBigError = rb_define_class_under(mError, "TooBig", eBaseError);
|
3154
|
+
/* Document-class: Couchbase::Error::UnknownCommand
|
3155
|
+
* Unknown command */
|
3156
|
+
eUnknownCommandError = rb_define_class_under(mError, "UnknownCommand", eBaseError);
|
3157
|
+
/* Document-class: Couchbase::Error::UnknownHost
|
3158
|
+
* Unknown host */
|
3159
|
+
eUnknownHostError = rb_define_class_under(mError, "UnknownHost", eBaseError);
|
3160
|
+
/* Document-class: Couchbase::Error::ValueFormat
|
3161
|
+
* Failed to decode or encode value */
|
3162
|
+
eValueFormatError = rb_define_class_under(mError, "ValueFormat", eBaseError);
|
3163
|
+
/* Document-class: Couchbase::Error::Protocol
|
3164
|
+
* Protocol error */
|
3165
|
+
eProtocolError = rb_define_class_under(mError, "Protocol", eBaseError);
|
3166
|
+
/* Document-class: Couchbase::Error::Timeout
|
3167
|
+
* Timeout error */
|
3168
|
+
eTimeoutError = rb_define_class_under(mError, "Timeout", eBaseError);
|
3169
|
+
/* Document-class: Couchbase::Error::Connect
|
3170
|
+
* Connect error */
|
3171
|
+
eConnectError = rb_define_class_under(mError, "Connect", eBaseError);
|
3172
|
+
|
3173
|
+
/* Document-method: error
|
3174
|
+
* @return [Boolean] the error code from libcouchbase */
|
3175
|
+
rb_define_attr(eBaseError, "error", 1, 0);
|
3176
|
+
id_iv_error = rb_intern("@error");
|
3177
|
+
/* Document-method: key
|
3178
|
+
* @return [String] the key which generated error */
|
3179
|
+
rb_define_attr(eBaseError, "key", 1, 0);
|
3180
|
+
id_iv_key = rb_intern("@key");
|
3181
|
+
/* Document-method: cas
|
3182
|
+
* @return [Fixnum] the version of the key (+nil+ unless accessible) */
|
3183
|
+
rb_define_attr(eBaseError, "cas", 1, 0);
|
3184
|
+
id_iv_cas = rb_intern("@cas");
|
3185
|
+
/* Document-method: operation
|
3186
|
+
* @return [Symbol] the operation (+nil+ unless accessible) */
|
3187
|
+
rb_define_attr(eBaseError, "operation", 1, 0);
|
3188
|
+
id_iv_operation = rb_intern("@operation");
|
3189
|
+
|
3190
|
+
/* Document-class: Couchbase::Result
|
3191
|
+
* The object which yielded to asynchronous callbacks */
|
3192
|
+
cResult = rb_define_class_under(mCouchbase, "Result", rb_cObject);
|
3193
|
+
rb_define_method(cResult, "inspect", cb_result_inspect, 0);
|
3194
|
+
rb_define_method(cResult, "success?", cb_result_success_p, 0);
|
3195
|
+
/* Document-method: operation
|
3196
|
+
* @return [Symbol] */
|
3197
|
+
rb_define_attr(cResult, "operation", 1, 0);
|
3198
|
+
/* Document-method: error
|
3199
|
+
* @return [Couchbase::Error::Base] */
|
3200
|
+
rb_define_attr(cResult, "error", 1, 0);
|
3201
|
+
/* Document-method: key
|
3202
|
+
* @return [String] */
|
3203
|
+
rb_define_attr(cResult, "key", 1, 0);
|
3204
|
+
id_iv_key = rb_intern("@key");
|
3205
|
+
/* Document-method: value
|
3206
|
+
* @return [String] */
|
3207
|
+
rb_define_attr(cResult, "value", 1, 0);
|
3208
|
+
id_iv_value = rb_intern("@value");
|
3209
|
+
/* Document-method: cas
|
3210
|
+
* @return [Fixnum] */
|
3211
|
+
rb_define_attr(cResult, "cas", 1, 0);
|
3212
|
+
id_iv_cas = rb_intern("@cas");
|
3213
|
+
/* Document-method: flags
|
3214
|
+
* @return [Fixnum] */
|
3215
|
+
rb_define_attr(cResult, "flags", 1, 0);
|
3216
|
+
id_iv_flags = rb_intern("@flags");
|
3217
|
+
/* Document-method: node
|
3218
|
+
* @return [String] */
|
3219
|
+
rb_define_attr(cResult, "node", 1, 0);
|
3220
|
+
id_iv_node = rb_intern("@node");
|
3221
|
+
|
3222
|
+
/* Document-class: Couchbase::Bucket
|
3223
|
+
* This class in charge of all stuff connected to communication with
|
3224
|
+
* Couchbase. */
|
3225
|
+
cBucket = rb_define_class_under(mCouchbase, "Bucket", rb_cObject);
|
3226
|
+
object_space = rb_hash_new();
|
3227
|
+
/* @private Hack to avoid GC in some cases */
|
3228
|
+
rb_define_const(cBucket, "OBJECT_SPACE", object_space);
|
3229
|
+
|
3230
|
+
/* 0x03: Bitmask for flag bits responsible for format */
|
3231
|
+
rb_define_const(cBucket, "FMT_MASK", INT2FIX(FMT_MASK));
|
3232
|
+
/* 0x00: Document format. The (default) format supports most of ruby
|
3233
|
+
* types which could be mapped to JSON data (hashes, arrays, strings,
|
3234
|
+
* numbers). Future version will be able to run map/reduce queries on
|
3235
|
+
* the values in the document form (hashes). */
|
3236
|
+
rb_define_const(cBucket, "FMT_DOCUMENT", INT2FIX(FMT_DOCUMENT));
|
3237
|
+
/* 0x01: Marshal format. The format which supports transparent
|
3238
|
+
* serialization of ruby objects with standard <tt>Marshal.dump</tt> and
|
3239
|
+
* <tt>Marhal.load</tt> methods. */
|
3240
|
+
rb_define_const(cBucket, "FMT_MARSHAL", INT2FIX(FMT_MARSHAL));
|
3241
|
+
/* 0x02: Plain format. The format which force client don't apply any
|
3242
|
+
* conversions to the value, but it should be passed as String. It
|
3243
|
+
* could be useful for building custom algorithms or formats. For
|
3244
|
+
* example implement set:
|
3245
|
+
* http://dustin.github.com/2011/02/17/memcached-set.html */
|
3246
|
+
rb_define_const(cBucket, "FMT_PLAIN", INT2FIX(FMT_PLAIN));
|
3247
|
+
|
3248
|
+
rb_define_singleton_method(cBucket, "new", cb_bucket_new, -1);
|
3249
|
+
|
3250
|
+
rb_define_method(cBucket, "initialize", cb_bucket_init, -1);
|
3251
|
+
rb_define_method(cBucket, "inspect", cb_bucket_inspect, 0);
|
3252
|
+
|
3253
|
+
/* Document-method: seqno
|
3254
|
+
* The number of scheduled commands */
|
3255
|
+
/* rb_define_attr(cBucket, "seqno", 1, 0); */
|
3256
|
+
rb_define_method(cBucket, "seqno", cb_bucket_seqno, 0);
|
3257
|
+
|
3258
|
+
rb_define_method(cBucket, "add", cb_bucket_add, -1);
|
3259
|
+
rb_define_method(cBucket, "append", cb_bucket_append, -1);
|
3260
|
+
rb_define_method(cBucket, "prepend", cb_bucket_prepend, -1);
|
3261
|
+
rb_define_method(cBucket, "replace", cb_bucket_replace, -1);
|
3262
|
+
rb_define_method(cBucket, "set", cb_bucket_set, -1);
|
3263
|
+
rb_define_method(cBucket, "get", cb_bucket_get, -1);
|
3264
|
+
rb_define_method(cBucket, "run", cb_bucket_run, 0);
|
3265
|
+
rb_define_method(cBucket, "touch", cb_bucket_touch, -1);
|
3266
|
+
rb_define_method(cBucket, "delete", cb_bucket_delete, -1);
|
3267
|
+
rb_define_method(cBucket, "stats", cb_bucket_stats, -1);
|
3268
|
+
rb_define_method(cBucket, "flush", cb_bucket_flush, 0);
|
3269
|
+
rb_define_method(cBucket, "version", cb_bucket_version, 0);
|
3270
|
+
rb_define_method(cBucket, "incr", cb_bucket_incr, -1);
|
3271
|
+
rb_define_method(cBucket, "decr", cb_bucket_decr, -1);
|
3272
|
+
rb_define_method(cBucket, "disconnect", cb_bucket_disconnect, 0);
|
3273
|
+
rb_define_method(cBucket, "reconnect", cb_bucket_reconnect, -1);
|
3274
|
+
|
3275
|
+
rb_define_alias(cBucket, "decrement", "decr");
|
3276
|
+
rb_define_alias(cBucket, "increment", "incr");
|
3277
|
+
|
3278
|
+
rb_define_alias(cBucket, "[]", "get");
|
3279
|
+
rb_define_alias(cBucket, "[]=", "set");
|
3280
|
+
rb_define_method(cBucket, "[]=", cb_bucket_aset, -1);
|
3281
|
+
|
3282
|
+
rb_define_method(cBucket, "connected?", cb_bucket_connected_p, 0);
|
3283
|
+
rb_define_method(cBucket, "async?", cb_bucket_async_p, 0);
|
3284
|
+
|
3285
|
+
/* Document-method: quiet
|
3286
|
+
* Flag specifying behaviour for operations on missing keys
|
3287
|
+
*
|
3288
|
+
* If it is +true+, the operations will silently return +nil+ or +false+
|
3289
|
+
* instead of raising {Couchbase::Error::NotFound}.
|
3290
|
+
*
|
3291
|
+
* @example Hiding cache miss (considering "miss" key is not stored)
|
3292
|
+
* connection.quiet = true
|
3293
|
+
* connection.get("miss") #=> nil
|
3294
|
+
*
|
3295
|
+
* @example Raising errors on miss (considering "miss" key is not stored)
|
3296
|
+
* connection.quiet = false
|
3297
|
+
* connection.get("miss") #=> will raise Couchbase::Error::NotFound
|
3298
|
+
*
|
3299
|
+
* @return [Boolean] */
|
3300
|
+
/* rb_define_attr(cBucket, "quiet", 1, 1); */
|
3301
|
+
rb_define_method(cBucket, "quiet", cb_bucket_quiet_get, 0);
|
3302
|
+
rb_define_method(cBucket, "quiet=", cb_bucket_quiet_set, 1);
|
3303
|
+
rb_define_alias(cBucket, "quiet?", "quiet");
|
3304
|
+
|
3305
|
+
/* Document-method: default_flags
|
3306
|
+
* Default flags for new values.
|
3307
|
+
*
|
3308
|
+
* The library reserves last two lower bits to store the format of the
|
3309
|
+
* value. The can be masked via FMT_MASK constant.
|
3310
|
+
*
|
3311
|
+
* @example Selecting format bits
|
3312
|
+
* connection.default_flags & Couchbase::Bucket::FMT_MASK
|
3313
|
+
*
|
3314
|
+
* @example Set user defined bits
|
3315
|
+
* connection.default_flags |= 0x6660
|
3316
|
+
*
|
3317
|
+
* @note Amending format bit will also change #default_format value
|
3318
|
+
*
|
3319
|
+
* @return [Fixnum] the effective flags */
|
3320
|
+
/* rb_define_attr(cBucket, "default_flags", 1, 1); */
|
3321
|
+
rb_define_method(cBucket, "default_flags", cb_bucket_default_flags_get, 0);
|
3322
|
+
rb_define_method(cBucket, "default_flags=", cb_bucket_default_flags_set, 1);
|
3323
|
+
|
3324
|
+
/* Document-method: default_format
|
3325
|
+
* Default format for new values.
|
3326
|
+
*
|
3327
|
+
* It uses flags field to store the format. It accepts either the Symbol
|
3328
|
+
* (+:document+, +:marshal+, +:plain+) or Fixnum (use constants
|
3329
|
+
* FMT_DOCUMENT, FMT_MARSHAL, FMT_PLAIN) and silently ignores all
|
3330
|
+
* other value.
|
3331
|
+
*
|
3332
|
+
* Here is some notes regarding how to choose the format:
|
3333
|
+
*
|
3334
|
+
* * <tt>:document</tt> (default) format supports most of ruby types
|
3335
|
+
* which could be mapped to JSON data (hashes, arrays, strings,
|
3336
|
+
* numbers). Future version will be able to run map/reduce queries on
|
3337
|
+
* the values in the document form (hashes).
|
3338
|
+
*
|
3339
|
+
* * <tt>:plain</tt> format if you no need any conversions to be applied
|
3340
|
+
* to your data, but your data should be passed as String. It could be
|
3341
|
+
* useful for building custom algorithms or formats. For example
|
3342
|
+
* implement set: http://dustin.github.com/2011/02/17/memcached-set.html
|
3343
|
+
*
|
3344
|
+
* * <tt>:marshal</tt> format if you'd like to transparently serialize
|
3345
|
+
* your ruby object with standard <tt>Marshal.dump</tt> and
|
3346
|
+
* <tt>Marhal.load</tt> methods.
|
3347
|
+
*
|
3348
|
+
* @example Selecting plain format using symbol
|
3349
|
+
* connection.format = :document
|
3350
|
+
*
|
3351
|
+
* @example Selecting plain format using Fixnum constant
|
3352
|
+
* connection.format = Couchbase::Bucket::FMT_PLAIN
|
3353
|
+
*
|
3354
|
+
* @note Amending default_format will also change #default_flags value
|
3355
|
+
*
|
3356
|
+
* @return [Symbol] the effective format */
|
3357
|
+
/* rb_define_attr(cBucket, "default_format", 1, 1); */
|
3358
|
+
rb_define_method(cBucket, "default_format", cb_bucket_default_format_get, 0);
|
3359
|
+
rb_define_method(cBucket, "default_format=", cb_bucket_default_format_set, 1);
|
3360
|
+
|
3361
|
+
/* Document-method: timeout
|
3362
|
+
* @return [Fixnum] The timeout for the operations. The client will
|
3363
|
+
* raise {Couchbase::Error::Timeout} exception for all commands which
|
3364
|
+
* weren't completed in given timeslot. */
|
3365
|
+
/* rb_define_attr(cBucket, "timeout", 1, 1); */
|
3366
|
+
rb_define_method(cBucket, "timeout", cb_bucket_timeout_get, 0);
|
3367
|
+
rb_define_method(cBucket, "timeout=", cb_bucket_timeout_set, 1);
|
3368
|
+
|
3369
|
+
/* Document-method: on_error
|
3370
|
+
* Error callback for asynchronous mode.
|
3371
|
+
*
|
3372
|
+
* This callback is using to deliver exceptions in asynchronous mode.
|
3373
|
+
*
|
3374
|
+
* @yieldparam [Symbol] op The operation caused the error
|
3375
|
+
* @yieldparam [String] key The key which cause the error or +nil+
|
3376
|
+
* @yieldparam [Exception] exc The exception instance
|
3377
|
+
*
|
3378
|
+
* @example Using lambda syntax
|
3379
|
+
* connection = Couchbase.new(:async => true)
|
3380
|
+
* connection.on_error = lambda {|op, key, exc| ... }
|
3381
|
+
* connection.run do |conn|
|
3382
|
+
* conn.set("foo", "bar")
|
3383
|
+
* end
|
3384
|
+
*
|
3385
|
+
* @example Using block syntax
|
3386
|
+
* connection = Couchbase.new(:async => true)
|
3387
|
+
* connection.on_error {|op, key, exc| ... }
|
3388
|
+
* ...
|
3389
|
+
*
|
3390
|
+
* @return [Proc] the effective callback */
|
3391
|
+
/* rb_define_attr(cBucket, "on_error", 1, 1); */
|
3392
|
+
rb_define_method(cBucket, "on_error", cb_bucket_on_error_get, 0);
|
3393
|
+
rb_define_method(cBucket, "on_error=", cb_bucket_on_error_set, 1);
|
3394
|
+
|
3395
|
+
/* rb_define_attr(cBucket, "url", 1, 0); */
|
3396
|
+
rb_define_method(cBucket, "url", cb_bucket_url_get, 0);
|
3397
|
+
/* rb_define_attr(cBucket, "hostname", 1, 0); */
|
3398
|
+
rb_define_method(cBucket, "hostname", cb_bucket_hostname_get, 0);
|
3399
|
+
/* rb_define_attr(cBucket, "port", 1, 0); */
|
3400
|
+
rb_define_method(cBucket, "port", cb_bucket_port_get, 0);
|
3401
|
+
/* rb_define_attr(cBucket, "authority", 1, 0); */
|
3402
|
+
rb_define_method(cBucket, "authority", cb_bucket_authority_get, 0);
|
3403
|
+
/* rb_define_attr(cBucket, "bucket", 1, 0); */
|
3404
|
+
rb_define_method(cBucket, "bucket", cb_bucket_bucket_get, 0);
|
3405
|
+
rb_define_alias(cBucket, "name", "bucket");
|
3406
|
+
/* rb_define_attr(cBucket, "pool", 1, 0); */
|
3407
|
+
rb_define_method(cBucket, "pool", cb_bucket_pool_get, 0);
|
3408
|
+
/* rb_define_attr(cBucket, "username", 1, 0); */
|
3409
|
+
rb_define_method(cBucket, "username", cb_bucket_username_get, 0);
|
3410
|
+
/* rb_define_attr(cBucket, "password", 1, 0); */
|
3411
|
+
rb_define_method(cBucket, "password", cb_bucket_password_get, 0);
|
3412
|
+
|
3413
|
+
/* Define symbols */
|
3414
|
+
id_arity = rb_intern("arity");
|
3415
|
+
id_call = rb_intern("call");
|
3416
|
+
id_dump = rb_intern("dump");
|
3417
|
+
id_flatten_bang = rb_intern("flatten!");
|
3418
|
+
id_has_key_p = rb_intern("has_key?");
|
3419
|
+
id_host = rb_intern("host");
|
3420
|
+
id_load = rb_intern("load");
|
3421
|
+
id_match = rb_intern("match");
|
3422
|
+
id_parse = rb_intern("parse");
|
3423
|
+
id_password = rb_intern("password");
|
3424
|
+
id_path = rb_intern("path");
|
3425
|
+
id_port = rb_intern("port");
|
3426
|
+
id_scheme = rb_intern("scheme");
|
3427
|
+
id_to_s = rb_intern("to_s");
|
3428
|
+
id_user = rb_intern("user");
|
3429
|
+
|
3430
|
+
sym_add = ID2SYM(rb_intern("add"));
|
3431
|
+
sym_append = ID2SYM(rb_intern("append"));
|
3432
|
+
sym_bucket = ID2SYM(rb_intern("bucket"));
|
3433
|
+
sym_cas = ID2SYM(rb_intern("cas"));
|
3434
|
+
sym_create = ID2SYM(rb_intern("create"));
|
3435
|
+
sym_decrement = ID2SYM(rb_intern("decrement"));
|
3436
|
+
sym_default_flags = ID2SYM(rb_intern("default_flags"));
|
3437
|
+
sym_default_format = ID2SYM(rb_intern("default_format"));
|
3438
|
+
sym_default_ttl = ID2SYM(rb_intern("default_ttl"));
|
3439
|
+
sym_delete = ID2SYM(rb_intern("delete"));
|
3440
|
+
sym_document = ID2SYM(rb_intern("document"));
|
3441
|
+
sym_extended = ID2SYM(rb_intern("extended"));
|
3442
|
+
sym_flags = ID2SYM(rb_intern("flags"));
|
3443
|
+
sym_flush = ID2SYM(rb_intern("flush"));
|
3444
|
+
sym_format = ID2SYM(rb_intern("format"));
|
3445
|
+
sym_get = ID2SYM(rb_intern("get"));
|
3446
|
+
sym_hostname = ID2SYM(rb_intern("hostname"));
|
3447
|
+
sym_increment = ID2SYM(rb_intern("increment"));
|
3448
|
+
sym_initial = ID2SYM(rb_intern("initial"));
|
3449
|
+
sym_marshal = ID2SYM(rb_intern("marshal"));
|
3450
|
+
sym_password = ID2SYM(rb_intern("password"));
|
3451
|
+
sym_plain = ID2SYM(rb_intern("plain"));
|
3452
|
+
sym_pool = ID2SYM(rb_intern("pool"));
|
3453
|
+
sym_port = ID2SYM(rb_intern("port"));
|
3454
|
+
sym_prepend = ID2SYM(rb_intern("prepend"));
|
3455
|
+
sym_quiet = ID2SYM(rb_intern("quiet"));
|
3456
|
+
sym_replace = ID2SYM(rb_intern("replace"));
|
3457
|
+
sym_set = ID2SYM(rb_intern("set"));
|
3458
|
+
sym_stats = ID2SYM(rb_intern("stats"));
|
3459
|
+
sym_timeout = ID2SYM(rb_intern("timeout"));
|
3460
|
+
sym_touch = ID2SYM(rb_intern("touch"));
|
3461
|
+
sym_ttl = ID2SYM(rb_intern("ttl"));
|
3462
|
+
sym_username = ID2SYM(rb_intern("username"));
|
3463
|
+
sym_version = ID2SYM(rb_intern("version"));
|
3464
|
+
}
|