tarantool 0.2.5 → 0.3.0.7
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.
- data/Gemfile +14 -12
- data/LICENSE +4 -4
- data/README.md +15 -9
- data/Rakefile +5 -129
- data/lib/tarantool/base_record.rb +288 -0
- data/lib/tarantool/block_db.rb +135 -0
- data/lib/tarantool/callback_db.rb +47 -0
- data/lib/tarantool/core-ext.rb +12 -0
- data/lib/tarantool/em_db.rb +37 -0
- data/lib/tarantool/exceptions.rb +52 -7
- data/lib/tarantool/fiber_db.rb +152 -0
- data/lib/tarantool/light_record.rb +68 -0
- data/lib/tarantool/query.rb +127 -0
- data/lib/tarantool/record/select.rb +59 -0
- data/lib/tarantool/record.rb +93 -282
- data/lib/tarantool/request.rb +351 -52
- data/lib/tarantool/response.rb +108 -45
- data/lib/tarantool/serializers/bson.rb +2 -2
- data/lib/tarantool/serializers.rb +32 -4
- data/lib/tarantool/space_array.rb +153 -0
- data/lib/tarantool/space_hash.rb +262 -0
- data/lib/tarantool/util.rb +182 -0
- data/lib/tarantool/version.rb +3 -0
- data/lib/tarantool.rb +79 -29
- data/test/box.pid +1 -0
- data/test/helper.rb +164 -0
- data/test/run_all.rb +3 -0
- data/test/shared_query.rb +73 -0
- data/test/shared_record.rb +474 -0
- data/test/shared_space_array.rb +284 -0
- data/test/shared_space_hash.rb +239 -0
- data/test/tarant/init.lua +22 -0
- data/test/tarantool.cfg +62 -0
- data/test/tarantool.log +6 -0
- data/{spec/tarantool.cfg → test/tarantool_repl.cfg} +11 -5
- data/test/test_light_record.rb +48 -0
- data/test/test_query_block.rb +6 -0
- data/test/test_query_fiber.rb +7 -0
- data/test/test_record.rb +88 -0
- data/{spec/tarantool/composite_primary_key_spec.rb → test/test_record_composite_pk.rb} +5 -7
- data/test/test_space_array_block.rb +6 -0
- data/test/test_space_array_callback.rb +255 -0
- data/test/test_space_array_callback_nodef.rb +190 -0
- data/test/test_space_array_fiber.rb +7 -0
- data/test/test_space_hash_block.rb +6 -0
- data/test/test_space_hash_fiber.rb +7 -0
- metadata +78 -55
- data/Gemfile.lock +0 -54
- data/examples/em_simple.rb +0 -16
- data/examples/record.rb +0 -68
- data/examples/simple.rb +0 -13
- data/lib/tarantool/requests/call.rb +0 -20
- data/lib/tarantool/requests/delete.rb +0 -18
- data/lib/tarantool/requests/insert.rb +0 -19
- data/lib/tarantool/requests/ping.rb +0 -16
- data/lib/tarantool/requests/select.rb +0 -22
- data/lib/tarantool/requests/update.rb +0 -35
- data/lib/tarantool/requests.rb +0 -19
- data/lib/tarantool/serializers/integer.rb +0 -14
- data/lib/tarantool/serializers/string.rb +0 -14
- data/lib/tarantool/space.rb +0 -39
- data/spec/helpers/let.rb +0 -11
- data/spec/helpers/truncate.rb +0 -12
- data/spec/spec_helper.rb +0 -21
- data/spec/tarantool/em_spec.rb +0 -22
- data/spec/tarantool/record_spec.rb +0 -316
- data/spec/tarantool/request_spec.rb +0 -103
- data/tarantool.gemspec +0 -69
data/lib/tarantool/request.rb
CHANGED
@@ -1,78 +1,377 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require 'tarantool/util'
|
2
|
+
require 'tarantool/serializers'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
module Tarantool
|
5
|
+
module Request
|
6
|
+
include Util::Packer
|
7
|
+
include Util::TailGetter
|
8
|
+
include Serializers
|
9
|
+
INT32 = 'V'.freeze
|
10
|
+
INT64 = 'Q<'.freeze
|
11
|
+
SELECT_HEADER = 'VVVVV'.freeze
|
12
|
+
INSERT_HEADER = 'VV'.freeze
|
13
|
+
UPDATE_HEADER = 'VV'.freeze
|
14
|
+
DELETE_HEADER = 'VV'.freeze
|
15
|
+
CALL_HEADER = 'Vwa*'.freeze
|
16
|
+
INT32_0 = "\x00\x00\x00\x00".freeze
|
17
|
+
INT32_1 = "\x01\x00\x00\x00".freeze
|
18
|
+
BER4 = "\x04".freeze
|
19
|
+
BER8 = "\x08".freeze
|
20
|
+
ZERO = "\x00".freeze
|
21
|
+
EMPTY = "".freeze
|
22
|
+
PACK_STRING = 'wa*'.freeze
|
23
|
+
LEST_INT32 = -(2**31)
|
24
|
+
GREATEST_INT32 = 2**32
|
25
|
+
TYPES_AUTO = [:auto].freeze
|
26
|
+
TYPES_FALLBACK = [:str].freeze
|
27
|
+
TYPES_STR_STR = [:str, :str].freeze
|
28
|
+
TYPES_STR_AUTO = [:str, :auto].freeze
|
29
|
+
|
30
|
+
REQUEST_SELECT = 17
|
31
|
+
REQUEST_INSERT = 13
|
32
|
+
REQUEST_UPDATE = 19
|
33
|
+
REQUEST_DELETE = 21
|
34
|
+
REQUEST_CALL = 22
|
35
|
+
REQUEST_PING = 65280
|
36
|
+
|
37
|
+
BOX_RETURN_TUPLE = 0x01
|
38
|
+
BOX_ADD = 0x02
|
39
|
+
BOX_REPLACE = 0x04
|
40
|
+
|
41
|
+
UPDATE_OPS = {
|
42
|
+
:"=" => 0, :+ => 1, :& => 2, :^ => 3, :| => 4, :[] => 5,
|
43
|
+
:set => 0, :add => 1, :and => 2, :xor => 3, :or => 4, :splice => 5,
|
44
|
+
:delete => 6, :insert => 7,
|
45
|
+
:del => 6, :ins => 7,
|
46
|
+
'=' => 0, '+' => 1, '&' => 2, '^' => 3, '|' => 4, '[]' => 5,
|
47
|
+
':' => 5,
|
48
|
+
'set'=> 0, 'add'=> 1, 'and'=> 2, 'xor'=> 3, 'or'=> 4, 'splice'=> 5,
|
49
|
+
'#' => 6, '!' => 7,
|
50
|
+
'delete'=> 6, 'insert'=> 7,
|
51
|
+
'del' => 6, 'ins' => 7,
|
52
|
+
0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7
|
53
|
+
}
|
54
|
+
UPDATE_FIELDNO_OP = 'VC'.freeze
|
55
|
+
|
56
|
+
def _select(space_no, index_no, offset, limit, keys, cb, fields, index_fields, translators = [])
|
57
|
+
get_tuples = limit == :first ? (limit = 1; :first) : :all
|
58
|
+
keys = [*keys]
|
59
|
+
body = [space_no, index_no, offset, limit, keys.size].pack(SELECT_HEADER)
|
60
|
+
|
61
|
+
for key in keys
|
62
|
+
pack_tuple(body, key, index_fields, index_no)
|
63
|
+
end
|
64
|
+
cb = Response.new(cb, get_tuples, fields, translators)
|
65
|
+
_send_request(REQUEST_SELECT, body, cb)
|
66
|
+
end
|
67
|
+
|
68
|
+
class IndexIndexError < StandardError; end
|
69
|
+
def pack_tuple(body, key, types, index_no = 0)
|
70
|
+
if Integer === types.last
|
71
|
+
*types, tail = types
|
72
|
+
else
|
73
|
+
tail = 1
|
74
|
+
end
|
75
|
+
case key
|
76
|
+
when Array
|
77
|
+
if index_no != :space && nili = key.index(nil)
|
78
|
+
key = key.slice(0, nili)
|
79
|
+
end
|
80
|
+
body << [key_size = key.size].pack(INT32)
|
81
|
+
i = 0
|
82
|
+
while i < key_size
|
83
|
+
field = types[i] || get_tail_item(types, i, tail)
|
84
|
+
pack_field(body, field, key[i])
|
85
|
+
i += 1
|
86
|
+
end
|
87
|
+
when nil
|
88
|
+
body << INT32_0
|
89
|
+
else
|
90
|
+
body << INT32_1
|
91
|
+
pack_field(body, types[0], key)
|
92
|
+
end
|
93
|
+
rescue IndexIndexError => e
|
94
|
+
raise ArgumentError, "tuple #{key} has more entries than index #{index_no}"
|
95
|
+
end
|
96
|
+
|
97
|
+
MAX_BYTE_SIZE = 1024 * 1024
|
98
|
+
def pack_field(body, field_kind, value)
|
99
|
+
if value.nil?
|
100
|
+
body << ZERO
|
101
|
+
return
|
102
|
+
end
|
103
|
+
case field_kind
|
104
|
+
when :int, :integer
|
105
|
+
value = value.to_i
|
106
|
+
if LEST_INT32 <= value && value < GREATEST_INT32
|
107
|
+
body << BER4 << [value].pack(INT32)
|
8
108
|
else
|
9
|
-
|
109
|
+
body << BER8 << [value].pack(INT64)
|
10
110
|
end
|
111
|
+
when :str, :string, :bytes
|
112
|
+
value = value.to_s
|
113
|
+
raise StringTooLong if value.bytesize > MAX_BYTE_SIZE
|
114
|
+
body << [value.bytesize, value].pack(PACK_STRING)
|
115
|
+
when :error
|
116
|
+
raise IndexIndexError
|
117
|
+
when :auto
|
118
|
+
case value
|
119
|
+
when Integer
|
120
|
+
pack_field(body, :int, value)
|
121
|
+
when String
|
122
|
+
pack_field(body, :str, value)
|
123
|
+
when Util::AutoType
|
124
|
+
pack_field(body, :str, value.data)
|
125
|
+
else
|
126
|
+
raise ArgumentError, "Could auto detect only Integer and String"
|
127
|
+
end
|
128
|
+
else
|
129
|
+
value = get_serializer(field_kind).encode(value).to_s
|
130
|
+
raise StringTooLong if value.bytesize > MAX_BYTE_SIZE
|
131
|
+
body << [value.bytesize, value].pack(PACK_STRING)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def _modify_request(type, body, fields, ret_tuple, cb, translators)
|
136
|
+
cb = Response.new(cb, ret_tuple && (ret_tuple != :all ? :first : :all),
|
137
|
+
fields, translators)
|
138
|
+
_send_request(type, body, cb)
|
139
|
+
end
|
140
|
+
|
141
|
+
def _insert(space_no, flags, tuple, fields, cb, ret_tuple, translators = [])
|
142
|
+
flags |= BOX_RETURN_TUPLE if ret_tuple
|
143
|
+
fields = [*fields]
|
144
|
+
|
145
|
+
tuple = [*tuple]
|
146
|
+
tuple_size = tuple.size
|
147
|
+
body = [space_no, flags].pack(INSERT_HEADER)
|
148
|
+
pack_tuple(body, tuple, fields, :space)
|
149
|
+
|
150
|
+
_modify_request(REQUEST_INSERT, body, fields, ret_tuple, cb, translators)
|
151
|
+
end
|
152
|
+
|
153
|
+
def _update(space_no, pk, operations, fields, pk_fields, cb, ret_tuple, translators = [])
|
154
|
+
flags = ret_tuple ? BOX_RETURN_TUPLE : 0
|
155
|
+
|
156
|
+
if Array === operations && !(Array === operations.first)
|
157
|
+
operations = [operations]
|
11
158
|
end
|
12
159
|
|
13
|
-
|
14
|
-
|
160
|
+
body = [space_no, flags].pack(UPDATE_HEADER)
|
161
|
+
pack_tuple(body, pk, pk_fields, 0)
|
162
|
+
body << [operations.size].pack(INT32)
|
163
|
+
|
164
|
+
_pack_operations(body, operations, fields)
|
165
|
+
|
166
|
+
_modify_request(REQUEST_UPDATE, body, fields, ret_tuple, cb, translators)
|
167
|
+
end
|
168
|
+
|
169
|
+
def _pack_operations(body, operations, fields)
|
170
|
+
if Integer === fields.last
|
171
|
+
*fields, tail = fields
|
172
|
+
else
|
173
|
+
tail = 1
|
15
174
|
end
|
175
|
+
for operation in operations
|
176
|
+
if Array === operation[0]
|
177
|
+
operation = operation[0] + operation.drop(1)
|
178
|
+
elsif Array === operation[1]
|
179
|
+
if operation.size == 2
|
180
|
+
operation = [operation[0]] + operation[1]
|
181
|
+
else
|
182
|
+
raise ArgumentError, "Could not understand operation #{operation}"
|
183
|
+
end
|
184
|
+
end
|
16
185
|
|
17
|
-
|
18
|
-
if
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
186
|
+
field_no = operation[0]
|
187
|
+
if operation.size == 2
|
188
|
+
if (Integer === field_no || field_no =~ /\A\d/)
|
189
|
+
unless Symbol === operation[1] && UPDATE_OPS[operation[1]] == 6
|
190
|
+
body << [field_no, 0].pack(UPDATE_FIELDNO_OP)
|
191
|
+
type = fields[field_no] || get_tail_item(fields, field_no, tail) ||
|
192
|
+
_detect_type(operation[1])
|
193
|
+
pack_field(body, type, operation[1])
|
194
|
+
next
|
195
|
+
end
|
196
|
+
elsif String === field_no
|
197
|
+
operation.insert(1, field_no.slice(0, 1))
|
198
|
+
field_no = field_no.slice(1..-1).to_i
|
24
199
|
else
|
25
|
-
|
200
|
+
raise ArgumentError, "Could not understand field number #{field_no.inspect}"
|
26
201
|
end
|
27
|
-
elsif value.is_a?(Tarantool::Field)
|
28
|
-
[value.data.bytesize].pack('w') + value.data
|
29
|
-
else
|
30
|
-
raise ArgumentError.new("Field should be integer or string")
|
31
202
|
end
|
32
|
-
end
|
33
|
-
end
|
34
203
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
204
|
+
op = operation[1]
|
205
|
+
op = UPDATE_OPS[op] unless Integer === op
|
206
|
+
raise ArgumentError, "Unknown operation #{operation[1]}" unless op
|
207
|
+
body << [field_no, op].pack(UPDATE_FIELDNO_OP)
|
208
|
+
case op
|
209
|
+
when 0
|
210
|
+
if (type = fields[field_no]).nil?
|
211
|
+
if operation.size == 4 && Symbol === operation.last
|
212
|
+
*operation, type = operation
|
213
|
+
else
|
214
|
+
type = get_tail_item(fields, field_no, tail) || _detect_type(operation[2])
|
215
|
+
end
|
216
|
+
end
|
217
|
+
unless operation.size == 3
|
218
|
+
raise ArgumentError, "wrong arguments for set or insert operation #{operation.inspect}"
|
219
|
+
end
|
220
|
+
pack_field(body, type, operation[2])
|
221
|
+
when 1, 2, 3, 4
|
222
|
+
unless operation.size == 3 && !operation[2].nil?
|
223
|
+
raise ArgumentError, "wrong arguments for integer operation #{operation.inspect}"
|
224
|
+
end
|
225
|
+
pack_field(body, :int, operation[2])
|
226
|
+
when 5
|
227
|
+
unless operation.size == 5 && !operation[2].nil? && !operation[3].nil?
|
228
|
+
raise ArgumentError, "wrong arguments for slice operation #{operation.inspect}"
|
229
|
+
end
|
230
|
+
|
231
|
+
str = operation[4].to_s
|
232
|
+
body << [ 10 + ber_size(str.bytesize) + str.bytesize ].pack('w')
|
233
|
+
pack_field(body, :int, operation[2])
|
234
|
+
pack_field(body, :int, operation[3])
|
235
|
+
pack_field(body, :str, str)
|
236
|
+
when 7
|
237
|
+
old_field_no = field_no +
|
238
|
+
(inserted ||= []).count{|i| i <= field_no} -
|
239
|
+
(deleted ||= []).count{|i| i <= field_no}
|
240
|
+
inserted << field_no
|
241
|
+
if (type = fields[old_field_no]).nil?
|
242
|
+
if operation.size == 4 && Symbol === operation.last
|
243
|
+
*operation, type = operation
|
244
|
+
else
|
245
|
+
type = get_tail_item(fields, old_field_no, tail)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
unless operation.size == 3
|
249
|
+
raise ArgumentError, "wrong arguments for set or insert operation #{operation.inspect}"
|
250
|
+
end
|
251
|
+
pack_field(body, type, operation[2])
|
252
|
+
when 6
|
253
|
+
body << ZERO
|
254
|
+
# pass
|
255
|
+
end
|
44
256
|
end
|
45
|
-
@space_no = params.delete(:space_no) || space.space_no || raise(UndefinedSpace.new)
|
46
|
-
parse_args
|
47
257
|
end
|
48
258
|
|
49
|
-
def
|
50
|
-
|
51
|
-
|
259
|
+
def _delete(space_no, pk, fields, pk_fields, cb, ret_tuple, translators = [])
|
260
|
+
flags = ret_tuple ? BOX_RETURN_TUPLE : 0
|
261
|
+
|
262
|
+
body = [space_no, flags].pack(DELETE_HEADER)
|
263
|
+
pack_tuple(body, pk, pk_fields, 0)
|
264
|
+
|
265
|
+
_modify_request(REQUEST_DELETE, body, fields, ret_tuple, cb, translators)
|
266
|
+
end
|
267
|
+
|
268
|
+
def _space_call_fix_values(values, space_no, opts)
|
269
|
+
opts = opts.dup
|
270
|
+
space_no = opts[:space_no] if opts.has_key?(:space_no)
|
271
|
+
if space_no
|
272
|
+
values = [space_no].concat([*values])
|
273
|
+
if opts[:types]
|
274
|
+
opts[:types] = [:str].concat([*opts[:types]]) # cause lua could convert it to integer by itself
|
275
|
+
else
|
276
|
+
opts[:types] = TYPES_STR_STR
|
277
|
+
end
|
278
|
+
end
|
279
|
+
[values, opts]
|
52
280
|
end
|
53
281
|
|
54
|
-
def
|
282
|
+
def _call(func_name, values, cb, opts={})
|
283
|
+
return_tuple = opts[:return_tuple] && :all
|
284
|
+
flags = return_tuple ? BOX_RETURN_TUPLE : 0
|
55
285
|
|
286
|
+
values = [*values]
|
287
|
+
value_types = opts[:types] ? [*opts[:types]] :
|
288
|
+
_detect_types(values)
|
289
|
+
return_types = [*opts[:returns] || TYPES_AUTO]
|
290
|
+
|
291
|
+
func_name = func_name.to_s
|
292
|
+
body = [flags, func_name.size, func_name].pack(CALL_HEADER)
|
293
|
+
pack_tuple(body, values, value_types, :func_call)
|
294
|
+
|
295
|
+
_modify_request(REQUEST_CALL, body, return_types, return_tuple, cb, opts[:translators] || [])
|
56
296
|
end
|
57
297
|
|
58
|
-
def
|
59
|
-
|
60
|
-
if return_code == 0
|
61
|
-
Response.new(data[4, data.size], response_params)
|
62
|
-
else
|
63
|
-
msg = data[4, data.size].unpack('A*')
|
64
|
-
raise BadReturnCode.new("Error code #{return_code}: #{msg}")
|
65
|
-
end
|
298
|
+
def _ping(cb)
|
299
|
+
_send_request(REQUEST_PING, EMPTY, cb)
|
66
300
|
end
|
301
|
+
alias ping_cb _ping
|
67
302
|
|
68
|
-
def
|
69
|
-
|
70
|
-
|
303
|
+
def _detect_types(values)
|
304
|
+
values.map{|v| Integer === v ? :int : :str}
|
305
|
+
end
|
306
|
+
|
307
|
+
def _detect_type(value)
|
308
|
+
Integer === v ? :int :
|
309
|
+
Util::AutoType === v ? :auto :
|
310
|
+
:str
|
311
|
+
end
|
312
|
+
|
313
|
+
def _parse_hash_definition(returns)
|
314
|
+
field_names = []
|
315
|
+
field_types = []
|
316
|
+
returns.each{|name, type|
|
317
|
+
field_names << name
|
318
|
+
field_types << type
|
319
|
+
}
|
320
|
+
field_types << if field_names.include?(:_tail)
|
321
|
+
unless field_names.last == :_tail
|
322
|
+
raise ArgumentError, "_tail should be de declared last"
|
323
|
+
end
|
324
|
+
field_names.pop
|
325
|
+
[*field_types.last].size
|
326
|
+
else
|
327
|
+
1
|
328
|
+
end
|
329
|
+
field_types.flatten!
|
330
|
+
[field_types, TranslateToHash.new(field_names, field_types.last)]
|
331
|
+
end
|
332
|
+
|
333
|
+
def _raise_or_return(res)
|
334
|
+
raise res if Exception === res
|
71
335
|
res
|
72
336
|
end
|
337
|
+
end
|
338
|
+
|
339
|
+
module CommonSpaceBlockMethods
|
340
|
+
def all_by_pks_blk(keys, opts={}, &block)
|
341
|
+
all_by_pks_cb(keys, block, opts)
|
342
|
+
end
|
73
343
|
|
74
|
-
def
|
75
|
-
|
344
|
+
def by_pk_blk(key_array, &block)
|
345
|
+
by_pk_cb(key_array, block)
|
346
|
+
end
|
347
|
+
|
348
|
+
def insert_blk(tuple, opts={}, &block)
|
349
|
+
insert_cb(tuple, block, opts)
|
350
|
+
end
|
351
|
+
|
352
|
+
def replace_blk(tuple, opts={}, &block)
|
353
|
+
replace_cb(tuple, block, opts)
|
354
|
+
end
|
355
|
+
|
356
|
+
def update_blk(pk, operations, opts={}, &block)
|
357
|
+
update_cb(pk, operations, block, opts)
|
358
|
+
end
|
359
|
+
|
360
|
+
def delete_blk(pk, opts={}, &block)
|
361
|
+
delete_cb(pk, block, opts)
|
362
|
+
end
|
363
|
+
|
364
|
+
def invoke_blk(func_name, values = [], opts={}, &block)
|
365
|
+
invoke_cb(func_name, values, block, opts)
|
366
|
+
end
|
367
|
+
|
368
|
+
def call_blk(func_name, values = [], opts={}, &block)
|
369
|
+
call_cb(func_name, values, block, opts)
|
370
|
+
end
|
371
|
+
|
372
|
+
def ping_blk(&block)
|
373
|
+
ping_cb(block)
|
76
374
|
end
|
77
375
|
end
|
78
|
-
|
376
|
+
|
377
|
+
end
|
data/lib/tarantool/response.rb
CHANGED
@@ -1,61 +1,124 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
def initialize(data)
|
5
|
-
@data = data
|
6
|
-
end
|
1
|
+
require 'tarantool/util'
|
2
|
+
require 'tarantool/exceptions'
|
3
|
+
require 'tarantool/serializers'
|
7
4
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
5
|
+
module Tarantool
|
6
|
+
class Response < Struct.new(:cb, :get_tuples, :fields, :translators)
|
7
|
+
include Util::Packer
|
8
|
+
include Util::TailGetter
|
9
|
+
include Serializers
|
10
|
+
def call(data)
|
11
|
+
if Exception === data
|
12
|
+
cb.call(data)
|
16
13
|
else
|
17
|
-
|
14
|
+
if (ret = return_code(data)) == 0
|
15
|
+
cb.call parse_response(data)
|
16
|
+
else
|
17
|
+
data.gsub!("\x00", "")
|
18
|
+
cb.call CODE_TO_EXCEPTION[ret].new(ret, data)
|
19
|
+
end
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
21
|
-
def
|
22
|
-
|
23
|
+
def translators
|
24
|
+
super || (self.translators = [])
|
23
25
|
end
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
@offset = 0
|
29
|
-
@tuples_affected, = data[0, 4].unpack('L')
|
30
|
-
@offset += 4
|
31
|
-
if params[:return_tuple]
|
32
|
-
@tuples = (1..tuples_affected).map do
|
33
|
-
unpack_tuple(data)
|
34
|
-
end
|
26
|
+
|
27
|
+
def parse_response(data)
|
28
|
+
unless get_tuples
|
29
|
+
unpack_int32(data)
|
35
30
|
else
|
36
|
-
|
31
|
+
tuples = unpack_tuples(data)
|
32
|
+
if translators
|
33
|
+
translators.each{|trans|
|
34
|
+
tuples.map!{|tuple| trans.call(tuple)}
|
35
|
+
}
|
36
|
+
end
|
37
|
+
get_tuples == :first ? tuples.first : tuples
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
tuples
|
43
|
-
|
41
|
+
def unpack_tuples(data)
|
42
|
+
tuples_affected = unpack_int32!(data)
|
43
|
+
tuples = []
|
44
|
+
fields = fields()
|
45
|
+
if Integer === fields.last
|
46
|
+
*fields, tail = fields
|
47
|
+
else
|
48
|
+
tail = 1
|
49
|
+
end
|
44
50
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
51
|
+
while tuples_affected > 0
|
52
|
+
byte_size = unpack_int32!(data)
|
53
|
+
fields_num = unpack_int32!(data)
|
54
|
+
tuple_str = data.slice!(0, byte_size)
|
55
|
+
i = 0
|
56
|
+
tuple = []
|
57
|
+
while i < fields_num
|
58
|
+
field_size = unpack_ber!(tuple_str)
|
59
|
+
|
60
|
+
field = fields[i] || get_tail_item(fields, i, tail)
|
61
|
+
|
62
|
+
tuple << (field_size == 0 ? nil :
|
63
|
+
case field
|
64
|
+
when :int, :integer
|
65
|
+
case field_size
|
66
|
+
when 8
|
67
|
+
unpack_int64!(tuple_str)
|
68
|
+
when 4
|
69
|
+
unpack_int32!(tuple_str)
|
70
|
+
when 2
|
71
|
+
unpack_int16!(tuple_str)
|
72
|
+
else
|
73
|
+
raise ValueError, "Bad field size #{field_size} for integer field ##{i}"
|
74
|
+
end
|
75
|
+
when :str, :string
|
76
|
+
tuple_str.slice!(0, field_size).force_encoding('utf-8')
|
77
|
+
when :bytes
|
78
|
+
tuple_str.slice!(0, field_size)
|
79
|
+
when :auto
|
80
|
+
str = tuple_str.slice!(0, field_size).force_encoding('utf-8')
|
81
|
+
case field_size
|
82
|
+
when 8, 4, 2
|
83
|
+
Util::AutoType.new(str)
|
84
|
+
else
|
85
|
+
str
|
86
|
+
end
|
87
|
+
else
|
88
|
+
get_serializer(field).decode(tuple_str.slice!(0, field_size))
|
89
|
+
end)
|
90
|
+
i += 1
|
91
|
+
end
|
92
|
+
tuples << tuple
|
93
|
+
tuples_affected -= 1
|
52
94
|
end
|
95
|
+
tuples
|
96
|
+
end
|
97
|
+
|
98
|
+
def return_code(data)
|
99
|
+
unpack_int32!(data)
|
53
100
|
end
|
101
|
+
end
|
54
102
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
103
|
+
# note that :_tail should not be in field_names
|
104
|
+
class TranslateToHash < Struct.new(:field_names, :tail_size)
|
105
|
+
def call(tuple)
|
106
|
+
i = 0
|
107
|
+
hash = {}
|
108
|
+
tuple_size = tuple.size
|
109
|
+
names = field_names
|
110
|
+
while i < tuple_size
|
111
|
+
if name = names[i]
|
112
|
+
hash[name] = tuple[i]
|
113
|
+
else
|
114
|
+
tail = tuple.slice(i..-1)
|
115
|
+
hash[:_tail] = tail_size == 1 ? tail :
|
116
|
+
tail.each_slice(tail_size).to_a
|
117
|
+
break
|
118
|
+
end
|
119
|
+
i += 1
|
120
|
+
end
|
121
|
+
hash
|
59
122
|
end
|
60
123
|
end
|
61
|
-
end
|
124
|
+
end
|
@@ -1,9 +1,37 @@
|
|
1
|
-
|
1
|
+
module Tarantool
|
2
2
|
module Serializers
|
3
3
|
MAP = {}
|
4
|
-
|
5
|
-
|
4
|
+
|
5
|
+
def check_type(type)
|
6
|
+
type = type.to_sym if String === type
|
7
|
+
return :int if Integer == type
|
8
|
+
return :str if String == type
|
9
|
+
|
10
|
+
case type
|
11
|
+
when :int, :integer, :str, :string, :bytes, :auto
|
12
|
+
# pass
|
13
|
+
when Symbol
|
14
|
+
unless MAP.include?(type)
|
15
|
+
raise ArgumentError, "Unknown type name #{type.inspect}"
|
16
|
+
end
|
17
|
+
else
|
18
|
+
unless type.respond_to?(:encode) && type.respond_to?(:decode)
|
19
|
+
raise ArgumentError, "Wrong serializer object #{type.inspect} (must respond to #encode and #decode)"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
type
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_serializer(type)
|
26
|
+
if Symbol === type
|
27
|
+
MAP.fetch(type){ raise ArgumentError, "Unknown type name #{type.inspect}" }
|
28
|
+
elsif type.respond_to?(:encode) && type.respond_to?(:decode)
|
29
|
+
type
|
30
|
+
else
|
31
|
+
raise ArgumentError, "Wrong serializer object #{type.inspect} (must respond to #encode and #decode)"
|
32
|
+
end
|
6
33
|
end
|
7
34
|
|
35
|
+
extend self
|
8
36
|
end
|
9
|
-
end
|
37
|
+
end
|