libcouchbase 1.0.4 → 1.1.0
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.
- 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
|