libcouchbase-mapo 1.4.1
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 +7 -0
- data/.gitignore +20 -0
- data/.gitmodules +3 -0
- data/.rspec +1 -0
- data/.travis.yml +38 -0
- data/Gemfile +4 -0
- data/LICENSE +24 -0
- data/README.md +445 -0
- data/Rakefile +76 -0
- data/ext/README.md +6 -0
- data/ext/Rakefile +19 -0
- data/lib/libcouchbase.rb +40 -0
- data/lib/libcouchbase/bucket.rb +825 -0
- data/lib/libcouchbase/callbacks.rb +69 -0
- data/lib/libcouchbase/connection.rb +886 -0
- data/lib/libcouchbase/design_docs.rb +92 -0
- data/lib/libcouchbase/error.rb +68 -0
- data/lib/libcouchbase/ext/libcouchbase.rb +1175 -0
- data/lib/libcouchbase/ext/libcouchbase/cmdbase.rb +23 -0
- data/lib/libcouchbase/ext/libcouchbase/cmdcounter.rb +36 -0
- data/lib/libcouchbase/ext/libcouchbase/cmdendure.rb +26 -0
- data/lib/libcouchbase/ext/libcouchbase/cmdfts.rb +24 -0
- data/lib/libcouchbase/ext/libcouchbase/cmdget.rb +30 -0
- data/lib/libcouchbase/ext/libcouchbase/cmdgetreplica.rb +49 -0
- data/lib/libcouchbase/ext/libcouchbase/cmdhttp.rb +58 -0
- data/lib/libcouchbase/ext/libcouchbase/cmdn1ql.rb +40 -0
- data/lib/libcouchbase/ext/libcouchbase/cmdobseqno.rb +33 -0
- data/lib/libcouchbase/ext/libcouchbase/cmdobserve.rb +30 -0
- data/lib/libcouchbase/ext/libcouchbase/cmdstore.rb +40 -0
- data/lib/libcouchbase/ext/libcouchbase/cmdstoredur.rb +45 -0
- data/lib/libcouchbase/ext/libcouchbase/cmdsubdoc.rb +61 -0
- data/lib/libcouchbase/ext/libcouchbase/cmdverbosity.rb +29 -0
- data/lib/libcouchbase/ext/libcouchbase/cmdviewquery.rb +61 -0
- data/lib/libcouchbase/ext/libcouchbase/contigbuf.rb +14 -0
- data/lib/libcouchbase/ext/libcouchbase/create_st.rb +15 -0
- data/lib/libcouchbase/ext/libcouchbase/create_st0.rb +23 -0
- data/lib/libcouchbase/ext/libcouchbase/create_st1.rb +26 -0
- data/lib/libcouchbase/ext/libcouchbase/create_st2.rb +32 -0
- data/lib/libcouchbase/ext/libcouchbase/create_st3.rb +26 -0
- data/lib/libcouchbase/ext/libcouchbase/crst_u.rb +20 -0
- data/lib/libcouchbase/ext/libcouchbase/durability_opts_st_v.rb +11 -0
- data/lib/libcouchbase/ext/libcouchbase/durability_opts_t.rb +14 -0
- data/lib/libcouchbase/ext/libcouchbase/durabilityopt_sv0.rb +63 -0
- data/lib/libcouchbase/ext/libcouchbase/enums.rb +1007 -0
- data/lib/libcouchbase/ext/libcouchbase/fragbuf.rb +18 -0
- data/lib/libcouchbase/ext/libcouchbase/ftshandle.rb +7 -0
- data/lib/libcouchbase/ext/libcouchbase/histogram.rb +34 -0
- data/lib/libcouchbase/ext/libcouchbase/http_request_t.rb +7 -0
- data/lib/libcouchbase/ext/libcouchbase/keybuf.rb +20 -0
- data/lib/libcouchbase/ext/libcouchbase/multicmd_ctx.rb +30 -0
- data/lib/libcouchbase/ext/libcouchbase/mutation_token.rb +17 -0
- data/lib/libcouchbase/ext/libcouchbase/n1qlhandle.rb +7 -0
- data/lib/libcouchbase/ext/libcouchbase/n1qlparams.rb +7 -0
- data/lib/libcouchbase/ext/libcouchbase/respbase.rb +29 -0
- data/lib/libcouchbase/ext/libcouchbase/respcounter.rb +32 -0
- data/lib/libcouchbase/ext/libcouchbase/respendure.rb +49 -0
- data/lib/libcouchbase/ext/libcouchbase/respfts.rb +40 -0
- data/lib/libcouchbase/ext/libcouchbase/respget.rb +44 -0
- data/lib/libcouchbase/ext/libcouchbase/resphttp.rb +48 -0
- data/lib/libcouchbase/ext/libcouchbase/respmcversion.rb +38 -0
- data/lib/libcouchbase/ext/libcouchbase/respn1ql.rb +41 -0
- data/lib/libcouchbase/ext/libcouchbase/respobseqno.rb +52 -0
- data/lib/libcouchbase/ext/libcouchbase/respobserve.rb +41 -0
- data/lib/libcouchbase/ext/libcouchbase/respserverbase.rb +32 -0
- data/lib/libcouchbase/ext/libcouchbase/respstats.rb +38 -0
- data/lib/libcouchbase/ext/libcouchbase/respstore.rb +32 -0
- data/lib/libcouchbase/ext/libcouchbase/respstoredur.rb +38 -0
- data/lib/libcouchbase/ext/libcouchbase/respsubdoc.rb +35 -0
- data/lib/libcouchbase/ext/libcouchbase/respviewquery.rb +67 -0
- data/lib/libcouchbase/ext/libcouchbase/sdentry.rb +22 -0
- data/lib/libcouchbase/ext/libcouchbase/sdspec.rb +31 -0
- data/lib/libcouchbase/ext/libcouchbase/t.rb +7 -0
- data/lib/libcouchbase/ext/libcouchbase/valbuf.rb +22 -0
- data/lib/libcouchbase/ext/libcouchbase/valbuf_u_buf.rb +14 -0
- data/lib/libcouchbase/ext/libcouchbase/viewhandle.rb +7 -0
- data/lib/libcouchbase/ext/libcouchbase_libuv.rb +22 -0
- data/lib/libcouchbase/ext/tasks.rb +39 -0
- data/lib/libcouchbase/n1ql.rb +78 -0
- data/lib/libcouchbase/query_full_text.rb +147 -0
- data/lib/libcouchbase/query_n1ql.rb +123 -0
- data/lib/libcouchbase/query_view.rb +135 -0
- data/lib/libcouchbase/results_fiber.rb +281 -0
- data/lib/libcouchbase/results_native.rb +220 -0
- data/lib/libcouchbase/subdoc_request.rb +139 -0
- data/lib/libcouchbase/version.rb +5 -0
- data/libcouchbase.gemspec +68 -0
- data/spec/bucket_spec.rb +290 -0
- data/spec/connection_spec.rb +257 -0
- data/spec/design_docs_spec.rb +31 -0
- data/spec/error_spec.rb +26 -0
- data/spec/fts_spec.rb +135 -0
- data/spec/n1ql_spec.rb +206 -0
- data/spec/results_libuv_spec.rb +244 -0
- data/spec/results_native_spec.rb +259 -0
- data/spec/seed/2016-10-25T043505Z/2016-10-25T043505Z-full/bucket-default/design.json +1 -0
- data/spec/seed/2016-10-25T043505Z/2016-10-25T043505Z-full/bucket-default/node-127.0.0.1%3A8091/data-0000.cbb +0 -0
- data/spec/seed/2016-10-25T043505Z/2016-10-25T043505Z-full/bucket-default/node-127.0.0.1%3A8091/failover.json +1 -0
- data/spec/seed/2016-10-25T043505Z/2016-10-25T043505Z-full/bucket-default/node-127.0.0.1%3A8091/meta.json +1 -0
- data/spec/seed/2016-10-25T043505Z/2016-10-25T043505Z-full/bucket-default/node-127.0.0.1%3A8091/seqno.json +1 -0
- data/spec/seed/2016-10-25T043505Z/2016-10-25T043505Z-full/bucket-default/node-127.0.0.1%3A8091/snapshot_markers.json +1 -0
- data/spec/subdoc_spec.rb +192 -0
- data/spec/view_spec.rb +201 -0
- data/windows_build.md +36 -0
- metadata +265 -0
@@ -0,0 +1,135 @@
|
|
1
|
+
# frozen_string_literal: true, encoding: ASCII-8BIT
|
2
|
+
|
3
|
+
module Libcouchbase
|
4
|
+
class QueryView
|
5
|
+
# Set this flag to execute an actual get with each response
|
6
|
+
F_INCLUDE_DOCS = 1 << 16
|
7
|
+
|
8
|
+
# Set this flag to only parse the top level row, and not its constituent
|
9
|
+
# parts. Note this is incompatible with `F_INCLUDE_DOCS`
|
10
|
+
F_NOROWPARSE = 1 << 17
|
11
|
+
|
12
|
+
# This view is spatial. Modifies how the final view path will be constructed
|
13
|
+
F_SPATIAL = 1 << 18
|
14
|
+
|
15
|
+
|
16
|
+
def initialize(connection, reactor, design, view, **opts)
|
17
|
+
@connection = connection
|
18
|
+
@reactor = reactor
|
19
|
+
|
20
|
+
@design = design
|
21
|
+
@view = view
|
22
|
+
@options = opts
|
23
|
+
|
24
|
+
@include_docs = true
|
25
|
+
@is_spatial = false
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :options, :design, :view, :connection
|
29
|
+
attr_accessor :include_docs, :is_spatial
|
30
|
+
|
31
|
+
def get_count(metadata)
|
32
|
+
metadata[:total_rows]
|
33
|
+
end
|
34
|
+
|
35
|
+
def perform(**options, &blk)
|
36
|
+
raise 'not connected' unless @connection.handle
|
37
|
+
raise 'query already in progress' if @cmd
|
38
|
+
raise 'callback required' unless block_given?
|
39
|
+
|
40
|
+
options = @options.merge(options)
|
41
|
+
# We should never exceed the users results limit
|
42
|
+
orig_limit = @options[:limit]
|
43
|
+
limit = options[:limit]
|
44
|
+
if orig_limit && limit
|
45
|
+
options[:limit] = orig_limit if limit > orig_limit
|
46
|
+
end
|
47
|
+
|
48
|
+
pairs = []
|
49
|
+
options.each { |key, val|
|
50
|
+
if key.to_s.include?('key') && val[0] != "["
|
51
|
+
pairs << "#{key}=#{[val].to_json[1...-1]}"
|
52
|
+
else
|
53
|
+
pairs << "#{key}=#{val}"
|
54
|
+
end
|
55
|
+
}
|
56
|
+
opts = pairs.join('&')
|
57
|
+
|
58
|
+
@reactor.schedule {
|
59
|
+
@error = nil
|
60
|
+
@callback = blk
|
61
|
+
|
62
|
+
@cmd = Ext::CMDVIEWQUERY.new
|
63
|
+
Ext.view_query_initcmd(@cmd, @design.to_s, @view.to_s, opts, @connection.get_callback(:viewquery_callback))
|
64
|
+
@cmd[:cmdflags] |= F_INCLUDE_DOCS if include_docs
|
65
|
+
@cmd[:cmdflags] |= F_SPATIAL if is_spatial
|
66
|
+
|
67
|
+
pointer = @cmd.to_ptr
|
68
|
+
|
69
|
+
@connection.requests[pointer.address] = self
|
70
|
+
err = Ext.view_query(@connection.handle, pointer, @cmd)
|
71
|
+
if err != :success
|
72
|
+
error(Error.lookup(err).new('view request not scheduled'))
|
73
|
+
end
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def received(row)
|
78
|
+
return if @error
|
79
|
+
|
80
|
+
key = row[:key].read_string(row[:nkey])
|
81
|
+
cas = row[:cas]
|
82
|
+
emitted = row[:value].read_string(row[:nvalue]) if row[:nvalue] > 0
|
83
|
+
geometry = row[:geometry].read_string(row[:ngeometry]) if row[:ngeometry] > 0
|
84
|
+
doc_id = row[:docid].read_string(row[:ndocid]) if row[:ndocid] > 0
|
85
|
+
|
86
|
+
meta = {
|
87
|
+
emitted: emitted,
|
88
|
+
geometry: geometry,
|
89
|
+
key: key
|
90
|
+
}
|
91
|
+
|
92
|
+
resp = Response.new(:viewquery_callback, doc_id, cas)
|
93
|
+
resp.metadata = meta
|
94
|
+
|
95
|
+
# check for included document here
|
96
|
+
if @include_docs && row[:docresp]
|
97
|
+
doc = row[:docresp]
|
98
|
+
raw_string = doc[:value].null? ? '' : doc[:value].read_string(doc[:nvalue])
|
99
|
+
resp.value = @connection.parse_document(raw_string)
|
100
|
+
meta[:flags] = doc[:itmflags]
|
101
|
+
end
|
102
|
+
|
103
|
+
@callback.call(false, resp)
|
104
|
+
rescue => e
|
105
|
+
@error = e
|
106
|
+
end
|
107
|
+
|
108
|
+
def received_final(metadata)
|
109
|
+
@connection.requests.delete(@cmd.to_ptr.address)
|
110
|
+
@cmd = nil
|
111
|
+
|
112
|
+
if @error
|
113
|
+
if @error == :cancelled
|
114
|
+
@callback.call(:final, metadata)
|
115
|
+
else
|
116
|
+
@callback.call(:error, @error)
|
117
|
+
end
|
118
|
+
else
|
119
|
+
@callback.call(:final, metadata)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def error(obj)
|
124
|
+
@error = obj
|
125
|
+
received_final(nil)
|
126
|
+
end
|
127
|
+
|
128
|
+
# We don't ever actually cancel a request here.
|
129
|
+
# There is an API however it indicates that @connection.handle might be destroyed
|
130
|
+
# Testing also indicated that @connection.handle was destroyed with seg faults
|
131
|
+
def cancel
|
132
|
+
@error = :cancelled
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,281 @@
|
|
1
|
+
# frozen_string_literal: true, encoding: ASCII-8BIT
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
module Libcouchbase
|
6
|
+
class ResultsFiber < Results
|
7
|
+
def initialize(query, &row_modifier)
|
8
|
+
@query_in_progress = false
|
9
|
+
@query_completed = false
|
10
|
+
@complete_result_set = false
|
11
|
+
|
12
|
+
@results = []
|
13
|
+
@fiber = nil
|
14
|
+
|
15
|
+
# We don't want to resume a fiber that is waiting
|
16
|
+
# in a yield to user code as then the Fiber might
|
17
|
+
# end before we've finished processing and this is
|
18
|
+
# very much not desirable - dead fiber errors
|
19
|
+
@resume_results = true
|
20
|
+
|
21
|
+
# This could be a view or n1ql query
|
22
|
+
@query = query
|
23
|
+
@row_modifier = row_modifier
|
24
|
+
end
|
25
|
+
|
26
|
+
def options(**opts)
|
27
|
+
reset
|
28
|
+
@query.options.merge!(opts)
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def stream(&blk)
|
33
|
+
if @complete_result_set
|
34
|
+
@results.each &blk
|
35
|
+
else
|
36
|
+
perform is_complete: false
|
37
|
+
@fiber = Fiber.current
|
38
|
+
|
39
|
+
begin
|
40
|
+
remaining = @results.length > 0
|
41
|
+
while !@query_completed || remaining do
|
42
|
+
if remaining
|
43
|
+
@resume_results = false
|
44
|
+
yield @results.shift
|
45
|
+
else
|
46
|
+
@resume_results = true
|
47
|
+
resume
|
48
|
+
end
|
49
|
+
|
50
|
+
remaining = @results.length > 0
|
51
|
+
end
|
52
|
+
ensure
|
53
|
+
# cancel is executed on break or error
|
54
|
+
@resume_results = true
|
55
|
+
cancel unless @query_completed
|
56
|
+
@fiber = nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
def reset
|
63
|
+
raise 'query in progress' if @query_in_progress
|
64
|
+
@query_in_progress = false
|
65
|
+
@complete_result_set = false
|
66
|
+
@results.clear
|
67
|
+
end
|
68
|
+
|
69
|
+
def each(&blk)
|
70
|
+
# return a valid enumerator
|
71
|
+
return load_all.each unless block_given?
|
72
|
+
|
73
|
+
if @complete_result_set
|
74
|
+
@results.each &blk
|
75
|
+
else
|
76
|
+
perform
|
77
|
+
@fiber = Fiber.current
|
78
|
+
|
79
|
+
begin
|
80
|
+
index = 0
|
81
|
+
remaining = index < @results.length
|
82
|
+
while !@query_completed || remaining do
|
83
|
+
if remaining
|
84
|
+
@resume_results = false
|
85
|
+
yield @results[index]
|
86
|
+
index += 1
|
87
|
+
else
|
88
|
+
@resume_results = true
|
89
|
+
resume
|
90
|
+
end
|
91
|
+
|
92
|
+
remaining = index < @results.length
|
93
|
+
end
|
94
|
+
ensure
|
95
|
+
# cancel is executed on break or error
|
96
|
+
@resume_results = true
|
97
|
+
cancel unless @query_completed
|
98
|
+
@fiber = nil
|
99
|
+
end
|
100
|
+
end
|
101
|
+
self
|
102
|
+
end
|
103
|
+
|
104
|
+
def first
|
105
|
+
if @complete_result_set || @results.length > 0
|
106
|
+
@results[0]
|
107
|
+
else
|
108
|
+
perform is_complete: false, limit: 1
|
109
|
+
|
110
|
+
@fiber = Fiber.current
|
111
|
+
begin
|
112
|
+
while not @query_completed do
|
113
|
+
resume
|
114
|
+
end
|
115
|
+
ensure
|
116
|
+
@fiber = nil
|
117
|
+
end
|
118
|
+
|
119
|
+
result = @results[0]
|
120
|
+
result
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def count
|
125
|
+
first unless @metadata
|
126
|
+
@query.get_count @metadata
|
127
|
+
end
|
128
|
+
|
129
|
+
def take(num)
|
130
|
+
if @complete_result_set || @results.length >= num
|
131
|
+
@results[0...num]
|
132
|
+
else
|
133
|
+
perform is_complete: false, limit: num
|
134
|
+
@fiber = Fiber.current
|
135
|
+
result = []
|
136
|
+
|
137
|
+
begin
|
138
|
+
index = 0
|
139
|
+
remaining = index < @results.length && index < num
|
140
|
+
while !@query_completed || remaining do
|
141
|
+
if remaining
|
142
|
+
result << @results[index]
|
143
|
+
index += 1
|
144
|
+
else
|
145
|
+
resume
|
146
|
+
end
|
147
|
+
|
148
|
+
remaining = index < @results.length && index < num
|
149
|
+
end
|
150
|
+
ensure
|
151
|
+
@fiber = nil
|
152
|
+
end
|
153
|
+
|
154
|
+
result
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def cancel
|
159
|
+
@cancelled = true
|
160
|
+
@query.cancel
|
161
|
+
resume
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
protected
|
166
|
+
|
167
|
+
|
168
|
+
def load_all
|
169
|
+
return @results if @complete_result_set
|
170
|
+
perform
|
171
|
+
|
172
|
+
@fiber = Fiber.current
|
173
|
+
begin
|
174
|
+
while not @query_completed do; resume; end
|
175
|
+
ensure
|
176
|
+
@fiber = nil
|
177
|
+
end
|
178
|
+
@results
|
179
|
+
end
|
180
|
+
|
181
|
+
def perform(is_complete: true, **opts)
|
182
|
+
return if @query_in_progress
|
183
|
+
@query_in_progress = true
|
184
|
+
@query_completed = false
|
185
|
+
|
186
|
+
# This flag is required to prevent race conditions
|
187
|
+
@cancelled = false
|
188
|
+
@results.clear
|
189
|
+
|
190
|
+
# This performs the query against the server
|
191
|
+
@query.perform(**opts) { |final, item|
|
192
|
+
on_thread(is_complete, final, item)
|
193
|
+
}
|
194
|
+
end
|
195
|
+
|
196
|
+
def process_response(is_complete, final, item)
|
197
|
+
# Has the operation completed?
|
198
|
+
if final
|
199
|
+
if final == :error
|
200
|
+
@error = item unless @cancelled
|
201
|
+
@complete_result_set = false
|
202
|
+
elsif @cancelled
|
203
|
+
@metadata = item
|
204
|
+
@complete_result_set = false
|
205
|
+
else
|
206
|
+
@metadata = item
|
207
|
+
@complete_result_set = is_complete
|
208
|
+
end
|
209
|
+
@query_completed = true
|
210
|
+
@query_in_progress = false
|
211
|
+
|
212
|
+
# Do we want to transform the results
|
213
|
+
elsif @row_modifier
|
214
|
+
begin
|
215
|
+
@results << @row_modifier.call(item)
|
216
|
+
rescue Exception => e
|
217
|
+
@error = e
|
218
|
+
end
|
219
|
+
else
|
220
|
+
@results << item
|
221
|
+
end
|
222
|
+
|
223
|
+
# Resume processing
|
224
|
+
@fiber.resume if @fiber && @resume_results && (!@cancelled || final)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
class ResultsLibuv < ResultsFiber
|
229
|
+
def initialize(query, thread = reactor, &row_modifier)
|
230
|
+
super(query, &row_modifier)
|
231
|
+
@reactor = thread
|
232
|
+
end
|
233
|
+
|
234
|
+
|
235
|
+
protected
|
236
|
+
|
237
|
+
|
238
|
+
def on_thread(is_complete, final, item)
|
239
|
+
@reactor.schedule {
|
240
|
+
process_response(is_complete, final, item)
|
241
|
+
}
|
242
|
+
end
|
243
|
+
|
244
|
+
def resume
|
245
|
+
@reactor = reactor
|
246
|
+
|
247
|
+
# Prevent the reactor from stopping
|
248
|
+
@reactor.ref
|
249
|
+
Fiber.yield
|
250
|
+
@reactor.unref
|
251
|
+
|
252
|
+
# Clear and raise the error
|
253
|
+
if @error
|
254
|
+
err = @error
|
255
|
+
@error = nil
|
256
|
+
raise err unless @cancelled
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
class ResultsEM < ResultsFiber
|
262
|
+
protected
|
263
|
+
|
264
|
+
def on_thread(is_complete, final, item)
|
265
|
+
EM.next_tick {
|
266
|
+
process_response(is_complete, final, item)
|
267
|
+
}
|
268
|
+
end
|
269
|
+
|
270
|
+
def resume
|
271
|
+
Fiber.yield
|
272
|
+
|
273
|
+
# Clear and raise the error
|
274
|
+
if @error
|
275
|
+
err = @error
|
276
|
+
@error = nil
|
277
|
+
raise err unless @cancelled
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
@@ -0,0 +1,220 @@
|
|
1
|
+
# frozen_string_literal: true, encoding: ASCII-8BIT
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
module Libcouchbase
|
6
|
+
class ResultsNative < Results
|
7
|
+
def initialize(query, &row_modifier)
|
8
|
+
@query_in_progress = false
|
9
|
+
@query_completed = false
|
10
|
+
@complete_result_set = false
|
11
|
+
|
12
|
+
@results = []
|
13
|
+
|
14
|
+
# This could be a view or n1ql query
|
15
|
+
@query = query
|
16
|
+
@row_modifier = row_modifier
|
17
|
+
end
|
18
|
+
|
19
|
+
def options(**opts)
|
20
|
+
reset
|
21
|
+
@query.options.merge!(opts)
|
22
|
+
end
|
23
|
+
|
24
|
+
def stream(&blk)
|
25
|
+
if @complete_result_set
|
26
|
+
@results.each &blk
|
27
|
+
else
|
28
|
+
perform is_complete: false
|
29
|
+
begin
|
30
|
+
remaining = @results.length > 0
|
31
|
+
while !@query_completed || remaining do
|
32
|
+
if remaining
|
33
|
+
yield @results.shift
|
34
|
+
else
|
35
|
+
process_next_item
|
36
|
+
end
|
37
|
+
|
38
|
+
remaining = @results.length > 0
|
39
|
+
end
|
40
|
+
ensure
|
41
|
+
# cancel is executed on break or error
|
42
|
+
cancel unless @query_completed
|
43
|
+
end
|
44
|
+
|
45
|
+
raise @error if @error
|
46
|
+
end
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def reset
|
51
|
+
raise 'query in progress' if @query_in_progress
|
52
|
+
@query_in_progress = false
|
53
|
+
@complete_result_set = false
|
54
|
+
@results.clear
|
55
|
+
end
|
56
|
+
|
57
|
+
def each(&blk)
|
58
|
+
# return a valid enumerator
|
59
|
+
return load_all.each unless block_given?
|
60
|
+
|
61
|
+
if @complete_result_set
|
62
|
+
@results.each &blk
|
63
|
+
else
|
64
|
+
perform
|
65
|
+
|
66
|
+
begin
|
67
|
+
index = 0
|
68
|
+
remaining = index < @results.length
|
69
|
+
while !@query_completed || remaining do
|
70
|
+
if remaining
|
71
|
+
yield @results[index]
|
72
|
+
index += 1
|
73
|
+
else
|
74
|
+
process_next_item
|
75
|
+
end
|
76
|
+
|
77
|
+
remaining = index < @results.length
|
78
|
+
end
|
79
|
+
ensure
|
80
|
+
# cancel is executed on break or error
|
81
|
+
cancel unless @query_completed
|
82
|
+
end
|
83
|
+
|
84
|
+
raise @error if @error
|
85
|
+
end
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
def first
|
90
|
+
if @complete_result_set || @results.length > 0
|
91
|
+
@results[0]
|
92
|
+
else
|
93
|
+
perform is_complete: false, limit: 1
|
94
|
+
|
95
|
+
while not @query_completed do
|
96
|
+
process_next_item
|
97
|
+
end
|
98
|
+
raise @error if @error
|
99
|
+
|
100
|
+
result = @results[0]
|
101
|
+
result
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def count
|
106
|
+
first unless @metadata
|
107
|
+
@query.get_count @metadata
|
108
|
+
end
|
109
|
+
|
110
|
+
def take(num)
|
111
|
+
if @complete_result_set || @results.length >= num
|
112
|
+
@results[0...num]
|
113
|
+
else
|
114
|
+
perform is_complete: false, limit: num
|
115
|
+
|
116
|
+
index = 0
|
117
|
+
result = []
|
118
|
+
remaining = index < @results.length && index < num
|
119
|
+
while !@query_completed || remaining do
|
120
|
+
if remaining
|
121
|
+
result << @results[index]
|
122
|
+
index += 1
|
123
|
+
else
|
124
|
+
process_next_item
|
125
|
+
end
|
126
|
+
|
127
|
+
remaining = index < @results.length && index < num
|
128
|
+
end
|
129
|
+
|
130
|
+
raise @error if @error
|
131
|
+
result
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def cancel
|
136
|
+
return if @cancelled
|
137
|
+
@cancelled = true
|
138
|
+
@query.cancel
|
139
|
+
process_next_item
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
protected
|
144
|
+
|
145
|
+
|
146
|
+
def process_next_item(can_loop = true)
|
147
|
+
return if @query_completed
|
148
|
+
final, item = @queue.pop
|
149
|
+
|
150
|
+
if final
|
151
|
+
if final == :error
|
152
|
+
@error = item unless @cancelled
|
153
|
+
@complete_result_set = false
|
154
|
+
elsif @cancelled
|
155
|
+
@metadata = item
|
156
|
+
@complete_result_set = false
|
157
|
+
else
|
158
|
+
@metadata = item
|
159
|
+
@complete_result_set = @is_complete
|
160
|
+
end
|
161
|
+
@query_completed = true
|
162
|
+
@query_in_progress = false
|
163
|
+
|
164
|
+
elsif not @cancelled
|
165
|
+
# Do we want to transform the results
|
166
|
+
if @row_modifier
|
167
|
+
begin
|
168
|
+
@results << @row_modifier.call(item)
|
169
|
+
rescue Exception => e
|
170
|
+
@error = e
|
171
|
+
@cancelled = true
|
172
|
+
@query.cancel
|
173
|
+
end
|
174
|
+
else
|
175
|
+
@results << item
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# This prevents the stack from blowing out
|
180
|
+
if can_loop
|
181
|
+
while !@queue.empty? || (@cancelled && !final) do
|
182
|
+
final = process_next_item(false)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
final
|
187
|
+
end
|
188
|
+
|
189
|
+
def load_all
|
190
|
+
return @results if @complete_result_set
|
191
|
+
perform
|
192
|
+
|
193
|
+
while not @query_completed do
|
194
|
+
process_next_item
|
195
|
+
end
|
196
|
+
raise @error if @error
|
197
|
+
|
198
|
+
@results
|
199
|
+
end
|
200
|
+
|
201
|
+
def perform(is_complete: true, **opts)
|
202
|
+
return if @query_in_progress
|
203
|
+
@query_in_progress = true
|
204
|
+
@query_completed = false
|
205
|
+
@is_complete = is_complete
|
206
|
+
@cancelled = false
|
207
|
+
@error = nil
|
208
|
+
|
209
|
+
# This flag is required so we don't
|
210
|
+
@results.clear
|
211
|
+
@queue = Queue.new
|
212
|
+
|
213
|
+
# This performs the query against the server
|
214
|
+
# The callback will always be on another thread
|
215
|
+
@query.perform(**opts) { |final, item|
|
216
|
+
@queue.push([final, item])
|
217
|
+
}
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|