couchbase 1.1.5-x86-mingw32 → 1.2.0.beta-x86-mingw32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. data/.gitignore +2 -1
  2. data/.travis.yml +12 -1
  3. data/HISTORY.markdown +112 -1
  4. data/README.markdown +149 -6
  5. data/couchbase.gemspec +5 -1
  6. data/ext/couchbase_ext/.gitignore +4 -0
  7. data/ext/couchbase_ext/arguments.c +973 -0
  8. data/ext/couchbase_ext/arithmetic.c +322 -0
  9. data/ext/couchbase_ext/bucket.c +1092 -0
  10. data/ext/couchbase_ext/couchbase_ext.c +618 -3247
  11. data/ext/couchbase_ext/couchbase_ext.h +519 -0
  12. data/ext/couchbase_ext/delete.c +167 -0
  13. data/ext/couchbase_ext/extconf.rb +24 -5
  14. data/ext/couchbase_ext/get.c +301 -0
  15. data/ext/couchbase_ext/gethrtime.c +124 -0
  16. data/ext/couchbase_ext/http.c +402 -0
  17. data/ext/couchbase_ext/observe.c +174 -0
  18. data/ext/couchbase_ext/result.c +126 -0
  19. data/ext/couchbase_ext/stats.c +169 -0
  20. data/ext/couchbase_ext/store.c +522 -0
  21. data/ext/couchbase_ext/timer.c +192 -0
  22. data/ext/couchbase_ext/touch.c +190 -0
  23. data/ext/couchbase_ext/unlock.c +180 -0
  24. data/ext/couchbase_ext/utils.c +471 -0
  25. data/ext/couchbase_ext/version.c +147 -0
  26. data/lib/action_dispatch/middleware/session/couchbase_store.rb +38 -0
  27. data/lib/active_support/cache/couchbase_store.rb +356 -0
  28. data/lib/couchbase.rb +24 -3
  29. data/lib/couchbase/bucket.rb +372 -9
  30. data/lib/couchbase/result.rb +26 -0
  31. data/lib/couchbase/utils.rb +59 -0
  32. data/lib/couchbase/version.rb +1 -1
  33. data/lib/couchbase/view.rb +305 -0
  34. data/lib/couchbase/view_row.rb +230 -0
  35. data/lib/ext/multi_json_fix.rb +47 -0
  36. data/lib/rack/session/couchbase.rb +104 -0
  37. data/tasks/compile.rake +5 -14
  38. data/test/setup.rb +6 -2
  39. data/test/test_arithmetic.rb +32 -2
  40. data/test/test_async.rb +18 -4
  41. data/test/test_bucket.rb +11 -1
  42. data/test/test_cas.rb +13 -3
  43. data/test/test_couchbase_rails_cache_store.rb +294 -0
  44. data/test/test_delete.rb +60 -3
  45. data/test/test_format.rb +28 -17
  46. data/test/test_get.rb +91 -14
  47. data/test/test_store.rb +31 -1
  48. data/test/{test_flush.rb → test_timer.rb} +11 -18
  49. data/test/test_touch.rb +33 -5
  50. data/test/test_unlock.rb +120 -0
  51. data/test/test_utils.rb +26 -0
  52. metadata +102 -12
