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.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.gitmodules +3 -0
  4. data/.rspec +1 -0
  5. data/.travis.yml +38 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +24 -0
  8. data/README.md +445 -0
  9. data/Rakefile +76 -0
  10. data/ext/README.md +6 -0
  11. data/ext/Rakefile +19 -0
  12. data/lib/libcouchbase.rb +40 -0
  13. data/lib/libcouchbase/bucket.rb +825 -0
  14. data/lib/libcouchbase/callbacks.rb +69 -0
  15. data/lib/libcouchbase/connection.rb +886 -0
  16. data/lib/libcouchbase/design_docs.rb +92 -0
  17. data/lib/libcouchbase/error.rb +68 -0
  18. data/lib/libcouchbase/ext/libcouchbase.rb +1175 -0
  19. data/lib/libcouchbase/ext/libcouchbase/cmdbase.rb +23 -0
  20. data/lib/libcouchbase/ext/libcouchbase/cmdcounter.rb +36 -0
  21. data/lib/libcouchbase/ext/libcouchbase/cmdendure.rb +26 -0
  22. data/lib/libcouchbase/ext/libcouchbase/cmdfts.rb +24 -0
  23. data/lib/libcouchbase/ext/libcouchbase/cmdget.rb +30 -0
  24. data/lib/libcouchbase/ext/libcouchbase/cmdgetreplica.rb +49 -0
  25. data/lib/libcouchbase/ext/libcouchbase/cmdhttp.rb +58 -0
  26. data/lib/libcouchbase/ext/libcouchbase/cmdn1ql.rb +40 -0
  27. data/lib/libcouchbase/ext/libcouchbase/cmdobseqno.rb +33 -0
  28. data/lib/libcouchbase/ext/libcouchbase/cmdobserve.rb +30 -0
  29. data/lib/libcouchbase/ext/libcouchbase/cmdstore.rb +40 -0
  30. data/lib/libcouchbase/ext/libcouchbase/cmdstoredur.rb +45 -0
  31. data/lib/libcouchbase/ext/libcouchbase/cmdsubdoc.rb +61 -0
  32. data/lib/libcouchbase/ext/libcouchbase/cmdverbosity.rb +29 -0
  33. data/lib/libcouchbase/ext/libcouchbase/cmdviewquery.rb +61 -0
  34. data/lib/libcouchbase/ext/libcouchbase/contigbuf.rb +14 -0
  35. data/lib/libcouchbase/ext/libcouchbase/create_st.rb +15 -0
  36. data/lib/libcouchbase/ext/libcouchbase/create_st0.rb +23 -0
  37. data/lib/libcouchbase/ext/libcouchbase/create_st1.rb +26 -0
  38. data/lib/libcouchbase/ext/libcouchbase/create_st2.rb +32 -0
  39. data/lib/libcouchbase/ext/libcouchbase/create_st3.rb +26 -0
  40. data/lib/libcouchbase/ext/libcouchbase/crst_u.rb +20 -0
  41. data/lib/libcouchbase/ext/libcouchbase/durability_opts_st_v.rb +11 -0
  42. data/lib/libcouchbase/ext/libcouchbase/durability_opts_t.rb +14 -0
  43. data/lib/libcouchbase/ext/libcouchbase/durabilityopt_sv0.rb +63 -0
  44. data/lib/libcouchbase/ext/libcouchbase/enums.rb +1007 -0
  45. data/lib/libcouchbase/ext/libcouchbase/fragbuf.rb +18 -0
  46. data/lib/libcouchbase/ext/libcouchbase/ftshandle.rb +7 -0
  47. data/lib/libcouchbase/ext/libcouchbase/histogram.rb +34 -0
  48. data/lib/libcouchbase/ext/libcouchbase/http_request_t.rb +7 -0
  49. data/lib/libcouchbase/ext/libcouchbase/keybuf.rb +20 -0
  50. data/lib/libcouchbase/ext/libcouchbase/multicmd_ctx.rb +30 -0
  51. data/lib/libcouchbase/ext/libcouchbase/mutation_token.rb +17 -0
  52. data/lib/libcouchbase/ext/libcouchbase/n1qlhandle.rb +7 -0
  53. data/lib/libcouchbase/ext/libcouchbase/n1qlparams.rb +7 -0
  54. data/lib/libcouchbase/ext/libcouchbase/respbase.rb +29 -0
  55. data/lib/libcouchbase/ext/libcouchbase/respcounter.rb +32 -0
  56. data/lib/libcouchbase/ext/libcouchbase/respendure.rb +49 -0
  57. data/lib/libcouchbase/ext/libcouchbase/respfts.rb +40 -0
  58. data/lib/libcouchbase/ext/libcouchbase/respget.rb +44 -0
  59. data/lib/libcouchbase/ext/libcouchbase/resphttp.rb +48 -0
  60. data/lib/libcouchbase/ext/libcouchbase/respmcversion.rb +38 -0
  61. data/lib/libcouchbase/ext/libcouchbase/respn1ql.rb +41 -0
  62. data/lib/libcouchbase/ext/libcouchbase/respobseqno.rb +52 -0
  63. data/lib/libcouchbase/ext/libcouchbase/respobserve.rb +41 -0
  64. data/lib/libcouchbase/ext/libcouchbase/respserverbase.rb +32 -0
  65. data/lib/libcouchbase/ext/libcouchbase/respstats.rb +38 -0
  66. data/lib/libcouchbase/ext/libcouchbase/respstore.rb +32 -0
  67. data/lib/libcouchbase/ext/libcouchbase/respstoredur.rb +38 -0
  68. data/lib/libcouchbase/ext/libcouchbase/respsubdoc.rb +35 -0
  69. data/lib/libcouchbase/ext/libcouchbase/respviewquery.rb +67 -0
  70. data/lib/libcouchbase/ext/libcouchbase/sdentry.rb +22 -0
  71. data/lib/libcouchbase/ext/libcouchbase/sdspec.rb +31 -0
  72. data/lib/libcouchbase/ext/libcouchbase/t.rb +7 -0
  73. data/lib/libcouchbase/ext/libcouchbase/valbuf.rb +22 -0
  74. data/lib/libcouchbase/ext/libcouchbase/valbuf_u_buf.rb +14 -0
  75. data/lib/libcouchbase/ext/libcouchbase/viewhandle.rb +7 -0
  76. data/lib/libcouchbase/ext/libcouchbase_libuv.rb +22 -0
  77. data/lib/libcouchbase/ext/tasks.rb +39 -0
  78. data/lib/libcouchbase/n1ql.rb +78 -0
  79. data/lib/libcouchbase/query_full_text.rb +147 -0
  80. data/lib/libcouchbase/query_n1ql.rb +123 -0
  81. data/lib/libcouchbase/query_view.rb +135 -0
  82. data/lib/libcouchbase/results_fiber.rb +281 -0
  83. data/lib/libcouchbase/results_native.rb +220 -0
  84. data/lib/libcouchbase/subdoc_request.rb +139 -0
  85. data/lib/libcouchbase/version.rb +5 -0
  86. data/libcouchbase.gemspec +68 -0
  87. data/spec/bucket_spec.rb +290 -0
  88. data/spec/connection_spec.rb +257 -0
  89. data/spec/design_docs_spec.rb +31 -0
  90. data/spec/error_spec.rb +26 -0
  91. data/spec/fts_spec.rb +135 -0
  92. data/spec/n1ql_spec.rb +206 -0
  93. data/spec/results_libuv_spec.rb +244 -0
  94. data/spec/results_native_spec.rb +259 -0
  95. data/spec/seed/2016-10-25T043505Z/2016-10-25T043505Z-full/bucket-default/design.json +1 -0
  96. data/spec/seed/2016-10-25T043505Z/2016-10-25T043505Z-full/bucket-default/node-127.0.0.1%3A8091/data-0000.cbb +0 -0
  97. data/spec/seed/2016-10-25T043505Z/2016-10-25T043505Z-full/bucket-default/node-127.0.0.1%3A8091/failover.json +1 -0
  98. data/spec/seed/2016-10-25T043505Z/2016-10-25T043505Z-full/bucket-default/node-127.0.0.1%3A8091/meta.json +1 -0
  99. data/spec/seed/2016-10-25T043505Z/2016-10-25T043505Z-full/bucket-default/node-127.0.0.1%3A8091/seqno.json +1 -0
  100. data/spec/seed/2016-10-25T043505Z/2016-10-25T043505Z-full/bucket-default/node-127.0.0.1%3A8091/snapshot_markers.json +1 -0
  101. data/spec/subdoc_spec.rb +192 -0
  102. data/spec/view_spec.rb +201 -0
  103. data/windows_build.md +36 -0
  104. 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