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,192 @@
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
+ cb_timer_free(void *ptr)
22
+ {
23
+ xfree(ptr);
24
+ }
25
+
26
+ void
27
+ cb_timer_mark(void *ptr)
28
+ {
29
+ struct timer_st *timer = ptr;
30
+ if (timer) {
31
+ rb_gc_mark(timer->callback);
32
+ }
33
+ }
34
+
35
+ VALUE
36
+ cb_timer_alloc(VALUE klass)
37
+ {
38
+ VALUE obj;
39
+ struct timer_st *timer;
40
+
41
+ /* allocate new bucket struct and set it to zero */
42
+ obj = Data_Make_Struct(klass, struct timer_st, cb_timer_mark,
43
+ cb_timer_free, timer);
44
+ return obj;
45
+ }
46
+
47
+ /*
48
+ * Returns a string containing a human-readable representation of the
49
+ * Timer.
50
+ *
51
+ * @since 1.2.0.dp6
52
+ *
53
+ * @return [String]
54
+ */
55
+ VALUE
56
+ cb_timer_inspect(VALUE self)
57
+ {
58
+ VALUE str;
59
+ struct timer_st *tm = DATA_PTR(self);
60
+ char buf[200];
61
+
62
+ str = rb_str_buf_new2("#<");
63
+ rb_str_buf_cat2(str, rb_obj_classname(self));
64
+ snprintf(buf, 20, ":%p", (void *)self);
65
+ rb_str_buf_cat2(str, buf);
66
+ snprintf(buf, 100, " timeout:%u periodic:%s>",
67
+ tm->usec, tm->periodic ? "true" : "false");
68
+ rb_str_buf_cat2(str, buf);
69
+
70
+ return str;
71
+ }
72
+
73
+ /*
74
+ * Cancel the timer.
75
+ *
76
+ * @since 1.2.0.dp6
77
+ *
78
+ * This operation makes sense for periodic timers or if one need to cancel
79
+ * regular timer before it will be triggered.
80
+ *
81
+ * @example Cancel periodic timer
82
+ * n = 1
83
+ * c.run do
84
+ * tm = c.create_periodic_timer(500000) do
85
+ * c.incr("foo") do
86
+ * if n == 5
87
+ * tm.cancel
88
+ * else
89
+ * n += 1
90
+ * end
91
+ * end
92
+ * end
93
+ * end
94
+ *
95
+ * @return [String]
96
+ */
97
+ VALUE
98
+ cb_timer_cancel(VALUE self)
99
+ {
100
+ struct timer_st *tm = DATA_PTR(self);
101
+ lcb_timer_destroy(tm->bucket->handle, tm->timer);
102
+ return self;
103
+ }
104
+
105
+ static VALUE
106
+ trigger_timer(VALUE timer)
107
+ {
108
+ struct timer_st *tm = DATA_PTR(timer);
109
+ return cb_proc_call(tm->callback, 1, timer);
110
+ }
111
+
112
+ static void
113
+ timer_callback(lcb_timer_t timer, lcb_t instance,
114
+ const void *cookie)
115
+ {
116
+ struct timer_st *tm = (struct timer_st *)cookie;
117
+ int error = 0;
118
+
119
+ rb_protect(trigger_timer, tm->self, &error);
120
+ if (error) {
121
+ lcb_timer_destroy(instance, timer);
122
+ }
123
+ (void)cookie;
124
+ }
125
+
126
+ /*
127
+ * Initialize new Timer
128
+ *
129
+ * @since 1.2.0
130
+ *
131
+ * The timers could used to trigger reccuring events or implement timeouts.
132
+ * The library will call given block after time interval pass.
133
+ *
134
+ * @param bucket [Bucket] the connection object
135
+ * @param interval [Fixnum] the interval in microseconds
136
+ * @param options [Hash]
137
+ * @option options [Boolean] :periodic (false) set it to +true+ if the timer
138
+ * should be triggered until it will be canceled.
139
+ *
140
+ * @yieldparam [Timer] timer the current timer
141
+ *
142
+ * @example Create regular timer for 0.5 second
143
+ * c.run do
144
+ * Couchbase::Timer.new(c, 500000) do
145
+ * puts "ding-dong"
146
+ * end
147
+ * end
148
+ *
149
+ * @example Create periodic timer
150
+ * n = 10
151
+ * c.run do
152
+ * Couchbase::Timer.new(c, 500000, :periodic => true) do |tm|
153
+ * puts "#{n}"
154
+ * n -= 1
155
+ * tm.cancel if n.zero?
156
+ * end
157
+ * end
158
+ *
159
+ *
160
+ * @return [Couchbase::Timer]
161
+ */
162
+ VALUE
163
+ cb_timer_init(int argc, VALUE *argv, VALUE self)
164
+ {
165
+ struct timer_st *tm = DATA_PTR(self);
166
+ VALUE bucket, opts, timeout, exc, cb;
167
+ lcb_error_t err;
168
+
169
+ rb_need_block();
170
+ rb_scan_args(argc, argv, "21&", &bucket, &timeout, &opts, &cb);
171
+
172
+ if (CLASS_OF(bucket) != cBucket) {
173
+ rb_raise(rb_eTypeError, "wrong argument type (expected Couchbase::Bucket)");
174
+ }
175
+ tm->self = self;
176
+ tm->callback = cb;
177
+ tm->usec = NUM2ULONG(timeout);
178
+ tm->bucket = DATA_PTR(bucket);
179
+ if (opts != Qnil) {
180
+ Check_Type(opts, T_HASH);
181
+ tm->periodic = RTEST(rb_hash_aref(opts, sym_periodic));
182
+ }
183
+ tm->timer = lcb_timer_create(tm->bucket->handle, tm, tm->usec,
184
+ tm->periodic, timer_callback, &err);
185
+ exc = cb_check_error(err, "failed to attach the timer", Qnil);
186
+ if (exc != Qnil) {
187
+ rb_exc_raise(exc);
188
+ }
189
+
190
+ return self;
191
+ }
192
+
@@ -0,0 +1,190 @@
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
+ touch_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_touch_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 touch value", key);
33
+ if (exc != Qnil) {
34
+ rb_ivar_set(exc, id_iv_operation, sym_touch);
35
+ if (NIL_P(ctx->exception)) {
36
+ ctx->exception = cb_gc_protect(bucket, exc);
37
+ }
38
+ }
39
+ }
40
+
41
+ if (bucket->async) { /* asynchronous */
42
+ if (ctx->proc != Qnil) {
43
+ res = rb_class_new_instance(0, NULL, cResult);
44
+ rb_ivar_set(res, id_iv_error, exc);
45
+ rb_ivar_set(res, id_iv_operation, sym_touch);
46
+ rb_ivar_set(res, id_iv_key, key);
47
+ cb_proc_call(ctx->proc, 1, res);
48
+ }
49
+ } else { /* synchronous */
50
+ rb_hash_aset(*rv, key, (error == LCB_SUCCESS) ? Qtrue : Qfalse);
51
+ }
52
+ if (ctx->nqueries == 0) {
53
+ cb_gc_unprotect(bucket, ctx->proc);
54
+ }
55
+ (void)handle;
56
+ }
57
+
58
+ /*
59
+ * Update the expiry time of an item
60
+ *
61
+ * @since 1.0.0
62
+ *
63
+ * The +touch+ method allow you to update the expiration time on a given
64
+ * key. This can be useful for situations where you want to prevent an item
65
+ * from expiring without resetting the associated value. For example, for a
66
+ * session database you might want to keep the session alive in the database
67
+ * each time the user accesses a web page without explicitly updating the
68
+ * session value, keeping the user's session active and available.
69
+ *
70
+ * @overload touch(key, options = {})
71
+ * @param key [String, Symbol] Key used to reference the value.
72
+ * @param options [Hash] Options for operation.
73
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
74
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
75
+ * absolute times (from the epoch).
76
+ * @option options [true, false] :quiet (self.quiet) If set to +true+, the
77
+ * operation won't raise error for missing key, it will return +nil+.
78
+ *
79
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
80
+ * (valid attributes: +error+, +operation+, +key+).
81
+ *
82
+ * @return [true, false] +true+ if the operation was successful and +false+
83
+ * otherwise.
84
+ *
85
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
86
+ *
87
+ * @raise [ArgumentError] when passing the block in synchronous mode
88
+ *
89
+ * @example Touch value using +default_ttl+
90
+ * c.touch("foo")
91
+ *
92
+ * @example Touch value using custom TTL (10 seconds)
93
+ * c.touch("foo", :ttl => 10)
94
+ *
95
+ * @overload touch(keys)
96
+ * @param keys [Hash] The Hash where keys represent the keys in the
97
+ * database, values -- the expiry times for corresponding key. See
98
+ * description of +:ttl+ argument above for more information about TTL
99
+ * values.
100
+ *
101
+ * @yieldparam ret [Result] the result of operation for each key in
102
+ * asynchronous mode (valid attributes: +error+, +operation+, +key+).
103
+ *
104
+ * @return [Hash] Mapping keys to result of touch operation (+true+ if the
105
+ * operation was successful and +false+ otherwise)
106
+ *
107
+ * @example Touch several values
108
+ * c.touch("foo" => 10, :bar => 20) #=> {"foo" => true, "bar" => true}
109
+ *
110
+ * @example Touch several values in async mode
111
+ * c.run do
112
+ * c.touch("foo" => 10, :bar => 20) do |ret|
113
+ * ret.operation #=> :touch
114
+ * ret.success? #=> true
115
+ * ret.key #=> "foo" and "bar" in separate calls
116
+ * end
117
+ * end
118
+ *
119
+ * @example Touch single value
120
+ * c.touch("foo" => 10) #=> true
121
+ *
122
+ */
123
+ VALUE
124
+ cb_bucket_touch(int argc, VALUE *argv, VALUE self)
125
+ {
126
+ struct bucket_st *bucket = DATA_PTR(self);
127
+ struct context_st *ctx;
128
+ VALUE args, rv, proc, exc;
129
+ lcb_error_t err;
130
+ struct params_st params;
131
+
132
+ if (bucket->handle == NULL) {
133
+ rb_raise(eConnectError, "closed connection");
134
+ }
135
+ rb_scan_args(argc, argv, "0*&", &args, &proc);
136
+ if (!bucket->async && proc != Qnil) {
137
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
138
+ }
139
+ rb_funcall(args, id_flatten_bang, 0);
140
+ memset(&params, 0, sizeof(struct params_st));
141
+ params.type = cmd_touch;
142
+ params.bucket = bucket;
143
+ cb_params_build(&params, RARRAY_LEN(args), args);
144
+ ctx = xcalloc(1, sizeof(struct context_st));
145
+ if (ctx == NULL) {
146
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for context");
147
+ }
148
+ ctx->proc = cb_gc_protect(bucket, proc);
149
+ ctx->bucket = bucket;
150
+ rv = rb_hash_new();
151
+ ctx->rv = &rv;
152
+ ctx->exception = Qnil;
153
+ ctx->quiet = params.cmd.touch.quiet;
154
+ ctx->nqueries = params.cmd.touch.num;
155
+ err = lcb_touch(bucket->handle, (const void *)ctx,
156
+ params.cmd.touch.num, params.cmd.touch.ptr);
157
+ cb_params_destroy(&params);
158
+ exc = cb_check_error(err, "failed to schedule touch request", Qnil);
159
+ if (exc != Qnil) {
160
+ xfree(ctx);
161
+ rb_exc_raise(exc);
162
+ }
163
+ bucket->nbytes += params.npayload;
164
+ if (bucket->async) {
165
+ maybe_do_loop(bucket);
166
+ return Qnil;
167
+ } else {
168
+ if (ctx->nqueries > 0) {
169
+ /* we have some operations pending */
170
+ lcb_wait(bucket->handle);
171
+ }
172
+ exc = ctx->exception;
173
+ xfree(ctx);
174
+ if (exc != Qnil) {
175
+ rb_exc_raise(cb_gc_unprotect(bucket, exc));
176
+ }
177
+ if (bucket->exception != Qnil) {
178
+ rb_exc_raise(bucket->exception);
179
+ }
180
+ if (params.cmd.touch.num > 1) {
181
+ return rv; /* return as a hash {key => true, ...} */
182
+ } else {
183
+ VALUE vv = Qnil;
184
+ rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
185
+ return vv;
186
+ }
187
+ }
188
+ }
189
+
190
+
@@ -0,0 +1,180 @@
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
+ unlock_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_unlock_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 unlock value", key);
33
+ if (exc != Qnil) {
34
+ rb_ivar_set(exc, id_iv_operation, sym_unlock);
35
+ if (NIL_P(ctx->exception)) {
36
+ ctx->exception = cb_gc_protect(bucket, exc);
37
+ }
38
+ }
39
+ }
40
+
41
+ if (bucket->async) { /* asynchronous */
42
+ if (ctx->proc != Qnil) {
43
+ res = rb_class_new_instance(0, NULL, cResult);
44
+ rb_ivar_set(res, id_iv_error, exc);
45
+ rb_ivar_set(res, id_iv_operation, sym_unlock);
46
+ rb_ivar_set(res, id_iv_key, key);
47
+ cb_proc_call(ctx->proc, 1, res);
48
+ }
49
+ } else { /* synchronous */
50
+ rb_hash_aset(*rv, key, (error == LCB_SUCCESS) ? Qtrue : Qfalse);
51
+ }
52
+ if (ctx->nqueries == 0) {
53
+ cb_gc_unprotect(bucket, ctx->proc);
54
+ }
55
+ (void)handle;
56
+ }
57
+
58
+ /*
59
+ * Unlock key
60
+ *
61
+ * @since 1.2.0
62
+ *
63
+ * The +unlock+ method allow you to unlock key once locked by {Bucket#get}
64
+ * with +:lock+ option.
65
+ *
66
+ * @overload unlock(key, options = {})
67
+ * @param key [String, Symbol] Key used to reference the value.
68
+ * @param options [Hash] Options for operation.
69
+ * @option options [Fixnum] :cas The CAS value must match the current one
70
+ * from the storage.
71
+ * @option options [true, false] :quiet (self.quiet) If set to +true+, the
72
+ * operation won't raise error for missing key, it will return +nil+.
73
+ *
74
+ * @return [true, false] +true+ if the operation was successful and +false+
75
+ * otherwise.
76
+ *
77
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
78
+ *
79
+ * @raise [ArgumentError] when passing the block in synchronous mode
80
+ *
81
+ * @raise [Couchbase::Error::NotFound] if key(s) not found in the storage
82
+ *
83
+ * @raise [Couchbase::Error::TemporaryFail] if either the key wasn't
84
+ * locked or given CAS value doesn't match to actual in the storage
85
+ *
86
+ * @example Unlock the single key
87
+ * val, _, cas = c.get("foo", :lock => true, :extended => true)
88
+ * c.unlock("foo", :cas => cas)
89
+ *
90
+ * @overload unlock(keys)
91
+ * @param keys [Hash] The Hash where keys represent the keys in the
92
+ * database, values -- the CAS for corresponding key.
93
+ *
94
+ * @yieldparam ret [Result] the result of operation for each key in
95
+ * asynchronous mode (valid attributes: +error+, +operation+, +key+).
96
+ *
97
+ * @return [Hash] Mapping keys to result of unlock operation (+true+ if the
98
+ * operation was successful and +false+ otherwise)
99
+ *
100
+ * @example Unlock several keys
101
+ * c.unlock("foo" => cas1, :bar => cas2) #=> {"foo" => true, "bar" => true}
102
+ *
103
+ * @example Unlock several values in async mode
104
+ * c.run do
105
+ * c.unlock("foo" => 10, :bar => 20) do |ret|
106
+ * ret.operation #=> :unlock
107
+ * ret.success? #=> true
108
+ * ret.key #=> "foo" and "bar" in separate calls
109
+ * end
110
+ * end
111
+ *
112
+ */
113
+ VALUE
114
+ cb_bucket_unlock(int argc, VALUE *argv, VALUE self)
115
+ {
116
+ struct bucket_st *bucket = DATA_PTR(self);
117
+ struct context_st *ctx;
118
+ VALUE args, rv, proc, exc;
119
+ lcb_error_t err;
120
+ struct params_st params;
121
+
122
+ if (bucket->handle == NULL) {
123
+ rb_raise(eConnectError, "closed connection");
124
+ }
125
+ rb_scan_args(argc, argv, "0*&", &args, &proc);
126
+ if (!bucket->async && proc != Qnil) {
127
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
128
+ }
129
+ rb_funcall(args, id_flatten_bang, 0);
130
+ memset(&params, 0, sizeof(struct params_st));
131
+ params.type = cmd_unlock;
132
+ params.bucket = bucket;
133
+ cb_params_build(&params, RARRAY_LEN(args), args);
134
+ ctx = xcalloc(1, sizeof(struct context_st));
135
+ if (ctx == NULL) {
136
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for context");
137
+ }
138
+ ctx->proc = cb_gc_protect(bucket, proc);
139
+ ctx->bucket = bucket;
140
+ rv = rb_hash_new();
141
+ ctx->rv = &rv;
142
+ ctx->exception = Qnil;
143
+ ctx->quiet = params.cmd.unlock.quiet;
144
+ ctx->nqueries = params.cmd.unlock.num;
145
+ err = lcb_unlock(bucket->handle, (const void *)ctx,
146
+ params.cmd.unlock.num, params.cmd.unlock.ptr);
147
+ cb_params_destroy(&params);
148
+ exc = cb_check_error(err, "failed to schedule unlock request", Qnil);
149
+ if (exc != Qnil) {
150
+ xfree(ctx);
151
+ rb_exc_raise(exc);
152
+ }
153
+ bucket->nbytes += params.npayload;
154
+ if (bucket->async) {
155
+ maybe_do_loop(bucket);
156
+ return Qnil;
157
+ } else {
158
+ if (ctx->nqueries > 0) {
159
+ /* we have some operations pending */
160
+ lcb_wait(bucket->handle);
161
+ }
162
+ exc = ctx->exception;
163
+ xfree(ctx);
164
+ if (exc != Qnil) {
165
+ rb_exc_raise(cb_gc_unprotect(bucket, exc));
166
+ }
167
+ if (bucket->exception != Qnil) {
168
+ rb_exc_raise(bucket->exception);
169
+ }
170
+ if (params.cmd.unlock.num > 1) {
171
+ return rv; /* return as a hash {key => true, ...} */
172
+ } else {
173
+ VALUE vv = Qnil;
174
+ rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
175
+ return vv;
176
+ }
177
+ }
178
+ }
179
+
180
+