@@ -0,0 +1,167 @@
1
+ /* vim: ft=c et ts=8 sts=4 sw=4 cino=
2
+ *
3
+ * Copyright 2011, 2012 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "couchbase_ext.h"
19
+
20
+ void
21
+ delete_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_remove_resp_t *resp)
22
+ {
23
+ struct context_st *ctx = (struct context_st *)cookie;
24
+ struct bucket_st *bucket = ctx->bucket;
25
+ VALUE key, *rv = ctx->rv, exc = Qnil, res;
26
+
27
+ ctx->nqueries--;
28
+ key = STR_NEW((const char*)resp->v.v0.key, resp->v.v0.nkey);
29
+ strip_key_prefix(bucket, key);
30
+
31
+ if (error != LCB_KEY_ENOENT || !ctx->quiet) {
32
+ exc = cb_check_error(error, "failed to remove value", key);
33
+ if (exc != Qnil) {
34
+ rb_ivar_set(exc, id_iv_operation, sym_delete);
35
+ if (NIL_P(ctx->exception)) {
36
+ ctx->exception = cb_gc_protect(bucket, exc);
37
+ }
38
+ }
39
+ }
40
+ if (bucket->async) { /* asynchronous */
41
+ if (ctx->proc != Qnil) {
42
+ res = rb_class_new_instance(0, NULL, cResult);
43
+ rb_ivar_set(res, id_iv_error, exc);
44
+ rb_ivar_set(res, id_iv_operation, sym_delete);
45
+ rb_ivar_set(res, id_iv_key, key);
46
+ cb_proc_call(ctx->proc, 1, res);
47
+ }
48
+ } else { /* synchronous */
49
+ rb_hash_aset(*rv, key, (error == LCB_SUCCESS) ? Qtrue : Qfalse);
50
+ }
51
+ if (ctx->nqueries == 0) {
52
+ cb_gc_unprotect(bucket, ctx->proc);
53
+ }
54
+ (void)handle;
55
+ }
56
+
57
+ /*
58
+ * Delete the specified key
59
+ *
60
+ * @since 1.0.0
61
+ *
62
+ * @overload delete(key, options = {})
63
+ * @param key [String, Symbol] Key used to reference the value.
64
+ * @param options [Hash] Options for operation.
65
+ * @option options [true, false] :quiet (self.quiet) If set to +true+, the
66
+ * operation won't raise error for missing key, it will return +nil+.
67
+ * Otherwise it will raise error in synchronous mode. In asynchronous
68
+ * mode this option ignored.
69
+ * @option options [Fixnum] :cas The CAS value for an object. This value
70
+ * created on the server and is guaranteed to be unique for each value of
71
+ * a given key. This value is used to provide simple optimistic
72
+ * concurrency control when multiple clients or threads try to
73
+ * update/delete an item simultaneously.
74
+ *
75
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
76
+ * @raise [ArgumentError] when passing the block in synchronous mode
77
+ * @raise [Couchbase::Error::KeyExists] on CAS mismatch
78
+ * @raise [Couchbase::Error::NotFound] if key is missing in verbose mode
79
+ *
80
+ * @return [true, false, Hash<String, Boolean>] the result of the
81
+ * operation
82
+ *
83
+ * @example Delete the key in quiet mode (default)
84
+ * c.set("foo", "bar")
85
+ * c.delete("foo") #=> true
86
+ * c.delete("foo") #=> false
87
+ *
88
+ * @example Delete the key verbosely
89
+ * c.set("foo", "bar")
90
+ * c.delete("foo", :quiet => false) #=> true
91
+ * c.delete("foo", :quiet => true) #=> nil (default behaviour)
92
+ * c.delete("foo", :quiet => false) #=> will raise Couchbase::Error::NotFound
93
+ *
94
+ * @example Delete the key with version check
95
+ * ver = c.set("foo", "bar") #=> 5992859822302167040
96
+ * c.delete("foo", :cas => 123456) #=> will raise Couchbase::Error::KeyExists
97
+ * c.delete("foo", :cas => ver) #=> true
98
+ */
99
+ VALUE
100
+ cb_bucket_delete(int argc, VALUE *argv, VALUE self)
101
+ {
102
+ struct bucket_st *bucket = DATA_PTR(self);
103
+ struct context_st *ctx;
104
+ VALUE rv, exc;
105
+ VALUE args, proc;
106
+ lcb_error_t err;
107
+ struct params_st params;
108
+
109
+ if (bucket->handle == NULL) {
110
+ rb_raise(eConnectError, "closed connection");
111
+ }
112
+ rb_scan_args(argc, argv, "0*&", &args, &proc);
113
+ if (!bucket->async && proc != Qnil) {
114
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
115
+ }
116
+ rb_funcall(args, id_flatten_bang, 0);
117
+ memset(&params, 0, sizeof(struct params_st));
118
+ params.type = cmd_remove;
119
+ params.bucket = bucket;
120
+ cb_params_build(&params, RARRAY_LEN(args), args);
121
+
122
+ ctx = xcalloc(1, sizeof(struct context_st));
123
+ if (ctx == NULL) {
124
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for context");
125
+ }
126
+ ctx->quiet = params.cmd.remove.quiet;
127
+ ctx->proc = cb_gc_protect(bucket, proc);
128
+ rv = rb_hash_new();
129
+ ctx->rv = &rv;
130
+ ctx->bucket = bucket;
131
+ ctx->exception = Qnil;
132
+ ctx->nqueries = params.cmd.remove.num;
133
+ err = lcb_remove(bucket->handle, (const void *)ctx,
134
+ params.cmd.remove.num, params.cmd.remove.ptr);
135
+ cb_params_destroy(&params);
136
+ exc = cb_check_error(err, "failed to schedule delete request", Qnil);
137
+ if (exc != Qnil) {
138
+ xfree(ctx);
139
+ rb_exc_raise(exc);
140
+ }
141
+ bucket->nbytes += params.npayload;
142
+ if (bucket->async) {
143
+ maybe_do_loop(bucket);
144
+ return Qnil;
145
+ } else {
146
+ if (ctx->nqueries > 0) {
147
+ /* we have some operations pending */
148
+ lcb_wait(bucket->handle);
149
+ }
150
+ exc = ctx->exception;
151
+ xfree(ctx);
152
+ if (exc != Qnil) {
153
+ rb_exc_raise(cb_gc_unprotect(bucket, exc));
154
+ }
155
+ if (bucket->exception != Qnil) {
156
+ rb_exc_raise(bucket->exception);
157
+ }
158
+ if (params.cmd.remove.num > 1) {
159
+ return rv; /* return as a hash {key => true, ...} */
160
+ } else {
161
+ VALUE vv = Qnil;
162
+ rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
163
+ return vv;
164
+ }
165
+ return rv;
166
+ }
167
+ }
@@ -16,7 +16,22 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- ENV['RC_ARCHS'] = '' if RUBY_PLATFORM =~ /darwin/
19
+ require 'rbconfig'
20
+ # This hack is more robust, because in bundler environment bundler touches
21
+ # all constants from rbconfig.rb before loading any scripts. This is why
22
+ # RC_ARCHS doesn't work under bundler on MacOS.
23
+ if RUBY_PLATFORM =~ /darwin/
24
+ [RbConfig::CONFIG, RbConfig::MAKEFILE_CONFIG].each do |cfg|
25
+ cfg["CFLAGS"].gsub!(RbConfig::ARCHFLAGS, '')
26
+ cfg["LDFLAGS"].gsub!(RbConfig::ARCHFLAGS, '')
27
+ cfg["LDSHARED"].gsub!(RbConfig::ARCHFLAGS, '')
28
+ cfg["LIBRUBY_LDSHARED"].gsub!(RbConfig::ARCHFLAGS, '')
29
+ cfg["configure_args"].gsub!(RbConfig::ARCHFLAGS, '')
30
+ end
31
+ end
32
+
33
+ # Unset RUBYOPT to avoid interferences
34
+ ENV['RUBYOPT'] = nil
20
35
 
