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
data/.gitignore CHANGED
@@ -1,6 +1,7 @@
1
+ *.bundle
1
2
  *.gem
2
3
  *.so
3
- .bundle
4
+ .timestamp
4
5
  .yardoc
5
6
  Gemfile.lock
6
7
  core*
@@ -1,6 +1,13 @@
1
1
  before_install:
2
+ - sudo rm -rf /etc/apt/sources.list.d/couchdb-ppa-source.list
3
+ - sudo rm -rf /etc/apt/sources.list.d/mongodb.list
4
+ - sudo rm -rf /etc/apt/sources.list.d/datastax-source.list
5
+ - sudo rm -rf /etc/apt/sources.list.d/rabbitmq-source.list
6
+ - sudo rm -rf /etc/apt/sources.list.d/mapopa-source.list
7
+ - sudo rm -rf /etc/apt/sources.list.d/webupd8team-java-ppa-source.list
8
+ - sudo rm -rf /etc/apt/sources.list.d/maven3-ppa-source.list
2
9
  - wget -O- http://packages.couchbase.com/ubuntu/couchbase.key | sudo apt-key add -
3
- - echo deb http://packages.couchbase.com/ubuntu lucid lucid/main | sudo tee /etc/apt/sources.list.d/couchbase.list
10
+ - echo deb http://packages.couchbase.com/snapshot/ubuntu oneiric oneiric/main | sudo tee /etc/apt/sources.list.d/couchbase.list
4
11
  - sudo apt-get update
5
12
  - sudo apt-get -y install libevent-dev libvbucket-dev libcouchbase-dev
6
13
 
@@ -9,3 +16,7 @@ rvm:
9
16
  - 1.9.2
10
17
  - 1.9.3
11
18
  - ree
19
+
20
+ notifications:
21
+ email:
22
+ - sdk_dev@couchbase.com
@@ -1,6 +1,117 @@
1
- ## 1.1.5 / 2012-09-17
1
+ ## 1.2.0.beta / 2012-09-18
2
2
 
3
+ * RCBC-70 return binary keys using Encoding.external value (thanks to Alex Leverington)
4
+ * Create new key object only if it is necessary
5
+ * Switch to rbenv because RVM doesn't work with tclsh
6
+ * [backport] RCBC-37 Bootstrapping using multiple nodes
7
+ * React on HTTP level errors in view request
8
+ * Fix typo: check for MultiJson.decode
9
+ * Use unified HTTP function from the latest libcouchbase
10
+ * Expose HTTP headers
11
+ * Update views to meet latest server changes
12
+ * Add support for spatial views
13
+ * Use updated libcouchbase_cancel_http_request()
14
+ * Workaround query issue on group=false&reduce=false
15
+ * Update windows build
16
+ * Bump version 1.1.4
17
+ * Refactor the C extension
18
+ * Fix CAS conversion for Bucket#delete method for 32-bit systems
19
+ * RCBC-28 Implement Bucket#unlock
20
+ * CCBC-98 Expose client temporary failure error
21
+ * Fix warnings in doc generator and specify return values
22
+ * Fix -Wreturn-type warning
23
+ * Add attribute reader for Error::Base status code
24
+ * Unset RUBYOPT to avoid issues with bundler
25
+ * Ignore shared object on macos
26
+ * RCBC-79 Use RESTful flush
27
+ * Fix build under bundler on MacOS
3
28
  * RCBC-81 Protect against NoMethodError
29
+ * Fixup MAKEFILE_CONFIG which is copy of the CONFIG
30
+ * RCBC-81 Protect against NoMethodError
31
+ * Update windows build
32
+
33
+ ## 1.2.0.dp6 / 2012-07-28
34
+
35
+ * Inherit StandardError instead RuntimeError for errors
36
+ * Use renamed functions for view requests
37
+ * Depend on yard-xml plugin to dump docs into single XML
38
+ * No need to check for NULL before deallocating memory
39
+ * RCBC-37 Bootstrapping using multiple nodes
40
+ * More docs and examples on views (fixes RCBC-43)
41
+ * RCBC-40 Fix Bucket#cas operation behaviour in async mode
42
+ * RCBC-39 Allow to specify delta for incr/decr in options
43
+ * Update README
44
+ * Apply timeout value before connection
45
+ * Clarify connection exceptions
46
+ * Remove seqno kludge
47
+ * rb_hash_delete() function could incorrectly detect block presence
48
+ * Add example with in-URL credentials
49
+ * RCBC-57 Expose timers API from libcouchbase
50
+ * RCBC-50 Allow to read keys from replica
51
+ * RCBC-6 Implement OBSERVE command
52
+ * Expose number of replicas to the user
53
+ * Notify about observe batch finish in async mode
54
+ * Separate memory errors for client and server
55
+ * Prefix error message from views with "SERVER: "
56
+ * Remove timeout hack
57
+ * RCBC-49 Bucket#observe_and_wait primitive
58
+ * RCBC-47 Allow to skip username for protected buckets
59
+ * Use allocators instead of singleton methods
60
+ * Check RDATA()->dfree to ensure object type
61
+ * Fix observe_and_wait in async mode
62
+ * Fill 'operation' in observe_and_wait Result object
63
+ * Fix extraction Hash with keys in observe_and_wait
64
+ * RCBC-49 :observe option for storage functions
65
+ * Fix timeout test
66
+ * Mention couchbase.com in install errors
67
+ * Make Bucket#observe_and_wait more 1.8.7 friendly
68
+
69
+ ## 1.2.0.dp5 / 2012-06-15
70
+
71
+ * Allow to force assembling result Hash for multi-get
72
+ * Fix documentation for :ttl option in Bucket#cas
73
+ * Implement key prefix (simple namespacing)
74
+ * Implement cache store adapter for Rails
75
+ * Integrate with Rack and Rails session store
76
+
77
+ ## 1.2.0.dp4 / 2012-06-07
78
+
79
+ * RCBC-36 Fix segfault
80
+ * Comment out unpredictable test
81
+ * Update replace documentation: it accepts :cas option
82
+
83
+ ## 1.2.0.dp3 / 2012-06-06
84
+
85
+ * Fix for multi_json < 1.3.3
86
+ * Break out from event loop for non-chunked responses (fix creating
87
+ design create)
88
+
89
+ ## 1.2.0.dp2 / 2012-06-05
90
+
91
+ * RCBC-31 Make Bucket#get more consistent
92
+ * Use monotonic high resolution clock
93
+ * Implement threshold for outgoing commands
94
+ * Allow to stop event loop from ruby
95
+ * RCBC-35 Fix the params escaping
96
+ * RCBC-34 Use multi_json gem
97
+ * Allow to block and wait for part of the requests
98
+ * Fix view iterator. It doesn't lock event loop anymore
99
+ * Specify HTTP method when body is set for View request
100
+ * Define views only if "views" key presented
101
+ * Use plain structs instead of typedefs
102
+ * Use debugger gem for 1.9.x rubies
103
+ * Use latest stable build of libcouchbase for travis-ci
104
+ * Require yajl as development dependency
105
+ * Implement get with lock operation
106
+ * Update docs
107
+
108
+ ## 1.2.0.dp / 2012-04-06
109
+
110
+ * Properly handle hashes as Couchbase.connection_options
111
+ * Implement views
112
+ * Use verbose mode by default throwing exceptions on NOT_FOUND errors.
113
+ This means that quiet attribute is false now on new connections.
114
+ * Doc fixes
4
115
 
5
116
  ## 1.1.4 / 2012-08-30
6
117
 
