libcouchbase 1.0.4 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +11 -8
- data/ext/libcouchbase/CMakeLists.txt +1 -1
- data/ext/libcouchbase/README.markdown +38 -6
- data/ext/libcouchbase/RELEASE_NOTES.markdown +151 -0
- data/ext/libcouchbase/cmake/Modules/GenerateConfigDotH.cmake +2 -2
- data/ext/libcouchbase/cmake/Modules/GetVersionInfo.cmake +3 -3
- data/ext/libcouchbase/cmake/source_files.cmake +1 -0
- data/ext/libcouchbase/contrib/cJSON/cJSON.c +686 -288
- data/ext/libcouchbase/contrib/cJSON/cJSON.h +0 -0
- data/ext/libcouchbase/contrib/cbsasl/src/hash.c +17 -17
- data/ext/libcouchbase/contrib/cliopts/cliopts.c +76 -0
- data/ext/libcouchbase/contrib/cliopts/cliopts.h +66 -15
- data/ext/libcouchbase/contrib/genhash/genhash.c +1 -2
- data/ext/libcouchbase/contrib/lcb-jsoncpp/lcb-jsoncpp.cpp +4 -3
- data/ext/libcouchbase/example/instancepool/main.cc +12 -2
- data/ext/libcouchbase/example/libeventdirect/main.c +99 -25
- data/ext/libcouchbase/example/minimal/minimal.c +7 -5
- data/ext/libcouchbase/example/observe/durability.c +102 -0
- data/ext/libcouchbase/example/observe/observe.c +19 -6
- data/ext/libcouchbase/example/subdoc/subdoc-xattrs.c +1 -2
- data/ext/libcouchbase/include/libcouchbase/cntl-private.h +6 -8
- data/ext/libcouchbase/include/libcouchbase/cntl.h +84 -64
- data/ext/libcouchbase/include/libcouchbase/couchbase.h +295 -78
- data/ext/libcouchbase/include/libcouchbase/deprecated.h +2 -2
- data/ext/libcouchbase/include/libcouchbase/error.h +1 -1
- data/ext/libcouchbase/include/libcouchbase/iops.h +9 -9
- data/ext/libcouchbase/include/libcouchbase/ixmgmt.h +2 -2
- data/ext/libcouchbase/include/libcouchbase/n1ql.h +69 -7
- data/ext/libcouchbase/include/libcouchbase/vbucket.h +17 -0
- data/ext/libcouchbase/include/libcouchbase/views.h +3 -3
- data/ext/libcouchbase/include/memcached/protocol_binary.h +62 -1
- data/ext/libcouchbase/packaging/deb/control +1 -1
- data/ext/libcouchbase/packaging/rpm/libcouchbase.spec.in +37 -36
- data/ext/libcouchbase/src/bootstrap.cc +22 -8
- data/ext/libcouchbase/src/bucketconfig/bc_cccp.cc +1 -1
- data/ext/libcouchbase/src/bucketconfig/bc_http.cc +0 -1
- data/ext/libcouchbase/src/bucketconfig/confmon.cc +13 -8
- data/ext/libcouchbase/src/callbacks.c +2 -0
- data/ext/libcouchbase/src/cntl.cc +28 -17
- data/ext/libcouchbase/src/dns-srv.cc +1 -2
- data/ext/libcouchbase/src/dump.cc +4 -0
- data/ext/libcouchbase/src/errmap.h +89 -16
- data/ext/libcouchbase/src/handler.cc +28 -11
- data/ext/libcouchbase/src/http/http-priv.h +4 -1
- data/ext/libcouchbase/src/http/http.cc +3 -0
- data/ext/libcouchbase/src/instance.cc +1 -1
- data/ext/libcouchbase/src/internal.h +1 -0
- data/ext/libcouchbase/src/lcbio/connect.cc +2 -3
- data/ext/libcouchbase/src/lcbio/manager.cc +2 -2
- data/ext/libcouchbase/src/lcbio/ssl.h +10 -0
- data/ext/libcouchbase/src/mc/mcreq.c +8 -0
- data/ext/libcouchbase/src/mcserver/mcserver.cc +14 -1
- data/ext/libcouchbase/src/n1ql/ixmgmt.cc +0 -3
- data/ext/libcouchbase/src/n1ql/n1ql.cc +22 -29
- data/ext/libcouchbase/src/n1ql/params.cc +46 -1
- data/ext/libcouchbase/src/newconfig.cc +4 -4
- data/ext/libcouchbase/src/operations/durability-seqno.cc +4 -0
- data/ext/libcouchbase/src/operations/durability.cc +3 -0
- data/ext/libcouchbase/src/operations/ping.cc +315 -0
- data/ext/libcouchbase/src/operations/stats.cc +10 -0
- data/ext/libcouchbase/src/operations/subdoc.cc +13 -1
- data/ext/libcouchbase/src/retrychk.cc +1 -0
- data/ext/libcouchbase/src/settings.c +2 -0
- data/ext/libcouchbase/src/settings.h +13 -7
- data/ext/libcouchbase/src/ssl/ssl_c.c +28 -2
- data/ext/libcouchbase/src/ssl/ssl_common.c +3 -0
- data/ext/libcouchbase/src/ssl/ssl_e.c +15 -1
- data/ext/libcouchbase/src/ssl/ssl_iot_common.h +3 -1
- data/ext/libcouchbase/src/timings.c +0 -1
- data/ext/libcouchbase/src/vbucket/vbucket.c +49 -1
- data/ext/libcouchbase/tests/iotests/mock-environment.cc +58 -40
- data/ext/libcouchbase/tests/iotests/mock-environment.h +23 -4
- data/ext/libcouchbase/tests/iotests/mock-unit-test.h +8 -8
- data/ext/libcouchbase/tests/iotests/t_behavior.cc +5 -5
- data/ext/libcouchbase/tests/iotests/t_durability.cc +50 -0
- data/ext/libcouchbase/tests/iotests/t_eerrs.cc +4 -2
- data/ext/libcouchbase/tests/iotests/t_errmap.cc +6 -3
- data/ext/libcouchbase/tests/iotests/t_lock.cc +5 -6
- data/ext/libcouchbase/tests/iotests/t_misc.cc +44 -0
- data/ext/libcouchbase/tests/iotests/t_serverops.cc +1 -0
- data/ext/libcouchbase/tests/iotests/t_subdoc.cc +28 -0
- data/ext/libcouchbase/tests/iotests/t_views.cc +22 -10
- data/ext/libcouchbase/tools/CMakeLists.txt +21 -1
- data/ext/libcouchbase/tools/cbc-handlers.h +23 -3
- data/ext/libcouchbase/tools/cbc-n1qlback.cc +1 -1
- data/ext/libcouchbase/tools/cbc-pillowfight.cc +126 -26
- data/ext/libcouchbase/tools/cbc-proxy.cc +403 -0
- data/ext/libcouchbase/tools/cbc-subdoc.cc +826 -0
- data/ext/libcouchbase/tools/cbc.cc +149 -37
- data/ext/libcouchbase/tools/common/options.h +5 -2
- data/ext/libcouchbase/tools/linenoise/linenoise.c +15 -15
- data/lib/libcouchbase.rb +4 -0
- data/lib/libcouchbase/bucket.rb +51 -0
- data/lib/libcouchbase/connection.rb +100 -13
- data/lib/libcouchbase/ext/libcouchbase.rb +40 -0
- data/lib/libcouchbase/ext/libcouchbase/cmdsubdoc.rb +13 -1
- data/lib/libcouchbase/ext/libcouchbase/enums.rb +2 -1
- data/lib/libcouchbase/ext/libcouchbase/sdspec.rb +5 -0
- data/lib/libcouchbase/subdoc_request.rb +129 -0
- data/lib/libcouchbase/version.rb +1 -1
- data/spec/bucket_spec.rb +15 -1
- data/spec/connection_spec.rb +1 -1
- data/spec/subdoc_spec.rb +192 -0
- metadata +13 -4
- data/ext/libcouchbase/.travis.yml +0 -19
data/lib/libcouchbase.rb
CHANGED
@@ -8,6 +8,9 @@ module Libcouchbase
|
|
8
8
|
require 'libcouchbase/callbacks'
|
9
9
|
require 'libcouchbase/connection'
|
10
10
|
|
11
|
+
DefaultOpts = Struct.new(:host, :bucket, :username, :password)
|
12
|
+
Defaults = DefaultOpts.new('127.0.0.1', 'default')
|
13
|
+
|
11
14
|
class Results
|
12
15
|
include Enumerable
|
13
16
|
|
@@ -33,4 +36,5 @@ module Libcouchbase
|
|
33
36
|
autoload :ResultsEM, 'libcouchbase/results_fiber'
|
34
37
|
autoload :ResultsLibuv, 'libcouchbase/results_fiber'
|
35
38
|
autoload :ResultsNative, 'libcouchbase/results_native'
|
39
|
+
autoload :SubdocRequest, 'libcouchbase/subdoc_request'
|
36
40
|
end
|
data/lib/libcouchbase/bucket.rb
CHANGED
@@ -164,6 +164,15 @@ module Libcouchbase
|
|
164
164
|
get(key, quiet: true)
|
165
165
|
end
|
166
166
|
|
167
|
+
# A helper method for returning a default value if one doesn't exist for the key
|
168
|
+
def fetch(key, value = nil, async: false, **opts)
|
169
|
+
cached_obj = get(key, quiet: true, async: false, extended: false)
|
170
|
+
return cached_obj if cached_obj
|
171
|
+
value = value || yield
|
172
|
+
set(key, value, opts.merge(async: false, extended: false))
|
173
|
+
value
|
174
|
+
end
|
175
|
+
|
167
176
|
# Add the item to the database, but fail if the object exists already
|
168
177
|
#
|
169
178
|
# @param key [String, Symbol] Key used to reference the value.
|
@@ -534,6 +543,48 @@ module Libcouchbase
|
|
534
543
|
result @connection.touch(**opts), async
|
535
544
|
end
|
536
545
|
|
546
|
+
# Perform subdocument operations on a key.
|
547
|
+
#
|
548
|
+
# Yields a request builder to a block and applies the operations performed
|
549
|
+
#
|
550
|
+
# @param [String, Symbol] key
|
551
|
+
#
|
552
|
+
# @yieldparam [Libcouchbase::SubdocRequest] the subdocument request object used to define the request
|
553
|
+
#
|
554
|
+
# @example Perform a subdocument operation using a block
|
555
|
+
# c.subdoc(:foo) { |subdoc|
|
556
|
+
# subdoc.get('sub.key')
|
557
|
+
# subdoc.exists?('other.key')
|
558
|
+
# subdoc.get_count('some.array')
|
559
|
+
# } # => ["sub key val", true, 23]
|
560
|
+
#
|
561
|
+
# @example perform a subdocument operation using execute!
|
562
|
+
# c.subdoc(:foo).get(:bob).execute! # => { age: 13, working: false }
|
563
|
+
#
|
564
|
+
# @example perform multiple subdocument operations using execute!
|
565
|
+
# c.subdoc(:foo)
|
566
|
+
# .get(:bob).get(:jane).execute! # => [{ age: 13, working: false }, { age: 47, working: true }]
|
567
|
+
#
|
568
|
+
# @example perform a subdocument mutation operation
|
569
|
+
# c.subdoc(:foo).counter('bob.age', 1).execute! # => 14
|
570
|
+
def subdoc(key, quiet: @quiet, **opts)
|
571
|
+
if block_given?
|
572
|
+
sd = SubdocRequest.new(key, quiet)
|
573
|
+
yield sd
|
574
|
+
subdoc_execute!(sd, opts)
|
575
|
+
else
|
576
|
+
SubdocRequest.new(key, quiet, bucket: self, exec_opts: opts)
|
577
|
+
end
|
578
|
+
end
|
579
|
+
|
580
|
+
def subdoc_execute!(sd, extended: false, async: false, **opts)
|
581
|
+
promise = @connection.subdoc(sd, opts).then { |resp|
|
582
|
+
raise resp.value if resp.value.is_a?(::Exception)
|
583
|
+
extended ? resp : resp.value
|
584
|
+
}
|
585
|
+
result promise, async
|
586
|
+
end
|
587
|
+
|
537
588
|
# Fetch design docs stored in current bucket
|
538
589
|
#
|
539
590
|
# @return [Libcouchbase::DesignDocs]
|
@@ -40,6 +40,8 @@ module Libcouchbase
|
|
40
40
|
define_callback function: :callback_remove
|
41
41
|
define_callback function: :callback_cbflush
|
42
42
|
define_callback function: :callback_http
|
43
|
+
define_callback function: :callback_sdlookup # subdoc lookup
|
44
|
+
define_callback function: :callback_sdmutate
|
43
45
|
|
44
46
|
# These are passed with the request
|
45
47
|
define_callback function: :viewquery_callback
|
@@ -58,7 +60,7 @@ module Libcouchbase
|
|
58
60
|
end
|
59
61
|
|
60
62
|
|
61
|
-
def initialize(hosts:
|
63
|
+
def initialize(hosts: Defaults.host, bucket: Defaults.bucket, username: Defaults.username, password: Defaults.password, thread: nil, **opts)
|
62
64
|
# build host string http://docs.couchbase.com/sdk-api/couchbase-c-client-2.5.6/group__lcb-init.html
|
63
65
|
hosts = Array(hosts).flatten.join(',')
|
64
66
|
connstr = "couchbase://#{hosts}/#{bucket}"
|
@@ -95,7 +97,7 @@ module Libcouchbase
|
|
95
97
|
@connection = Ext::CreateSt.new
|
96
98
|
@connection[:version] = 3
|
97
99
|
@connection[:v][:v3][:connstr] = FFI::MemoryPointer.from_string(connstr)
|
98
|
-
@connection[:v][:v3][:username] = FFI::MemoryPointer.from_string(bucket.to_s)
|
100
|
+
@connection[:v][:v3][:username] = FFI::MemoryPointer.from_string(username&.to_s || bucket.to_s)
|
99
101
|
@connection[:v][:v3][:passwd] = FFI::MemoryPointer.from_string(password) if password
|
100
102
|
@connection[:v][:v3][:io] = @io_ptr.get_pointer(0)
|
101
103
|
@handle_ptr = FFI::MemoryPointer.new :pointer, 1
|
@@ -133,15 +135,17 @@ module Libcouchbase
|
|
133
135
|
# Register the callbacks we are interested in
|
134
136
|
Ext.set_bootstrap_callback(@handle, callback(:bootstrap_callback))
|
135
137
|
|
136
|
-
Ext.install_callback3(@handle, Ext::CALLBACKTYPE[:callback_get],
|
137
|
-
Ext.install_callback3(@handle, Ext::CALLBACKTYPE[:callback_unlock],
|
138
|
-
Ext.install_callback3(@handle, Ext::CALLBACKTYPE[:callback_store],
|
139
|
-
Ext.install_callback3(@handle, Ext::CALLBACKTYPE[:callback_storedur],callback(:callback_storedur))
|
140
|
-
Ext.install_callback3(@handle, Ext::CALLBACKTYPE[:callback_counter],
|
141
|
-
Ext.install_callback3(@handle, Ext::CALLBACKTYPE[:callback_touch],
|
142
|
-
Ext.install_callback3(@handle, Ext::CALLBACKTYPE[:callback_remove],
|
143
|
-
Ext.install_callback3(@handle, Ext::CALLBACKTYPE[:callback_http],
|
144
|
-
Ext.install_callback3(@handle, Ext::CALLBACKTYPE[:
|
138
|
+
Ext.install_callback3(@handle, Ext::CALLBACKTYPE[:callback_get], callback(:callback_get))
|
139
|
+
Ext.install_callback3(@handle, Ext::CALLBACKTYPE[:callback_unlock], callback(:callback_unlock))
|
140
|
+
Ext.install_callback3(@handle, Ext::CALLBACKTYPE[:callback_store], callback(:callback_store))
|
141
|
+
Ext.install_callback3(@handle, Ext::CALLBACKTYPE[:callback_storedur], callback(:callback_storedur))
|
142
|
+
Ext.install_callback3(@handle, Ext::CALLBACKTYPE[:callback_counter], callback(:callback_counter))
|
143
|
+
Ext.install_callback3(@handle, Ext::CALLBACKTYPE[:callback_touch], callback(:callback_touch))
|
144
|
+
Ext.install_callback3(@handle, Ext::CALLBACKTYPE[:callback_remove], callback(:callback_remove))
|
145
|
+
Ext.install_callback3(@handle, Ext::CALLBACKTYPE[:callback_http], callback(:callback_http))
|
146
|
+
Ext.install_callback3(@handle, Ext::CALLBACKTYPE[:callback_sdlookup], callback(:callback_sdlookup))
|
147
|
+
Ext.install_callback3(@handle, Ext::CALLBACKTYPE[:callback_sdmutate], callback(:callback_sdmutate))
|
148
|
+
Ext.install_callback3(@handle, Ext::CALLBACKTYPE[:callback_cbflush], callback(:callback_cbflush)) if @flush_enabled
|
145
149
|
|
146
150
|
# Connect to the database
|
147
151
|
err = Ext.connect(@handle)
|
@@ -453,6 +457,31 @@ module Libcouchbase
|
|
453
457
|
defer.promise
|
454
458
|
end
|
455
459
|
|
460
|
+
def subdoc(request, expire_in: nil, ttl: nil, expire_at: nil, cas: nil, **opts)
|
461
|
+
raise 'not connected' unless @handle
|
462
|
+
defer ||= @reactor.defer
|
463
|
+
|
464
|
+
cmd = Ext::CMDSUBDOC.new
|
465
|
+
req = Request.new(cmd, defer, request.key, request)
|
466
|
+
key = cmd_set_key(req, cmd, request.key)
|
467
|
+
|
468
|
+
cmd[:multimode] = request.mode == :mutate ? Ext::CMDSUBDOC::SDMULTI_MODE_MUTATE : Ext::CMDSUBDOC::SDMULTI_MODE_LOOKUP
|
469
|
+
cmd[:specs], cmd[:nspecs] = request.to_specs_array
|
470
|
+
|
471
|
+
cmd[:cas] = cas if cas
|
472
|
+
expire_in ||= ttl
|
473
|
+
cmd[:exptime] = expire_in ? expires_in(expire_in) : expire_at.to_i
|
474
|
+
|
475
|
+
@reactor.schedule {
|
476
|
+
pointer = cmd.to_ptr
|
477
|
+
@requests[pointer.address] = req
|
478
|
+
check_error(key, defer, Ext.subdoc3(@handle, pointer, cmd), subdoc: true)
|
479
|
+
request.free_memory
|
480
|
+
}
|
481
|
+
|
482
|
+
defer.promise
|
483
|
+
end
|
484
|
+
|
456
485
|
# http://docs.couchbase.com/sdk-api/couchbase-c-client-2.6.2/group__lcb-flush.html
|
457
486
|
def flush(defer: nil)
|
458
487
|
raise 'not connected' unless @handle
|
@@ -608,7 +637,7 @@ module Libcouchbase
|
|
608
637
|
end
|
609
638
|
end
|
610
639
|
|
611
|
-
def check_error(key, defer, err)
|
640
|
+
def check_error(key, defer, err, subdoc: false)
|
612
641
|
if err != :success
|
613
642
|
error = Error.lookup(err).new("request not scheduled for #{key}")
|
614
643
|
backtrace = caller
|
@@ -717,6 +746,63 @@ module Libcouchbase
|
|
717
746
|
end
|
718
747
|
end
|
719
748
|
|
749
|
+
def callback_sdlookup(handle, type, response)
|
750
|
+
resp = Ext::RESPSUBDOC.new response
|
751
|
+
resp_callback_common(resp, :callback_sdlookup) do |req, cb|
|
752
|
+
subdoc_common(resp, req, cb)
|
753
|
+
end
|
754
|
+
end
|
755
|
+
|
756
|
+
# Only counter returns a result
|
757
|
+
def callback_sdmutate(handle, type, response)
|
758
|
+
resp = Ext::RESPSUBDOC.new response
|
759
|
+
resp_callback_common(resp, :callback_sdmutate) do |req, cb|
|
760
|
+
subdoc_common(resp, req, cb)
|
761
|
+
end
|
762
|
+
end
|
763
|
+
|
764
|
+
def subdoc_common(resp, req, cb)
|
765
|
+
iterval = FFI::MemoryPointer.new(:ulong, 1)
|
766
|
+
cur_res = Ext::SDENTRY.new
|
767
|
+
values = []
|
768
|
+
index = 0
|
769
|
+
|
770
|
+
ignore = req.value.ignore
|
771
|
+
mutation = req.value.mode == :mutate
|
772
|
+
|
773
|
+
loop do
|
774
|
+
check = Ext.sdresult_next(resp, cur_res, iterval)
|
775
|
+
break if check == 0
|
776
|
+
|
777
|
+
if cur_res[:status] == :success
|
778
|
+
count = cur_res[:nvalue]
|
779
|
+
if count > 0
|
780
|
+
result = cur_res[:value].read_string(count)
|
781
|
+
else
|
782
|
+
result = true # success response
|
783
|
+
end
|
784
|
+
result = "[#{result}]"
|
785
|
+
values << JSON.parse(result, DECODE_OPTIONS)[0]
|
786
|
+
elsif cur_res[:status] == :subdoc_path_enoent && ignore[mutation ? cur_res[:index] : index]
|
787
|
+
values << nil
|
788
|
+
else
|
789
|
+
values << Error.lookup(cur_res[:status]).new("Subdoc #{cb} failed for #{req.key} index #{mutation ? cur_res[:index] : index}")
|
790
|
+
end
|
791
|
+
|
792
|
+
index += 1
|
793
|
+
end
|
794
|
+
|
795
|
+
# Return the single result instead of an array if single
|
796
|
+
is_single = resp[:rflags] & Ext::RESPFLAGS[:resp_f_sdsingle] > 0
|
797
|
+
if is_single
|
798
|
+
values = values.first
|
799
|
+
elsif values.empty? # multiple mutate arrays should return true (same as a single mutate)
|
800
|
+
values = true
|
801
|
+
end
|
802
|
+
|
803
|
+
Response.new(cb, req.key, resp[:cas], values)
|
804
|
+
end
|
805
|
+
|
720
806
|
def callback_cbflush(handle, type, response)
|
721
807
|
resp = Ext::RESPBASE.new response
|
722
808
|
resp_callback_common(resp, :callback_cbflush) do |req, cb|
|
@@ -756,7 +842,8 @@ module Libcouchbase
|
|
756
842
|
req = @requests.delete(resp[:cookie].address)
|
757
843
|
if req
|
758
844
|
begin
|
759
|
-
|
845
|
+
# Errors will be provided in the response
|
846
|
+
if resp[:rc] == :success || resp[:rc] == :subdoc_multi_failure
|
760
847
|
req.defer.resolve(yield(req, callback))
|
761
848
|
else
|
762
849
|
req.defer.reject(Error.lookup(resp[:rc]).new("#{callback} failed for #{req.key}"))
|
@@ -1016,6 +1016,46 @@ module Libcouchbase::Ext
|
|
1016
1016
|
#
|
1017
1017
|
attach_function :n1p_posparam, :lcb_n1p_posparam, [N1QLPARAMS.by_ref, :string, :ulong], ErrorT
|
1018
1018
|
|
1019
|
+
# (Not documented)
|
1020
|
+
#
|
1021
|
+
# @method n1p_readonly(params, readonly)
|
1022
|
+
# @param [N1QLPARAMS] params
|
1023
|
+
# @param [Integer] readonly if non-zero, the query will be read-only
|
1024
|
+
# @return [ErrorT]
|
1025
|
+
# @scope class
|
1026
|
+
#
|
1027
|
+
attach_function :n1p_readonly, :lcb_n1p_readonly, [N1QLPARAMS.by_ref, :int], ErrorT
|
1028
|
+
|
1029
|
+
# (Not documented)
|
1030
|
+
#
|
1031
|
+
# @method n1p_readonly(params, readonly)
|
1032
|
+
# @param [N1QLPARAMS] params
|
1033
|
+
# @param [Integer] Sets maximum buffered channel size. Use 0 or a negative number to disable.
|
1034
|
+
# @return [ErrorT]
|
1035
|
+
# @scope class
|
1036
|
+
#
|
1037
|
+
attach_function :n1p_scancap, :lcb_n1p_scancap, [N1QLPARAMS.by_ref, :int], ErrorT
|
1038
|
+
|
1039
|
+
# (Not documented)
|
1040
|
+
#
|
1041
|
+
# @method n1p_readonly(params, readonly)
|
1042
|
+
# @param [N1QLPARAMS] params
|
1043
|
+
# @param [Integer] maximum number of items each execution operator can buffer between operators.
|
1044
|
+
# @return [ErrorT]
|
1045
|
+
# @scope class
|
1046
|
+
#
|
1047
|
+
attach_function :n1p_pipelinecap, :lcb_n1p_pipelinecap, [N1QLPARAMS.by_ref, :int], ErrorT
|
1048
|
+
|
1049
|
+
# (Not documented)
|
1050
|
+
#
|
1051
|
+
# @method n1p_readonly(params, readonly)
|
1052
|
+
# @param [N1QLPARAMS] params
|
1053
|
+
# @param [Integer] number of items execution operators can batch for fetch from the KV
|
1054
|
+
# @return [ErrorT]
|
1055
|
+
# @scope class
|
1056
|
+
#
|
1057
|
+
attach_function :n1p_pipelinebatch, :lcb_n1p_pipelinebatch, [N1QLPARAMS.by_ref, :int], ErrorT
|
1058
|
+
|
1019
1059
|
# (Not documented)
|
1020
1060
|
#
|
1021
1061
|
# @method n1p_setopt(params, name, n_name, value, n_value)
|
@@ -35,14 +35,26 @@ module Libcouchbase::Ext
|
|
35
35
|
# This field may be left empty, in which case the mode is implicitly
|
36
36
|
# derived from the _first_ command issued.
|
37
37
|
class CMDSUBDOC < FFI::Struct
|
38
|
+
# CMD flags
|
39
|
+
UPSERT_DOC = (1<<16) # document is to be created if it does not exist.
|
40
|
+
INSERT_DOC = (1<<17) # document must be created anew. Fail if it exists
|
41
|
+
ACCESS_DELETED = (1<<18) # Access a potentially deleted document.
|
42
|
+
|
43
|
+
SDMULTI_MODE_INVALID = 0
|
44
|
+
SDMULTI_MODE_LOOKUP = 1
|
45
|
+
SDMULTI_MODE_MUTATE = 2
|
46
|
+
|
38
47
|
layout :cmdflags, :uint,
|
39
48
|
:exptime, :uint,
|
40
49
|
:cas, :ulong_long,
|
41
50
|
:key, KEYBUF.by_value,
|
42
51
|
:hashkey, KEYBUF.by_value,
|
43
|
-
:specs, SDSPEC.by_ref,
|
52
|
+
:specs, :pointer, # ==> SDSPEC.by_ref,
|
44
53
|
:nspecs, :ulong,
|
45
54
|
:error_index, :pointer,
|
55
|
+
|
56
|
+
# This can either be SDMULTI_MODE_LOOKUP or SDMULTI_MODE_MUTATE
|
57
|
+
# This field may be left empty, in which case the mode is implicitly derived from the _first_ command issued.
|
46
58
|
:multimode, :uint
|
47
59
|
end
|
48
60
|
|
@@ -17,6 +17,11 @@ module Libcouchbase::Ext
|
|
17
17
|
# @ref LCB_SDSPEC_SET_VALUE. The contents of the value should be valid
|
18
18
|
# until the operation is scheduled (i.e. lcb_subdoc3())
|
19
19
|
class SDSPEC < FFI::Struct
|
20
|
+
MKINTERMEDIATES = (1<<16) # Create intermediate paths
|
21
|
+
XATTRPATH = (1<<18) # Access document XATTR path
|
22
|
+
XATTR_MACROVALUES = (1<<19) # Access document virtual/materialized path
|
23
|
+
XATTR_DELETED_OK = (1<<20) # Access Xattrs of deleted documents
|
24
|
+
|
20
25
|
layout :sdcmd, :uint,
|
21
26
|
:options, :uint,
|
22
27
|
:path, KEYBUF.by_value,
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true, encoding: ASCII-8BIT
|
2
|
+
|
3
|
+
module Libcouchbase; end;
|
4
|
+
class Libcouchbase::SubdocRequest
|
5
|
+
|
6
|
+
def initialize(key, quiet, bucket: nil, exec_opts: nil)
|
7
|
+
@key = key.to_s
|
8
|
+
raise ArgumentError.new("invalid document key #{key.inspect}") unless @key.length > 0
|
9
|
+
@refs = []
|
10
|
+
@mode = nil
|
11
|
+
@quiet = quiet
|
12
|
+
@specs = []
|
13
|
+
@ignore = []
|
14
|
+
|
15
|
+
@bucket = bucket
|
16
|
+
@exec_opts = exec_opts
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :mode, :key, :ignore
|
20
|
+
|
21
|
+
# Internal use only
|
22
|
+
def to_specs_array
|
23
|
+
return @mem if @mem # effectively freezes this object
|
24
|
+
number = @specs.length
|
25
|
+
@mem = FFI::MemoryPointer.new(::Libcouchbase::Ext::SDSPEC, number, false)
|
26
|
+
@specs.each_with_index do |spec, index|
|
27
|
+
struct_bytes = spec.to_ptr.get_bytes(0, ::Libcouchbase::Ext::SDSPEC.size) # (offset, length)
|
28
|
+
@mem[index].put_bytes(0, struct_bytes) # (offset, byte_string)
|
29
|
+
end
|
30
|
+
@specs = nil
|
31
|
+
[@mem, number]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Internal use only
|
35
|
+
def free_memory
|
36
|
+
@refs = nil
|
37
|
+
@mem = nil
|
38
|
+
end
|
39
|
+
|
40
|
+
# When not used in block form
|
41
|
+
def execute!(**opts)
|
42
|
+
opts = @exec_opts.merge(opts)
|
43
|
+
@exec_opts = nil
|
44
|
+
bucket = @bucket
|
45
|
+
@bucket = nil
|
46
|
+
bucket.subdoc_execute!(self, **opts)
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
# =========
|
51
|
+
# Lookups
|
52
|
+
# =========
|
53
|
+
|
54
|
+
[ :get, :exists, :get_count ].each do |cmd|
|
55
|
+
command = :"sdcmd_#{cmd}"
|
56
|
+
define_method cmd do |path, quiet: nil, **opts|
|
57
|
+
quiet = @quiet if quiet.nil?
|
58
|
+
new_spec(quiet, path, command, :lookup)
|
59
|
+
self
|
60
|
+
end
|
61
|
+
end
|
62
|
+
alias_method :exists?, :exists
|
63
|
+
|
64
|
+
|
65
|
+
# ===========
|
66
|
+
# Mutations
|
67
|
+
# ===========
|
68
|
+
|
69
|
+
def remove(path, quiet: nil, **opts)
|
70
|
+
quiet = @quiet if quiet.nil?
|
71
|
+
new_spec(quiet, path, :sdcmd_remove, :mutate)
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
[
|
76
|
+
:replace, :dict_add, :dict_upsert, :array_add_first, :array_add_last,
|
77
|
+
:array_add_unique, :array_insert, :counter
|
78
|
+
].each do |cmd|
|
79
|
+
command = :"sdcmd_#{cmd}"
|
80
|
+
define_method cmd do |path, value, create_intermediates: true, **opts|
|
81
|
+
spec = new_spec(false, path, command, :mutate, create_intermediates)
|
82
|
+
set_value(spec, value)
|
83
|
+
self
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
protected
|
89
|
+
|
90
|
+
|
91
|
+
# http://docs.couchbase.com/sdk-api/couchbase-c-client-2.8.2/group__lcb-subdoc.html#ga53e89dd6b480e81b82fb305d04d92e18
|
92
|
+
def new_spec(quiet, path, cmd, mode, create_intermediates = false)
|
93
|
+
@mode ||= mode
|
94
|
+
raise "unable to perform #{cmd} as mode is currently #{@mode}" if @mode != mode
|
95
|
+
|
96
|
+
spec = ::Libcouchbase::Ext::SDSPEC.new
|
97
|
+
spec[:sdcmd] = ::Libcouchbase::Ext::SUBDOCOP[cmd]
|
98
|
+
spec[:options] = ::Libcouchbase::Ext::SDSPEC::MKINTERMEDIATES if create_intermediates
|
99
|
+
|
100
|
+
loc = path.to_s
|
101
|
+
str = ref(loc)
|
102
|
+
spec[:path][:type] = :kv_copy
|
103
|
+
spec[:path][:contig][:bytes] = str
|
104
|
+
spec[:path][:contig][:nbytes] = loc.bytesize
|
105
|
+
|
106
|
+
@ignore << quiet
|
107
|
+
@specs << spec
|
108
|
+
spec
|
109
|
+
end
|
110
|
+
|
111
|
+
# http://docs.couchbase.com/sdk-api/couchbase-c-client-2.8.2/group__lcb-subdoc.html#ga61009762f6b23ae2a9685ddb888dc406
|
112
|
+
def set_value(spec, value)
|
113
|
+
# Create a JSON version of the value.
|
114
|
+
# We throw it into an array so strings and numbers etc are valid, then we remove the array.
|
115
|
+
val = [value].to_json[1...-1]
|
116
|
+
str = ref(val)
|
117
|
+
spec[:value][:vtype] = :kv_copy
|
118
|
+
spec[:value][:u_buf][:contig][:bytes] = str
|
119
|
+
spec[:value][:u_buf][:contig][:nbytes] = val.bytesize
|
120
|
+
value
|
121
|
+
end
|
122
|
+
|
123
|
+
# We need to hold a reference to c-strings so they are not GC'd
|
124
|
+
def ref(string)
|
125
|
+
str = ::FFI::MemoryPointer.from_string(string)
|
126
|
+
@refs << str
|
127
|
+
str
|
128
|
+
end
|
129
|
+
end
|