21
36
  require 'mkmf'
22
37
 
@@ -105,9 +120,13 @@ if try_compile(<<-SRC)
105
120
  end
106
121
 
107
122
 
108
- if RbConfig::CONFIG['target_os'] =~ /mingw32/
109
- have_library("vbucket", "vbucket_config_create", "libvbucket/vbucket.h") or abort "You should install libvbucket >= 1.8.0.2"
110
- end
111
- have_library("couchbase", "libcouchbase_server_versions", "libcouchbase/couchbase.h") or abort "You should install libcouchbase >= 1.0.2"
123
+ have_library("couchbase", "lcb_get", "libcouchbase/couchbase.h") or abort "You should install libcouchbase >= 2.0.0. See http://www.couchbase.com/develop/ for more details"
124
+ have_header("mach/mach_time.h")
125
+ have_header("stdint.h") or abort "Failed to locate stdint.h"
126
+ have_header("sys/time.h")
127
+ have_func("clock_gettime")
128
+ have_func("gettimeofday")
129
+ have_func("QueryPerformanceCounter")
130
+ define("_GNU_SOURCE")
112
131
  create_header("couchbase_config.h")
113
132
  create_makefile("couchbase_ext")
@@ -0,0 +1,301 @@
1
+ /* vim: ft=c et ts=8 sts=4 sw=4 cino=
2
+ *
3
+ * Copyright 2011, 2012 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "couchbase_ext.h"
19
+
20
+ void
21
+ get_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_get_resp_t *resp)
22
+ {
23
+ struct context_st *ctx = (struct context_st *)cookie;
24
+ struct bucket_st *bucket = ctx->bucket;
25
+ VALUE key, val, flags, cas, *rv = ctx->rv, exc = Qnil, res;
26
+
27
+ ctx->nqueries--;
28
+ key = STR_NEW((const char*)resp->v.v0.key, resp->v.v0.nkey);
29
+ strip_key_prefix(bucket, key);
30
+
31
+ if (error != LCB_KEY_ENOENT || !ctx->quiet) {
32
+ exc = cb_check_error(error, "failed to get value", key);
33
+ if (exc != Qnil) {
34
+ rb_ivar_set(exc, id_iv_operation, sym_get);
35
+ if (NIL_P(ctx->exception)) {
36
+ ctx->exception = cb_gc_protect(bucket, exc);
37
+ }
38
+ }
39
+ }
40
+
41
+ flags = ULONG2NUM(resp->v.v0.flags);
42
+ cas = ULL2NUM(resp->v.v0.cas);
43
+ val = Qnil;
44
+ if (resp->v.v0.nbytes != 0) {
45
+ val = decode_value(STR_NEW((const char*)resp->v.v0.bytes, resp->v.v0.nbytes),
46
+ resp->v.v0.flags, ctx->force_format);
47
+ if (val == Qundef) {
48
+ if (ctx->exception != Qnil) {
49
+ cb_gc_unprotect(bucket, ctx->exception);
50
+ }
51
+ ctx->exception = rb_exc_new2(eValueFormatError, "unable to convert value");
52
+ rb_ivar_set(ctx->exception, id_iv_operation, sym_get);
53
+ rb_ivar_set(ctx->exception, id_iv_key, key);
54
+ cb_gc_protect(bucket, ctx->exception);
55
+ }
56
+ } else if (flags_get_format(resp->v.v0.flags) == sym_plain) {
57
+ val = STR_NEW_CSTR("");
58
+ }
59
+ if (bucket->async) { /* asynchronous */
60
+ if (ctx->proc != Qnil) {
61
+ res = rb_class_new_instance(0, NULL, cResult);
62
+ rb_ivar_set(res, id_iv_error, exc);
63
+ rb_ivar_set(res, id_iv_operation, sym_get);
64
+ rb_ivar_set(res, id_iv_key, key);
65
+ rb_ivar_set(res, id_iv_value, val);
66
+ rb_ivar_set(res, id_iv_flags, flags);
67
+ rb_ivar_set(res, id_iv_cas, cas);
68
+ cb_proc_call(ctx->proc, 1, res);
69
+ }
70
+ } else { /* synchronous */
71
+ if (NIL_P(exc) && error != LCB_KEY_ENOENT) {
72
+ if (ctx->extended) {
73
+ rb_hash_aset(*rv, key, rb_ary_new3(3, val, flags, cas));
74
+ } else {
75
+ rb_hash_aset(*rv, key, val);
76
+ }
77
+ }
78
+ }
79
+
80
+ if (ctx->nqueries == 0) {
81
+ cb_gc_unprotect(bucket, ctx->proc);
82
+ }
83
+ (void)handle;
84
+ }
85
+
86
+ /*
87
+ * Obtain an object stored in Couchbase by given key.
88
+ *
89
+ * @since 1.0.0
90
+ *
91
+ * @see http://couchbase.com/docs/couchbase-manual-2.0/couchbase-architecture-apis-memcached-protocol-additions.html#couchbase-architecture-apis-memcached-protocol-additions-getl
92
+ *
93
+ * @overload get(*keys, options = {})
94
+ * @param keys [String, Symbol, Array] One or several keys to fetch
95
+ * @param options [Hash] Options for operation.
96
+ * @option options [true, false] :extended (false) If set to +true+, the
97
+ * operation will return tuple +[value, flags, cas]+, otherwise (by
98
+ * default) it returns just value.
99
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
100
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
101
+ * absolute times (from the epoch).
102
+ * @option options [true, false] :quiet (self.quiet) If set to +true+, the
103
+ * operation won't raise error for missing key, it will return +nil+.
104
+ * Otherwise it will raise error in synchronous mode. In asynchronous
105
+ * mode this option ignored.
106
+ * @option options [Symbol] :format (nil) Explicitly choose the decoder
107
+ * for this key (+:plain+, +:document+, +:marshal+). See
108
+ * {Bucket#default_format}.
109
+ * @option options [Fixnum, Boolean] :lock Lock the keys for time span.
110
+ * If this parameter is +true+ the key(s) will be locked for default
111
+ * timeout. Also you can use number to setup your own timeout in
112
+ * seconds. If it will be lower that zero or exceed the maximum, the
113
+ * server will use default value. You can determine actual default and
114
+ * maximum values calling {Bucket#stats} without arguments and
115
+ * inspecting keys "ep_getl_default_timeout" and "ep_getl_max_timeout"
116
+ * correspondingly. See overloaded hash syntax to specify custom timeout
117
+ * per each key.
118
+ * @option options [true, false] :assemble_hash (false) Assemble Hash for
119
+ * results. Hash assembled automatically if +:extended+ option is true
120
+ * or in case of "get and touch" multimple keys.
121
+ * @option options [true, false] :replica (false) Read key from replica
122
+ * node. Options +:ttl+ and +:lock+ are not compatible with +:replica+.
123
+ *
124
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
125
+ * (valid attributes: +error+, +operation+, +key+, +value+, +flags+,
126
+ * +cas+).
127
+ *
128
+ * @return [Object, Array, Hash] the value(s) (or tuples in extended mode)
129
+ * assiciated with the key.
130
+ *
131
+ * @raise [Couchbase::Error::NotFound] if the key is missing in the
132
+ * bucket.
133
+ *
134
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
135
+ *
136
+ * @raise [ArgumentError] when passing the block in synchronous mode
137
+ *
138
+ * @example Get single value in quite mode (the default)
139
+ * c.get("foo") #=> the associated value or nil
140
+ *
141
+ * @example Use alternative hash-like syntax
142
+ * c["foo"] #=> the associated value or nil
143
+ *
144
+ * @example Get single value in verbose mode
145
+ * c.get("missing-foo", :quiet => false) #=> raises Couchbase::NotFound
146
+ * c.get("missing-foo", :quiet => true) #=> returns nil
147
+ *
148
+ * @example Get and touch single value. The key won't be accessible after 10 seconds
149
+ * c.get("foo", :ttl => 10)
150
+ *
151
+ * @example Extended get
152
+ * val, flags, cas = c.get("foo", :extended => true)
153
+ *
154
+ * @example Get multiple keys
155
+ * c.get("foo", "bar", "baz") #=> [val1, val2, val3]
156
+ *
157
+ * @example Get multiple keys with assembing result into the Hash
158
+ * c.get("foo", "bar", "baz", :assemble_hash => true)
159
+ * #=> {"foo" => val1, "bar" => val2, "baz" => val3}
160
+ *
161
+ * @example Extended get multiple keys
162
+ * c.get("foo", "bar", :extended => true)
163
+ * #=> {"foo" => [val1, flags1, cas1], "bar" => [val2, flags2, cas2]}
164
+ *
165
+ * @example Asynchronous get
166
+ * c.run do
167
+ * c.get("foo", "bar", "baz") do |res|
168
+ * ret.operation #=> :get
169
+ * ret.success? #=> true
170
+ * ret.key #=> "foo", "bar" or "baz" in separate calls
171
+ * ret.value
172
+ * ret.flags
173
+ * ret.cas
174
+ * end
175
+ * end
176
+ *
177
+ * @example Get and lock key using default timeout
178
+ * c.get("foo", :lock => true)
179
+ *
180
+ * @example Determine lock timeout parameters
181
+ * c.stats.values_at("ep_getl_default_timeout", "ep_getl_max_timeout")
182
+ * #=> [{"127.0.0.1:11210"=>"15"}, {"127.0.0.1:11210"=>"30"}]
183
+ *
184
+ * @example Get and lock key using custom timeout
185
+ * c.get("foo", :lock => 3)
186
+ *
187
+ * @example Get and lock multiple keys using custom timeout
188
+ * c.get("foo", "bar", :lock => 3)
189
+ *
190
+ * @overload get(keys, options = {})
191
+ * When the method receive hash map, it will behave like it receive list
192
+ * of keys (+keys.keys+), but also touch each key setting expiry time to
193
+ * the corresponding value. But unlike usual get this command always
194
+ * return hash map +{key => value}+ or +{key => [value, flags, cas]}+.
195
+ *
196
+ * @param keys [Hash] Map key-ttl
197
+ * @param options [Hash] Options for operation. (see options definition
198
+ * above)
199
+ *
200
+ * @return [Hash] the values (or tuples in extended mode) assiciated with
201
+ * the keys.
202
+ *
203
+ * @example Get and touch multiple keys
204
+ * c.get("foo" => 10, "bar" => 20) #=> {"foo" => val1, "bar" => val2}
205
+ *
206
+ * @example Extended get and touch multiple keys
207
+ * c.get({"foo" => 10, "bar" => 20}, :extended => true)
208
+ * #=> {"foo" => [val1, flags1, cas1], "bar" => [val2, flags2, cas2]}
209
+ *
210
+ * @example Get and lock multiple keys for chosen period in seconds
211
+ * c.get("foo" => 10, "bar" => 20, :lock => true)
212
+ * #=> {"foo" => val1, "bar" => val2}
213
+ */
214
+ VALUE
215
+ cb_bucket_get(int argc, VALUE *argv, VALUE self)
216
+ {
217
+ struct bucket_st *bucket = DATA_PTR(self);
218
+ struct context_st *ctx;
219
+ VALUE args, rv, proc, exc;
220
+ size_t ii;
221
+ lcb_error_t err = LCB_SUCCESS;
222
+ struct params_st params;
223
+
224
+ if (bucket->handle == NULL) {
225
+ rb_raise(eConnectError, "closed connection");
226
+ }
227
+ rb_scan_args(argc, argv, "0*&", &args, &proc);
228
+ if (!bucket->async && proc != Qnil) {
229
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
230
+ }
231
+ memset(&params, 0, sizeof(struct params_st));
232
+ params.type = cmd_get;
233
+ params.bucket = bucket;
234
+ params.cmd.get.keys_ary = cb_gc_protect(bucket, rb_ary_new());
235
+ cb_params_build(&params, RARRAY_LEN(args), args);
236
+ ctx = xcalloc(1, sizeof(struct context_st));
237
+ if (ctx == NULL) {
238
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for context");
239
+ }
240
+ ctx->extended = params.cmd.get.extended;
241
+ ctx->quiet = params.cmd.get.quiet;
242
+ ctx->force_format = params.cmd.get.forced_format;
243
+ ctx->proc = cb_gc_protect(bucket, proc);
244
+ ctx->bucket = bucket;
245
+ rv = rb_hash_new();
246
+ ctx->rv = &rv;
247
+ ctx->exception = Qnil;
248
+ ctx->nqueries = params.cmd.get.num;
249
+ if (params.cmd.get.replica) {
250
+ err = lcb_get_replica(bucket->handle, (const void *)ctx,
251
+ params.cmd.get.num, params.cmd.get.ptr_gr);
252
+ } else {
253
+ err = lcb_get(bucket->handle, (const void *)ctx,
254
+ params.cmd.get.num, params.cmd.get.ptr);
255
+ }
256
+ cb_params_destroy(&params);
257
+ cb_gc_unprotect(bucket, params.cmd.get.keys_ary);
258
+ exc = cb_check_error(err, "failed to schedule get request", Qnil);
259
+ if (exc != Qnil) {
260
+ xfree(ctx);
261
+ rb_exc_raise(exc);
262
+ }
263
+ bucket->nbytes += params.npayload;
264
+ if (bucket->async) {
265
+ maybe_do_loop(bucket);
266
+ return Qnil;
267
+ } else {
268
+ if (ctx->nqueries > 0) {
269
+ /* we have some operations pending */
270
+ lcb_wait(bucket->handle);
271
+ }
272
+ exc = ctx->exception;
273
+ xfree(ctx);
274
+ if (exc != Qnil) {
275
+ cb_gc_unprotect(bucket, exc);
276
+ rb_exc_raise(exc);
277
+ }
278
+ if (bucket->exception != Qnil) {
279
+ rb_exc_raise(bucket->exception);
280
+ }
281
+ if (params.cmd.get.gat || params.cmd.get.assemble_hash ||
282
+ (params.cmd.get.extended && (params.cmd.get.num > 1 || params.cmd.get.array))) {
283
+ return rv; /* return as a hash {key => [value, flags, cas], ...} */
284
+ }
285
+ if (params.cmd.get.num > 1 || params.cmd.get.array) {
286
+ VALUE *keys_ptr, ret;
287
+ ret = rb_ary_new();
288
+ keys_ptr = RARRAY_PTR(params.cmd.get.keys_ary);
289
+ for (ii = 0; ii < params.cmd.get.num; ++ii) {
290
+ rb_ary_push(ret, rb_hash_aref(rv, keys_ptr[ii]));
291
+ }
292
+ return ret; /* return as an array [value1, value2, ...] */
293
+ } else {
294
+ VALUE vv = Qnil;
295
+ rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
296
+ return vv;
297
+ }
298
+ }
299
+ }
300
+
301
+