@@ -1,4 +1,4 @@
1
- # Couchbase Ruby Client [![Build Status](https://secure.travis-ci.org/avsej/couchbase-ruby-client.png?branch=master)](http://travis-ci.org/avsej/couchbase-ruby-client)
1
+ # Couchbase Ruby Client [![Build Status](https://secure.travis-ci.org/couchbase/couchbase-ruby-client.png?branch=master)](http://travis-ci.org/couchbase/couchbase-ruby-client)
2
2
 
3
3
  This is the official client library for use with Couchbase Server.
4
4
 
@@ -18,14 +18,15 @@ libcouchbase doesn't take much effort.
18
18
 
19
19
  $ brew install libcouchbase
20
20
 
21
- Or if [our pull requests][4] for isn't yet merged:
21
+ The official homebrew repository contains only stable versions of
22
+ libvbucket and libcouchbase, if you need preview, take a look at
23
+ Couchbase's fork: https://github.com/couchbase/homebrew
22
24
 
23
- $ brew install https://github.com/avsej/homebrew/raw/libvbucket/Library/Formula/libvbucket.rb
24
- $ brew install https://github.com/avsej/homebrew/raw/libcouchbase/Library/Formula/libcouchbase.rb
25
+ $ brew install https://raw.github.com/couchbase/homebrew/preview/Library/Formula/libcouchbase.rb
25
26
 
26
27
  ### Debian (Ubuntu)
27
28
 
28
- Add the appropriate line to /etc/apt/sources.list.d/couchbase.list for
29
+ Add the appropriate line to `/etc/apt/sources.list.d/couchbase.list` for
29
30
  your OS release:
30
31
 
31
32
  # Ubuntu 11.10 Oneiric Ocelot (Debian unstable)
@@ -42,6 +43,15 @@ Then install them
42
43
 
43
44
  $ sudo apt-get update && sudo apt-get install libcouchbase-dev
44
45
 
46
+ Again, if you need preview versions, just use another repositories in
47
+ your `couchbase.list`
48
+
49
+ # Ubuntu 11.10 Oneiric Ocelot (Debian unstable)
50
+ deb http://packages.couchbase.com/preview/ubuntu oneiric oneiric/main
51
+
52
+ # Ubuntu 10.04 Lucid Lynx (Debian stable or testing)
53
+ deb http://packages.couchbase.com/preview/ubuntu lucid lucid/main
54
+
45
55
  ### Centos (Redhat and rpm-based systems)
46
56
 
47
57
  Add these lines to /etc/yum.repos.d/couchbase.repo using the correct architecture
@@ -52,12 +62,23 @@ Add these lines to /etc/yum.repos.d/couchbase.repo using the correct architectur
52
62
 
53
63
  [couchbase]
54
64
  name = Couchbase package repository
55
- baseurl = http:///packages.couchbase.com/rpm/5.5/x86_64
65
+ baseurl = http://packages.couchbase.com/rpm/5.5/x86_64
56
66
 
57
67
  Then to install libcouchbase itself, run:
58
68
 
59
69
  $ sudo yum update && sudo yum install libcouchbase-devel
60
70
 
71
+ We have preview repositories for RPMs too, use them if you need to try
72
+ fresh version of couchbase gem:
73
+
74
+ [couchbase]
75
+ name = Couchbase package repository
76
+ baseurl = http://packages.couchbase.com/preview/rpm/5.5/i386
77
+
78
+ [couchbase]
79
+ name = Couchbase package repository
80
+ baseurl = http://packages.couchbase.com/preview/rpm/5.5/x86_64
81
+
61
82
  ### Windows
62
83
 
63
84
  There no additional dependencies for Windows systems. The gem carry
@@ -94,6 +115,14 @@ This is equivalent to following forms:
94
115
 
95
116
  The hash parameters take precedence on string URL.
96
117
 
118
+ If you worry about state of your nodes or not sure what node is alive,
119
+ you can pass the list of nodes and the library will iterate over it
120
+ until finds the working one. From that moment it won't use **your**
121
+ list, because node list from cluster config is more actual.
122
+
123
+ c = Couchbase.connect(:bucket => "mybucket",
124
+ :node_list => ['example.com:8091', example.net'])
125
+
97
126
  There is also handy method `Couchbase.bucket` which uses thread local
98
127
  storage to keep the reference to default connection. You can set the
99
128
  connection options via `Couchbase.connection_options`:
@@ -371,6 +400,120 @@ the key and stats as key-value pairs.
371
400
  # ...
372
401
  }
373
402
 
403
+ ### Timers
404
+
405
+ It is possible to create timers to implement general purpose timeouts.
406
+ Note that timers are using microseconds for time intervals. For example,
407
+ following examples increment the keys value five times with 0.5 second
408
+ interval:
409
+
410
+ c.set("foo", 100)
411
+ n = 1
412
+ c.run do
413
+ c.create_periodic_timer(500000) do |tm|
414
+ c.incr("foo") do
415
+ if n == 5
416
+ tm.cancel
417
+ else
418
+ n += 1
419
+ end
420
+ end
421
+ end
422
+ end
423
+
424
+ ### Views (Map/Reduce queries)
425
+
426
+ If you store structured data, they will be treated as documents and you
427
+ can handle them in map/reduce function from Couchbase Views. For example,
428
+ store a couple of posts using memcached API:
429
+
430
+ c['biking'] = {:title => 'Biking',
431
+ :body => 'My biggest hobby is mountainbiking. The other day...',
432
+ :date => '2009/01/30 18:04:11'}
433
+ c['bought-a-cat'] = {:title => 'Bought a Cat',
434
+ :body => 'I went to the the pet store earlier and brought home a little kitty...',
435
+ :date => '2009/01/30 20:04:11'}
436
+ c['hello-world'] = {:title => 'Hello World',
437
+ :body => 'Well hello and welcome to my new blog...',
438
+ :date => '2009/01/15 15:52:20'}
439
+ c.all_docs.count #=> 3
440
+
441
+ Now let's create design doc with sample view and save it in file
442
+ 'blog.json':
443
+
444
+ {
445
+ "_id": "_design/blog",
446
+ "language": "javascript",
447
+ "views": {
448
+ "recent_posts": {
449
+ "map": "function(doc){if(doc.date && doc.title){emit(doc.date, doc.title);}}"
450
+ }
451
+ }
452
+ }
453
+
454
+ This design document could be loaded into the database like this (also you can
455
+ pass the ruby Hash or String with JSON encoded document):
456
+
457
+ c.save_design_doc(File.open('blog.json'))
458
+
459
+ To execute view you need to fetch it from design document `_design/blog`:
460
+
461
+ blog = c.design_docs['blog']
462
+ blog.views #=> ["recent_posts"]
463
+ blog.recent_posts #=> [#<Couchbase::ViewRow:9855800 @id="hello-world" @key="2009/01/15 15:52:20" @value="Hello World" @doc=nil @meta={} @views=[]>, ...]
464
+
465
+ Gem uses streaming parser to access view results so you can iterate them
466
+ easily and if your code won't keep links to the documents GC might free
467
+ them as soon as it decide they are unreachable, because parser doesn't
468
+ store global JSON tree.
469
+
470
+ blog.recent_posts.each do |doc|
471
+ # do something
472
+ # with doc object
473
+ doc.key # gives the key argument of the emit()
474
+ doc.value # gives the value argument of the emit()
475
+ end
476
+
477
+ Load with documents
478
+
479
+ blog.recent_posts(:include_docs => true).each do |doc|
480
+ doc.doc # gives the document which emitted the item
481
+ doc['date'] # gives the argument of the underlying document
482
+ end
483
+
484
+
485
+ You can also use Enumerator to iterate view results
486
+
487
+ require 'date'
488
+ posts_by_date = Hash.new{|h,k| h[k] = []}
489
+ enum = c.all_docs(:include_docs => true).each # request hasn't issued yet
490
+ enum.inject(posts_by_date) do |acc, doc|
491
+ acc[date] = Date.strptime(doc['date'], '%Y/%m/%d')
492
+ acc
493
+ end
494
+
495
+ The Couchbase server could generate errors during view execution with
496
+ `200 OK` and partial results. By default the library raises exception as
497
+ soon as errors detected in the result stream, but you can define the
498
+ callback `on_error` to intercept these errors and do something more
499
+ useful.
500
+
501
+ view = blog.recent_posts(:include_docs => true)
502
+ logger = Logger.new(STDOUT)
503
+
504
+ view.on_error do |from, reason|
505
+ logger.warn("#{view.inspect} received the error '#{reason}' from #{from}")
506
+ end
507
+
508
+ posts = view.each do |doc|
509
+ # do something
510
+ # with doc object
511
+ end
512
+
513
+ Note that errors object in view results usually goes *after* the rows,
514
+ so you will likely receive a number of view results successfully before
515
+ the error is detected.
516
+
374
517
  [1]: http://couchbase.com/issues/browse/RCBC
375
518
  [2]: http://freenode.net/irc_servers.shtml
376
519
  [3]: http://www.couchbase.com/develop/c/current
@@ -35,13 +35,17 @@ Gem::Specification.new do |s|
35
35
  s.extensions = `git ls-files -- ext/**/extconf.rb`.split("\n")
36
36
  s.require_paths = ['lib']
37
37
 
38
- s.add_runtime_dependency 'yajl-ruby', '~> 1.1.0'
38
+ s.add_runtime_dependency 'yaji', '~> 0.3.2'
39
+ s.add_runtime_dependency 'multi_json', '~> 1.0'
39
40
 
40
41
  s.add_development_dependency 'rake', '~> 0.8.7'
41
42
  s.add_development_dependency 'minitest'
42
43
  s.add_development_dependency 'rake-compiler', '>= 0.7.5'
43
44
  s.add_development_dependency 'rdiscount'
44
45
  s.add_development_dependency 'yard'
46
+ s.add_development_dependency 'yard-xml'
45
47
  s.add_development_dependency 'mini_portile'
48
+ s.add_development_dependency 'yajl-ruby', '~> 1.1.0'
46
49
  s.add_development_dependency RUBY_VERSION =~ /^1\.9/ ? 'debugger' : 'ruby-debug'
50
+ s.add_development_dependency 'active_support'
47
51
  end
@@ -0,0 +1,4 @@
1
+ *.log
2
+ *.o
3
+ Makefile
4
+ couchbase_config.h
@@ -0,0 +1,973 @@
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
+
21
+ /* TOUCH */
22
+
23
+ static void
24
+ cb_params_touch_alloc(struct params_st *params, lcb_size_t size)
25
+ {
26
+ lcb_size_t ii;
27
+
28
+ params->cmd.touch.num = size;
29
+ params->cmd.touch.items = xcalloc(size, sizeof(lcb_touch_cmd_t));
30
+ if (params->cmd.touch.items == NULL) {
31
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for arguments");
32
+ }
33
+ params->cmd.touch.ptr = xcalloc(size, sizeof(lcb_touch_cmd_t *));
34
+ if (params->cmd.touch.ptr == NULL) {
35
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for arguments");
36
+ }
37
+ for (ii = 0; ii < size; ++ii) {
38
+ params->cmd.touch.ptr[ii] = params->cmd.touch.items + ii;
39
+ }
40
+ }
41
+
42
+ static void
43
+ cb_params_touch_init_item(struct params_st *params, lcb_size_t idx, VALUE key_obj, lcb_time_t exptime)
44
+ {
45
+ key_obj = unify_key(params->bucket, key_obj, 1);
46
+ params->cmd.touch.items[idx].v.v0.key = RSTRING_PTR(key_obj);
47
+ params->cmd.touch.items[idx].v.v0.nkey = RSTRING_LEN(key_obj);
48
+ params->cmd.touch.items[idx].v.v0.exptime = exptime;
49
+ params->npayload += RSTRING_LEN(key_obj) + sizeof(exptime);
50
+ }
51
+
52
+ static int
53
+ cb_params_touch_extract_keys_i(VALUE key, VALUE value, VALUE arg)
54
+ {
55
+ struct params_st *params = (struct params_st *)arg;
56
+ cb_params_touch_init_item(params, params->idx++, key, NUM2ULONG(value));
57
+ return ST_CONTINUE;
58
+ }
59
+
60
+ static void
61
+ cb_params_touch_parse_options(struct params_st *params, VALUE options)
62
+ {
63
+ VALUE tmp;
64
+
65
+ if (NIL_P(options)) {
66
+ return;
67
+ }
68
+ tmp = rb_hash_aref(options, sym_ttl);
69
+ if (tmp != Qnil) {
70
+ params->cmd.touch.ttl = NUM2ULONG(tmp);
71
+ }
72
+ if (RTEST(rb_funcall(options, id_has_key_p, 1, sym_quiet))) {
73
+ params->cmd.touch.quiet = RTEST(rb_hash_aref(options, sym_quiet));
74
+ }
75
+ }
76
+
77
+ static void
78
+ cb_params_touch_parse_arguments(struct params_st *params, int argc, VALUE argv)
79
+ {
80
+ lcb_size_t ii;
81
+
82
+ if (argc < 1) {
83
+ rb_raise(rb_eArgError, "must be at least one key");
84
+ }
85
+ if (argc == 1) {
86
+ VALUE keys = RARRAY_PTR(argv)[0];
87
+ switch(TYPE(keys)) {
88
+ case T_ARRAY:
89
+ /* array of keys as a first argument */
90
+ params->cmd.touch.array = 1;
91
+ cb_params_touch_alloc(params, RARRAY_LEN(keys));
92
+ for (ii = 0; ii < params->cmd.touch.num; ++ii) {
93
+ cb_params_touch_init_item(params, ii, RARRAY_PTR(keys)[ii], params->cmd.touch.ttl);
94
+ }
95
+ break;
96
+ case T_HASH:
97
+ /* key-ttl pairs */
98
+ cb_params_touch_alloc(params, RHASH_SIZE(keys));
99
+ rb_hash_foreach(keys, cb_params_touch_extract_keys_i,
100
+ (VALUE)params);
101
+ break;
102
+ default:
103
+ /* single key */
104
+ cb_params_touch_alloc(params, 1);
105
+ cb_params_touch_init_item(params, 0, keys, params->cmd.touch.ttl);
106
+ }
107
+ } else {
108
+ /* just list of arguments */
109
+ cb_params_touch_alloc(params, argc);
110
+ for (ii = 0; ii < params->cmd.touch.num; ++ii) {
111
+ cb_params_touch_init_item(params, ii, RARRAY_PTR(argv)[ii], params->cmd.touch.ttl);
112
+ }
113
+ }
114
+ }
115
+
116
+
117
+ /* REMOVE */
118
+
119
+ static void
120
+ cb_params_remove_alloc(struct params_st *params, lcb_size_t size)
121
+ {
122
+ lcb_size_t ii;
123
+
124
+ params->cmd.remove.num = size;
125
+ params->cmd.remove.items = xcalloc(size, sizeof(lcb_remove_cmd_t));
126
+ if (params->cmd.remove.items == NULL) {
127
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for arguments");
128
+ }
129
+ params->cmd.remove.ptr = xcalloc(size, sizeof(lcb_remove_cmd_t *));
130
+ if (params->cmd.remove.ptr == NULL) {
131
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for arguments");
132
+ }
133
+ for (ii = 0; ii < size; ++ii) {
134
+ params->cmd.remove.ptr[ii] = params->cmd.remove.items + ii;
135
+ }
136
+ }
137
+
138
+ static void
139
+ cb_params_remove_init_item(struct params_st *params, lcb_size_t idx, VALUE key_obj, lcb_cas_t cas)
140
+ {
141
+ key_obj = unify_key(params->bucket, key_obj, 1);
142
+ params->cmd.remove.items[idx].v.v0.key = RSTRING_PTR(key_obj);
143
+ params->cmd.remove.items[idx].v.v0.nkey = RSTRING_LEN(key_obj);
144
+ params->cmd.remove.items[idx].v.v0.cas = cas;
145
+ params->npayload += RSTRING_LEN(key_obj);
146
+ }
147
+
148
+ static int
149
+ cb_params_remove_extract_keys_i(VALUE key, VALUE value, VALUE arg)
150
+ {
151
+ struct params_st *params = (struct params_st *)arg;
152
+ cb_params_remove_init_item(params, params->idx++, key, NUM2ULL(value));
153
+ return ST_CONTINUE;
154
+ }
155
+
156
+ static void
157
+ cb_params_remove_parse_options(struct params_st *params, VALUE options)
158
+ {
159
+ VALUE tmp;
160
+
161
+ if (NIL_P(options)) {
162
+ return;
163
+ }
164
+ if (RTEST(rb_funcall(options, id_has_key_p, 1, sym_quiet))) {
165
+ params->cmd.remove.quiet = RTEST(rb_hash_aref(options, sym_quiet));
166
+ }
167
+ tmp = rb_hash_aref(options, sym_cas);
168
+ if (tmp != Qnil) {
169
+ params->cmd.remove.cas = NUM2ULL(tmp);
170
+ }
171
+ }
172
+
173
+ static void
174
+ cb_params_remove_parse_arguments(struct params_st *params, int argc, VALUE argv)
175
+ {
176
+ lcb_size_t ii;
177
+
178
+ if (argc < 1) {
179
+ rb_raise(rb_eArgError, "must be at least one key");
180
+ }
181
+ if (argc == 1) {
182
+ VALUE keys = RARRAY_PTR(argv)[0];
183
+ switch(TYPE(keys)) {
184
+ case T_ARRAY:
185
+ /* array of keys as a first argument */
186
+ params->cmd.remove.array = 1;
187
+ cb_params_remove_alloc(params, RARRAY_LEN(keys));
188
+ for (ii = 0; ii < params->cmd.remove.num; ++ii) {
189
+ cb_params_remove_init_item(params, ii, RARRAY_PTR(keys)[ii], params->cmd.remove.cas);
190
+ }
191
+ break;
192
+ case T_HASH:
193
+ /* key-cas pairs */
194
+ cb_params_remove_alloc(params, RHASH_SIZE(keys));
195
+ rb_hash_foreach(keys, cb_params_remove_extract_keys_i,
196
+ (VALUE)params);
197
+ break;
198
+ default:
199
+ /* single key */
200
+ cb_params_remove_alloc(params, 1);
201
+ cb_params_remove_init_item(params, 0, keys, params->cmd.remove.cas);
202
+ }
203
+ } else {
204
+ /* just list of arguments */
205
+ cb_params_remove_alloc(params, argc);
206
+ for (ii = 0; ii < params->cmd.remove.num; ++ii) {
207
+ cb_params_remove_init_item(params, ii, RARRAY_PTR(argv)[ii], params->cmd.remove.cas);
208
+ }
209
+ }
210
+ }
211
+
212
+
213
+ /* STORE */
214
+ static void
215
+ cb_params_store_alloc(struct params_st *params, lcb_size_t size)
216
+ {
217
+ lcb_size_t ii;
218
+
219
+ params->cmd.store.num = size;
220
+ params->cmd.store.items = xcalloc(size, sizeof(lcb_store_cmd_t));
221
+ if (params->cmd.store.items == NULL) {
222
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for arguments");
223
+ }
224
+ params->cmd.store.ptr = xcalloc(size, sizeof(lcb_store_cmd_t *));
225
+ if (params->cmd.store.ptr == NULL) {
226
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for arguments");
227
+ }
228
+ for (ii = 0; ii < size; ++ii) {
229
+ params->cmd.store.ptr[ii] = params->cmd.store.items + ii;
230
+ }
231
+ }
232
+
233
+ static void
234
+ cb_params_store_init_item(struct params_st *params, lcb_size_t idx,
235
+ VALUE key_obj, VALUE value_obj, lcb_uint32_t flags, lcb_cas_t cas,
236
+ lcb_time_t exptime)
237
+ {
238
+ key_obj = unify_key(params->bucket, key_obj, 1);
239
+ value_obj = encode_value(value_obj, params->cmd.store.flags);
240
+ if (value_obj == Qundef) {
241
+ rb_raise(eValueFormatError, "unable to convert value for key '%s'", RSTRING_PTR(key_obj));
242
+ }
243
+ params->cmd.store.items[idx].v.v0.datatype = params->cmd.store.datatype;
244
+ params->cmd.store.items[idx].v.v0.operation = params->cmd.store.operation;
245
+ params->cmd.store.items[idx].v.v0.key = RSTRING_PTR(key_obj);
246
+ params->cmd.store.items[idx].v.v0.nkey = RSTRING_LEN(key_obj);
247
+ params->cmd.store.items[idx].v.v0.bytes = RSTRING_PTR(value_obj);
248
+ params->cmd.store.items[idx].v.v0.nbytes = RSTRING_LEN(value_obj);
249
+ params->cmd.store.items[idx].v.v0.flags = flags;
250
+ params->cmd.store.items[idx].v.v0.cas = cas;
251
+ params->cmd.store.items[idx].v.v0.exptime = exptime;
252
+ params->npayload += RSTRING_LEN(key_obj) + RSTRING_LEN(value_obj) + sizeof(flags) + sizeof(exptime);
253
+ }
254
+
255
+ static int
256
+ cb_params_store_extract_keys_i(VALUE key, VALUE value, VALUE arg)
257
+ {
258
+ struct params_st *params = (struct params_st *)arg;
259
+ cb_params_store_init_item(params, params->idx++, key, value,
260
+ params->cmd.store.flags, 0, params->cmd.store.ttl);
261
+ return ST_CONTINUE;
262
+ }
263
+
264
+ static void
265
+ cb_params_store_parse_options(struct params_st *params, VALUE options)
266
+ {
267
+ VALUE tmp;
268
+
269
+ if (NIL_P(options)) {
270
+ return;
271
+ }
272
+ tmp = rb_hash_aref(options, sym_flags);
273
+ if (tmp != Qnil) {
274
+ params->cmd.store.flags = (lcb_uint32_t)NUM2ULONG(tmp);
275
+ }
276
+ tmp = rb_hash_aref(options, sym_format);
277
+ if (tmp != Qnil) { /* rewrite format bits */
278
+ params->cmd.store.flags = flags_set_format(params->cmd.store.flags, tmp);
279
+ }
280
+ tmp = rb_hash_aref(options, sym_ttl);
281
+ if (tmp != Qnil) {
282
+ params->cmd.store.ttl = NUM2ULONG(tmp);
283
+ }
284
+ tmp = rb_hash_aref(options, sym_cas);
285
+ if (tmp != Qnil) {
286
+ params->cmd.store.cas = NUM2ULL(tmp);
287
+ }
288
+ tmp = rb_hash_aref(options, sym_observe);
289
+ if (tmp != Qnil) {
290
+ Check_Type(tmp, T_HASH);
291
+ rb_funcall(params->bucket->self, id_verify_observe_options, 1, tmp);
292
+ params->cmd.store.observe = tmp;
293
+ }
294
+ if (flags_get_format(params->cmd.store.flags) == sym_document) {
295
+ /* just amend datatype for now */
296
+ params->cmd.store.datatype = 0x01;
297
+ }
298
+ }
299
+
300
+ static void
301
+ cb_params_store_parse_arguments(struct params_st *params, int argc, VALUE argv)
302
+ {
303
+ VALUE keys;
304
+
305
+ if (argc < 1) {
306
+ rb_raise(rb_eArgError, "the key and value must be specified");
307
+ }
308
+ switch (argc) {
309
+ case 1:
310
+ keys = RARRAY_PTR(argv)[0];
311
+ switch(TYPE(keys)) {
312
+ case T_HASH:
313
+ /* key-value pairs */
314
+ cb_params_store_alloc(params, RHASH_SIZE(keys));
315
+ rb_hash_foreach(keys, cb_params_store_extract_keys_i,
316
+ (VALUE)params);
317
+ break;
318
+ default:
319
+ rb_raise(rb_eArgError, "there must be either Hash with key-value pairs"
320
+ " or two separate arguments: key and value");
321
+ }
322
+ break;
323
+ case 2:
324
+ /* just key and value */
325
+ cb_params_store_alloc(params, 1);
326
+ cb_params_store_init_item(params, 0, RARRAY_PTR(argv)[0], RARRAY_PTR(argv)[1],
327
+ params->cmd.store.flags, params->cmd.store.cas, params->cmd.store.ttl);
328
+ break;
329
+ default:
330
+ rb_raise(rb_eArgError, "too many arguments");
331
+ }
332
+ }
333
+
334
+
335
+ /* GET */
336
+ static void
337
+ cb_params_get_alloc(struct params_st *params, lcb_size_t size)
338
+ {
339
+ lcb_size_t ii;
340
+
341
+ params->cmd.get.num = size;
342
+ if (params->cmd.get.replica) {
343
+ params->cmd.get.items = xcalloc(size, sizeof(lcb_get_replica_cmd_t));
344
+ if (params->cmd.get.items_gr == NULL) {
345
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for arguments");
346
+ }
347
+ params->cmd.get.ptr = xcalloc(size, sizeof(lcb_get_replica_cmd_t *));
348
+ if (params->cmd.get.ptr_gr == NULL) {
349
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for arguments");
350
+ }
351
+ for (ii = 0; ii < size; ++ii) {
352
+ params->cmd.get.ptr_gr[ii] = params->cmd.get.items_gr + ii;
353
+ }
354
+ } else {
355
+ params->cmd.get.items = xcalloc(size, sizeof(lcb_get_cmd_t));
356
+ if (params->cmd.get.items == NULL) {
357
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for arguments");
358
+ }
359
+ params->cmd.get.ptr = xcalloc(size, sizeof(lcb_get_cmd_t *));
360
+ if (params->cmd.get.ptr == NULL) {
361
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for arguments");
362
+ }
363
+ for (ii = 0; ii < size; ++ii) {
364
+ params->cmd.get.ptr[ii] = params->cmd.get.items + ii;
365
+ }
366
+ }
367
+ }
368
+
369
+ static void
370
+ cb_params_get_init_item(struct params_st *params, lcb_size_t idx,
371
+ VALUE key_obj, lcb_time_t exptime)
372
+ {
373
+ key_obj = unify_key(params->bucket, key_obj, 1);
374
+ if (params->cmd.get.replica) {
375
+ params->cmd.get.items_gr[idx].v.v0.key = RSTRING_PTR(key_obj);
376
+ params->cmd.get.items_gr[idx].v.v0.nkey = RSTRING_LEN(key_obj);
377
+ } else {
378
+ params->cmd.get.items[idx].v.v0.key = RSTRING_PTR(key_obj);
379
+ params->cmd.get.items[idx].v.v0.nkey = RSTRING_LEN(key_obj);
380
+ params->cmd.get.items[idx].v.v0.exptime = exptime;
381
+ params->cmd.get.items[idx].v.v0.lock = params->cmd.get.lock;
382
+ params->npayload += sizeof(exptime);
383
+ }
384
+ params->npayload += RSTRING_LEN(key_obj);
385
+ }
386
+
387
+ static int
388
+ cb_params_get_extract_keys_i(VALUE key, VALUE value, VALUE arg)
389
+ {
390
+ struct params_st *params = (struct params_st *)arg;
391
+ rb_ary_push(params->cmd.get.keys_ary, key);
392
+ cb_params_get_init_item(params, params->idx++, key, NUM2ULONG(value));
393
+ return ST_CONTINUE;
394
+ }
395
+
396
+ static void
397
+ cb_params_get_parse_options(struct params_st *params, VALUE options)
398
+ {
399
+ VALUE tmp;
400
+
401
+ if (NIL_P(options)) {
402
+ return;
403
+ }
404
+ params->cmd.get.replica = RTEST(rb_hash_aref(options, sym_replica));
405
+ params->cmd.get.extended = RTEST(rb_hash_aref(options, sym_extended));
406
+ params->cmd.get.assemble_hash = RTEST(rb_hash_aref(options, sym_assemble_hash));
407
+ if (RTEST(rb_funcall(options, id_has_key_p, 1, sym_quiet))) {
408
+ params->cmd.get.quiet = RTEST(rb_hash_aref(options, sym_quiet));
409
+ }
410
+ tmp = rb_hash_aref(options, sym_format);
411
+ if (tmp != Qnil) {
412
+ Check_Type(tmp, T_SYMBOL);
413
+ params->cmd.get.forced_format = tmp;
414
+ }
415
+ tmp = rb_hash_aref(options, sym_ttl);
416
+ if (tmp != Qnil) {
417
+ params->cmd.get.ttl = NUM2ULONG(tmp);
418
+ }
419
+ /* boolean or number of seconds to lock */
420
+ tmp = rb_hash_aref(options, sym_lock);
421
+ if (tmp != Qnil) {
422
+ params->cmd.get.lock = RTEST(tmp);
423
+ if (TYPE(tmp) == T_FIXNUM) {
424
+ params->cmd.get.ttl = NUM2ULONG(tmp);
425
+ }
426
+ }
427
+ }
428
+
429
+ static void
430
+ cb_params_get_parse_arguments(struct params_st *params, int argc, VALUE argv)
431
+ {
432
+ lcb_size_t ii;
433
+
434
+ if (argc < 1) {
435
+ rb_raise(rb_eArgError, "must be at least one key");
436
+ }
437
+ if (argc == 1) {
438
+ VALUE keys = RARRAY_PTR(argv)[0];
439
+ switch(TYPE(keys)) {
440
+ case T_ARRAY:
441
+ /* array of keys as a first argument */
442
+ params->cmd.get.array = 1;
443
+ cb_params_get_alloc(params, RARRAY_LEN(keys));
444
+ for (ii = 0; ii < params->cmd.get.num; ++ii) {
445
+ rb_ary_push(params->cmd.get.keys_ary, RARRAY_PTR(keys)[ii]);
446
+ cb_params_get_init_item(params, ii, RARRAY_PTR(keys)[ii], params->cmd.get.ttl);
447
+ }
448
+ break;
449
+ case T_HASH:
450
+ /* key-ttl pairs */
451
+ if (params->cmd.get.replica) {
452
+ rb_raise(rb_eArgError, "must be either list of key or single key");
453
+ }
454
+ params->cmd.get.gat = 1;
455
+ cb_params_get_alloc(params, RHASH_SIZE(keys));
456
+ rb_hash_foreach(keys, cb_params_get_extract_keys_i, (VALUE)params);
457
+ break;
458
+ default:
459
+ /* single key */
460
+ cb_params_get_alloc(params, 1);
461
+ rb_ary_push(params->cmd.get.keys_ary, keys);
462
+ cb_params_get_init_item(params, 0, keys, params->cmd.get.ttl);
463
+ }
464
+ } else {
465
+ /* just list of arguments */
466
+ cb_params_get_alloc(params, argc);
467
+ for (ii = 0; ii < params->cmd.get.num; ++ii) {
468
+ rb_ary_push(params->cmd.get.keys_ary, RARRAY_PTR(argv)[ii]);
469
+ cb_params_get_init_item(params, ii, RARRAY_PTR(argv)[ii], params->cmd.get.ttl);
470
+ }
471
+ }
472
+ }
473
+
474
+
475
+ /* ARITH */
476
+ static void
477
+ cb_params_arith_alloc(struct params_st *params, lcb_size_t size)
478
+ {
479
+ lcb_size_t ii;
480
+
481
+ params->cmd.arith.num = size;
482
+ params->cmd.arith.items = xcalloc(size, sizeof(lcb_arithmetic_cmd_t));
483
+ if (params->cmd.arith.items == NULL) {
484
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for arguments");
485
+ }
486
+ params->cmd.arith.ptr = xcalloc(size, sizeof(lcb_arithmetic_cmd_t *));
487
+ if (params->cmd.arith.ptr == NULL) {
488
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for arguments");
489
+ }
490
+ for (ii = 0; ii < size; ++ii) {
491
+ params->cmd.arith.ptr[ii] = params->cmd.arith.items + ii;
492
+ }
493
+ }
494
+
495
+ static void
496
+ cb_params_arith_init_item(struct params_st *params, lcb_size_t idx,
497
+ VALUE key_obj, lcb_int64_t delta)
498
+ {
499
+ key_obj = unify_key(params->bucket, key_obj, 1);
500
+ params->cmd.arith.items[idx].v.v0.key = RSTRING_PTR(key_obj);
501
+ params->cmd.arith.items[idx].v.v0.nkey = RSTRING_LEN(key_obj);
502
+ params->cmd.arith.items[idx].v.v0.delta = delta * params->cmd.arith.sign;
503
+ params->cmd.arith.items[idx].v.v0.exptime = params->cmd.arith.ttl;
504
+ params->cmd.arith.items[idx].v.v0.create = params->cmd.arith.create;
505
+ params->cmd.arith.items[idx].v.v0.initial = params->cmd.arith.initial;
506
+ params->npayload += RSTRING_LEN(key_obj);
507
+ }
508
+
509
+ static int
510
+ cb_params_arith_extract_keys_i(VALUE key, VALUE value, VALUE arg)
511
+ {
512
+ struct params_st *params = (struct params_st *)arg;
513
+ cb_params_arith_init_item(params, params->idx++, key, NUM2ULONG(value) & INT64_MAX);
514
+ return ST_CONTINUE;
515
+ }
516
+
517
+ static void
518
+ cb_params_arith_parse_options(struct params_st *params, VALUE options)
519
+ {
520
+ VALUE tmp;
521
+
522
+ if (NIL_P(options)) {
523
+ return;
524
+ }
525
+ params->cmd.arith.create = RTEST(rb_hash_aref(options, sym_create));
526
+ params->cmd.arith.extended = RTEST(rb_hash_aref(options, sym_extended));
527
+ tmp = rb_hash_aref(options, sym_ttl);
528
+ if (tmp != Qnil) {
529
+ params->cmd.arith.ttl = NUM2ULONG(tmp);
530
+ }
531
+ tmp = rb_hash_aref(options, sym_initial);
532
+ if (tmp != Qnil) {
533
+ params->cmd.arith.initial = NUM2ULL(tmp);
534
+ params->cmd.arith.create = 1;
535
+ }
536
+ tmp = rb_hash_aref(options, sym_delta);
537
+ if (tmp != Qnil) {
538
+ params->cmd.arith.delta = NUM2ULL(tmp) & INT64_MAX;
539
+ }
540
+ tmp = rb_hash_aref(options, sym_format);
541
+ if (tmp != Qnil) { /* rewrite format bits */
542
+ params->cmd.arith.format = tmp;
543
+ }
544
+ if (params->cmd.arith.format == sym_document) {
545
+ /* just amend datatype for now */
546
+ params->cmd.arith.datatype = 0x01;
547
+ }
548
+ }
549
+
550
+ static void
551
+ cb_params_arith_parse_arguments(struct params_st *params, int argc, VALUE argv)
552
+ {
553
+ lcb_size_t ii;
554
+
555
+ if (argc < 1) {
556
+ rb_raise(rb_eArgError, "must be at least one key");
557
+ }
558
+ if (argc == 1) {
559
+ VALUE keys = RARRAY_PTR(argv)[0];
560
+ switch(TYPE(keys)) {
561
+ case T_ARRAY:
562
+ /* array of keys as a first argument */
563
+ params->cmd.arith.array = 1;
564
+ cb_params_arith_alloc(params, RARRAY_LEN(keys));
565
+ for (ii = 0; ii < params->cmd.arith.num; ++ii) {
566
+ cb_params_arith_init_item(params, ii, RARRAY_PTR(keys)[ii], params->cmd.arith.delta);
567
+ }
568
+ break;
569
+ case T_HASH:
570
+ /* key-delta pairs */
571
+ cb_params_arith_alloc(params, RHASH_SIZE(keys));
572
+ rb_hash_foreach(keys, cb_params_arith_extract_keys_i, (VALUE)params);
573
+ break;
574
+ default:
575
+ /* single key */
576
+ cb_params_arith_alloc(params, 1);
577
+ cb_params_arith_init_item(params, 0, keys, params->cmd.arith.delta);
578
+ }
579
+ } else {
580
+ /* just list of arguments */
581
+ cb_params_arith_alloc(params, argc);
582
+ for (ii = 0; ii < params->cmd.arith.num; ++ii) {
583
+ cb_params_arith_init_item(params, ii, RARRAY_PTR(argv)[ii], params->cmd.arith.delta);
584
+ }
585
+ }
586
+ }
587
+
588
+
589
+ /* STATS */
590
+ static void
591
+ cb_params_stats_alloc(struct params_st *params, lcb_size_t size)
592
+ {
593
+ lcb_size_t ii;
594
+
595
+ params->cmd.stats.num = size;
596
+ params->cmd.stats.items = xcalloc(size, sizeof(lcb_server_stats_cmd_t));
597
+ if (params->cmd.stats.items == NULL) {
598
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for arguments");
599
+ }
600
+ params->cmd.stats.ptr = xcalloc(size, sizeof(lcb_server_stats_cmd_t *));
601
+ if (params->cmd.stats.ptr == NULL) {
602
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for arguments");
603
+ }
604
+ for (ii = 0; ii < size; ++ii) {
605
+ params->cmd.stats.ptr[ii] = params->cmd.stats.items + ii;
606
+ }
607
+ }
608
+
609
+ static void
610
+ cb_params_stats_init_item(struct params_st *params, lcb_size_t idx,
611
+ VALUE key_obj)
612
+ {
613
+ key_obj = unify_key(params->bucket, key_obj, 1);
614
+ params->cmd.stats.items[idx].v.v0.name = RSTRING_PTR(key_obj);
615
+ params->cmd.stats.items[idx].v.v0.nname = RSTRING_LEN(key_obj);
616
+ params->npayload += RSTRING_LEN(key_obj);
617
+ }
618
+
619
+ static void
620
+ cb_params_stats_parse_arguments(struct params_st *params, int argc, VALUE argv)
621
+ {
622
+ lcb_size_t ii;
623
+
624
+ if (argc == 1) {
625
+ VALUE keys = RARRAY_PTR(argv)[0];
626
+ switch(TYPE(keys)) {
627
+ case T_ARRAY:
628
+ /* array of keys as a first argument */
629
+ params->cmd.stats.array = 1;
630
+ cb_params_stats_alloc(params, RARRAY_LEN(keys));
631
+ for (ii = 0; ii < params->cmd.stats.num; ++ii) {
632
+ cb_params_stats_init_item(params, ii, RARRAY_PTR(keys)[ii]);
633
+ }
634
+ break;
635
+ default:
636
+ /* single key */
637
+ cb_params_stats_alloc(params, 1);
638
+ cb_params_stats_init_item(params, 0, keys);
639
+ }
640
+ } else if (argc == 0) {
641
+ /* stat without argument (single empty struct) */
642
+ cb_params_stats_alloc(params, 1);
643
+ } else {
644
+ /* just list of arguments */
645
+ cb_params_stats_alloc(params, argc);
646
+ for (ii = 0; ii < params->cmd.stats.num; ++ii) {
647
+ cb_params_stats_init_item(params, ii, RARRAY_PTR(argv)[ii]);
648
+ }
649
+ }
650
+ }
651
+
652
+
653
+ /* REMOVE */
654
+
655
+ static void
656
+ cb_params_observe_alloc(struct params_st *params, lcb_size_t size)
657
+ {
658
+ lcb_size_t ii;
659
+
660
+ params->cmd.observe.num = size;
661
+ params->cmd.observe.items = xcalloc(size, sizeof(lcb_observe_cmd_t));
662
+ if (params->cmd.observe.items == NULL) {
663
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for arguments");
664
+ }
665
+ params->cmd.observe.ptr = xcalloc(size, sizeof(lcb_observe_cmd_t *));
666
+ if (params->cmd.observe.ptr == NULL) {
667
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for arguments");
668
+ }
669
+ for (ii = 0; ii < size; ++ii) {
670
+ params->cmd.observe.ptr[ii] = params->cmd.observe.items + ii;
671
+ }
672
+ }
673
+
674
+ static void
675
+ cb_params_observe_init_item(struct params_st *params, lcb_size_t idx, VALUE key_obj)
676
+ {
677
+ key_obj = unify_key(params->bucket, key_obj, 1);
678
+ params->cmd.observe.items[idx].v.v0.key = RSTRING_PTR(key_obj);
679
+ params->cmd.observe.items[idx].v.v0.nkey = RSTRING_LEN(key_obj);
680
+ params->npayload += RSTRING_LEN(key_obj);
681
+ }
682
+
683
+ static void
684
+ cb_params_observe_parse_arguments(struct params_st *params, int argc, VALUE argv)
685
+ {
686
+ lcb_size_t ii;
687
+
688
+ if (argc < 1) {
689
+ rb_raise(rb_eArgError, "must be at least one key");
690
+ }
691
+ if (argc == 1) {
692
+ VALUE keys = RARRAY_PTR(argv)[0];
693
+ switch(TYPE(keys)) {
694
+ case T_ARRAY:
695
+ /* array of keys as a first argument */
696
+ params->cmd.observe.array = 1;
697
+ cb_params_observe_alloc(params, RARRAY_LEN(keys));
698
+ for (ii = 0; ii < params->cmd.observe.num; ++ii) {
699
+ cb_params_observe_init_item(params, ii, RARRAY_PTR(keys)[ii]);
700
+ }
701
+ break;
702
+ default:
703
+ /* single key */
704
+ cb_params_observe_alloc(params, 1);
705
+ cb_params_observe_init_item(params, 0, keys);
706
+ }
707
+ } else {
708
+ /* just list of arguments */
709
+ cb_params_observe_alloc(params, argc);
710
+ for (ii = 0; ii < params->cmd.observe.num; ++ii) {
711
+ cb_params_observe_init_item(params, ii, RARRAY_PTR(argv)[ii]);
712
+ }
713
+ }
714
+ }
715
+
716
+
717
+ /* UNLOCK */
718
+ static void
719
+ cb_params_unlock_alloc(struct params_st *params, lcb_size_t size)
720
+ {
721
+ lcb_size_t ii;
722
+
723
+ params->cmd.unlock.num = size;
724
+ params->cmd.unlock.items = xcalloc(size, sizeof(lcb_unlock_cmd_t));
725
+ if (params->cmd.unlock.items == NULL) {
726
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for arguments");
727
+ }
728
+ params->cmd.unlock.ptr = xcalloc(size, sizeof(lcb_unlock_cmd_t *));
729
+ if (params->cmd.unlock.ptr == NULL) {
730
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for arguments");
731
+ }
732
+ for (ii = 0; ii < size; ++ii) {
733
+ params->cmd.unlock.ptr[ii] = params->cmd.unlock.items + ii;
734
+ }
735
+ }
736
+
737
+ static void
738
+ cb_params_unlock_init_item(struct params_st *params, lcb_size_t idx, VALUE key_obj, lcb_cas_t cas)
739
+ {
740
+ key_obj = unify_key(params->bucket, key_obj, 1);
741
+ params->cmd.unlock.items[idx].v.v0.key = RSTRING_PTR(key_obj);
742
+ params->cmd.unlock.items[idx].v.v0.nkey = RSTRING_LEN(key_obj);
743
+ params->cmd.unlock.items[idx].v.v0.cas = cas;
744
+ params->npayload += RSTRING_LEN(key_obj);
745
+ }
746
+
747
+ static int
748
+ cb_params_unlock_extract_keys_i(VALUE key, VALUE value, VALUE arg)
749
+ {
750
+ struct params_st *params = (struct params_st *)arg;
751
+ cb_params_unlock_init_item(params, params->idx++, key, NUM2ULL(value));
752
+ return ST_CONTINUE;
753
+ }
754
+
755
+ static void
756
+ cb_params_unlock_parse_options(struct params_st *params, VALUE options)
757
+ {
758
+ VALUE tmp;
759
+
760
+ if (NIL_P(options)) {
761
+ return;
762
+ }
763
+ tmp = rb_hash_aref(options, sym_cas);
764
+ if (tmp != Qnil) {
765
+ params->cmd.unlock.cas = NUM2ULL(tmp);
766
+ }
767
+ if (RTEST(rb_funcall(options, id_has_key_p, 1, sym_quiet))) {
768
+ params->cmd.unlock.quiet = RTEST(rb_hash_aref(options, sym_quiet));
769
+ }
770
+ }
771
+
772
+ static void
773
+ cb_params_unlock_parse_arguments(struct params_st *params, int argc, VALUE argv)
774
+ {
775
+ if (argc == 1) {
776
+ VALUE keys = RARRAY_PTR(argv)[0];
777
+ switch(TYPE(keys)) {
778
+ case T_HASH:
779
+ /* key-cas pairs */
780
+ cb_params_unlock_alloc(params, RHASH_SIZE(keys));
781
+ rb_hash_foreach(keys, cb_params_unlock_extract_keys_i, (VALUE)params);
782
+ break;
783
+ default:
784
+ /* single key */
785
+ cb_params_unlock_alloc(params, 1);
786
+ cb_params_unlock_init_item(params, 0, keys, params->cmd.unlock.cas);
787
+ }
788
+ } else {
789
+ rb_raise(rb_eArgError, "must be either Hash or single key with cas option");
790
+ }
791
+ }
792
+
793
+
794
+ /* VERSION */
795
+ static void
796
+ cb_params_version_alloc(struct params_st *params)
797
+ {
798
+ params->cmd.version.num = 1;
799
+ params->cmd.version.items = xcalloc(1, sizeof(lcb_server_version_cmd_t));
800
+ if (params->cmd.version.items == NULL) {
801
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for arguments");
802
+ }
803
+ params->cmd.version.ptr = xcalloc(1, sizeof(lcb_server_version_cmd_t *));
804
+ if (params->cmd.version.ptr == NULL) {
805
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for arguments");
806
+ }
807
+ params->cmd.version.ptr[0] = params->cmd.version.items;
808
+ }
809
+
810
+
811
+ /* common stuff */
812
+ void
813
+ cb_params_destroy(struct params_st *params)
814
+ {
815
+ #define _release_data_for(type) \
816
+ xfree(params->cmd.type.items); \
817
+ xfree(params->cmd.type.ptr);
818
+
819
+ switch (params->type) {
820
+ case cmd_get:
821
+ _release_data_for(get);
822
+ xfree(params->cmd.get.items_gr);
823
+ xfree(params->cmd.get.ptr_gr);
824
+ break;
825
+ case cmd_touch:
826
+ _release_data_for(touch);
827
+ break;
828
+ case cmd_arith:
829
+ _release_data_for(arith);
830
+ break;
831
+ case cmd_remove:
832
+ _release_data_for(remove);
833
+ break;
834
+ case cmd_store:
835
+ _release_data_for(store);
836
+ break;
837
+ case cmd_stats:
838
+ _release_data_for(stats);
839
+ break;
840
+ case cmd_version:
841
+ _release_data_for(version);
842
+ break;
843
+ case cmd_observe:
844
+ _release_data_for(observe);
845
+ break;
846
+ case cmd_unlock:
847
+ _release_data_for(unlock);
848
+ break;
849
+ }
850
+ #undef _release_data_for
851
+ }
852
+
853
+ struct build_params_st
854
+ {
855
+ struct params_st *params;
856
+ int argc;
857
+ VALUE argv;
858
+ };
859
+
860
+ static VALUE
861
+ do_params_build(VALUE ptr)
862
+ {
863
+ VALUE opts;
864
+ /* unpack arguments */
865
+ struct build_params_st *p = (struct build_params_st *)ptr;
866
+ struct params_st *params = p->params;
867
+ int argc = p->argc;
868
+ VALUE argv = p->argv;
869
+
870
+ /* extract options */
871
+ if (argc > 1 && TYPE(RARRAY_PTR(argv)[argc-1]) == T_HASH) {
872
+ opts = rb_ary_pop(argv);
873
+ --argc;
874
+ } else {
875
+ opts = Qnil;
876
+ }
877
+
878
+ params->npayload = PACKET_HEADER_SIZE; /* size of packet header */
879
+ switch (params->type) {
880
+ case cmd_touch:
881
+ params->cmd.touch.quiet = params->bucket->quiet;
882
+ params->cmd.touch.ttl = params->bucket->default_ttl;
883
+ cb_params_touch_parse_options(params, opts);
884
+ cb_params_touch_parse_arguments(params, argc, argv);
885
+ break;
886
+ case cmd_remove:
887
+ params->cmd.remove.quiet = params->bucket->quiet;
888
+ if (argc == 2) {
889
+ int type = TYPE(RARRAY_PTR(argv)[1]);
890
+ if (type == T_FIXNUM || type == T_BIGNUM) {
891
+ /* allow form delete("foo", 0xdeadbeef) */
892
+ --argc;
893
+ params->cmd.remove.cas = NUM2ULL(rb_ary_pop(argv));
894
+ }
895
+ }
896
+ cb_params_remove_parse_options(params, opts);
897
+ cb_params_remove_parse_arguments(params, argc, argv);
898
+ break;
899
+ case cmd_store:
900
+ if (argc == 1 && opts != Qnil) {
901
+ /* put last hash back because it is the value */
902
+ rb_ary_push(argv, opts);
903
+ opts = Qnil;
904
+ ++argc;
905
+ }
906
+ params->cmd.store.datatype = 0x00;
907
+ params->cmd.store.ttl = params->bucket->default_ttl;
908
+ params->cmd.store.flags = flags_set_format(params->bucket->default_flags,
909
+ params->bucket->default_format);
910
+ params->cmd.store.observe = Qnil;
911
+ cb_params_store_parse_options(params, opts);
912
+ cb_params_store_parse_arguments(params, argc, argv);
913
+ break;
914
+ case cmd_get:
915
+ params->cmd.get.quiet = params->bucket->quiet;
916
+ cb_params_get_parse_options(params, opts);
917
+ cb_params_get_parse_arguments(params, argc, argv);
918
+ break;
919
+ case cmd_arith:
920
+ params->cmd.arith.delta = 1;
921
+ params->cmd.arith.format = params->bucket->default_format;
922
+ params->cmd.arith.ttl = params->bucket->default_ttl;
923
+ if (argc == 2 && TYPE(RARRAY_PTR(argv)[1]) == T_FIXNUM) {
924
+ /* allow form incr("foo", 1) */
925
+ --argc;
926
+ params->cmd.arith.delta = NUM2ULL(rb_ary_pop(argv)) & INT64_MAX;
927
+ }
928
+ cb_params_arith_parse_options(params, opts);
929
+ cb_params_arith_parse_arguments(params, argc, argv);
930
+ break;
931
+ case cmd_stats:
932
+ cb_params_stats_parse_arguments(params, argc, argv);
933
+ break;
934
+ case cmd_version:
935
+ cb_params_version_alloc(params);
936
+ break;
937
+ case cmd_observe:
938
+ cb_params_observe_parse_arguments(params, argc, argv);
939
+ break;
940
+ case cmd_unlock:
941
+ params->cmd.unlock.quiet = params->bucket->quiet;
942
+ if (argc == 2) {
943
+ int type = TYPE(RARRAY_PTR(argv)[1]);
944
+ if (type == T_FIXNUM || type == T_BIGNUM) {
945
+ /* allow form unlock("foo", 0xdeadbeef) */
946
+ --argc;
947
+ params->cmd.unlock.cas = NUM2ULL(rb_ary_pop(argv));
948
+ }
949
+ }
950
+ cb_params_unlock_parse_options(params, opts);
951
+ cb_params_unlock_parse_arguments(params, argc, argv);
952
+ break;
953
+ }
954
+
955
+ return Qnil;
956
+ }
957
+
958
+ void
959
+ cb_params_build(struct params_st *params, int argc, VALUE argv)
960
+ {
961
+ int fail = 0;
962
+ struct build_params_st args;
963
+
964
+ args.params = params;
965
+ args.argc = argc;
966
+ args.argv = argv;
967
+ rb_protect(do_params_build, (VALUE)&args, &fail);
968
+ if (fail) {
969
+ cb_params_destroy(params);
970
+ /* raise exception from protected block */
971
+ rb_jump_tag(fail);
972
+ }
973
+ }