tarantool16 0.0.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.
@@ -0,0 +1,82 @@
1
+
2
+ module Tarantool16
3
+ class Error < ::StandardError; end
4
+ class ConnectionError < Error; end
5
+ DBErrors = {}
6
+ class SchemaError < Error; end
7
+ class DBError < Error
8
+ class KnownDBError < DBError
9
+ class << self
10
+ attr_accessor :return_code
11
+ end
12
+ def return_code
13
+ self.class.return_code
14
+ end
15
+ end
16
+ class UnknownDBError < DBError
17
+ attr_accessor :return_code
18
+ end
19
+
20
+ {
21
+ 1=> :ER_ILLEGAL_PARAMS,
22
+ 2=> :ER_MEMORY_ISSUE,
23
+ 3=> :ER_TUPLE_FOUND,
24
+ 4=> :ER_TUPLE_NOT_FOUND,
25
+ 5=> :ER_UNSUPPORTED,
26
+ 6=> :ER_NONMASTER,
27
+ 7=> :ER_SECONDARY,
28
+ 8=> :ER_INJECTION,
29
+ 9=> :ER_CREATE_SPACE,
30
+ 10=> :ER_SPACE_EXISTS,
31
+ 11=> :ER_DROP_SPACE,
32
+ 12=> :ER_ALTER_SPACE,
33
+ 13=> :ER_INDEX_TYPE,
34
+ 14=> :ER_MODIFY_INDEX,
35
+ 15=> :ER_LAST_DROP,
36
+ 16=> :ER_TUPLE_FORMAT_LIMIT,
37
+ 17=> :ER_DROP_PRIMARY_KEY,
38
+ 18=> :ER_KEY_FIELD_TYPE,
39
+ 19=> :ER_EXACT_MATCH,
40
+ 20=> :ER_INVALID_MSGPACK,
41
+ 21=> :ER_PROC_RET,
42
+ 22=> :ER_TUPLE_NOT_ARRAY,
43
+ 23=> :ER_FIELD_TYPE,
44
+ 24=> :ER_FIELD_TYPE_MISMATCH,
45
+ 25=> :ER_SPLICE,
46
+ 26=> :ER_ARG_TYPE,
47
+ 27=> :ER_TUPLE_IS_TOO_LONG,
48
+ 28=> :ER_UNKNOWN_UPDATE_OP,
49
+ 29=> :ER_UPDATE_FIELD,
50
+ 30=> :ER_FIBER_STACK,
51
+ 31=> :ER_KEY_PART_COUNT,
52
+ 32=> :ER_PROC_LUA,
53
+ 33=> :ER_NO_SUCH_PROC,
54
+ 34=> :ER_NO_SUCH_TRIGGER,
55
+ 35=> :ER_NO_SUCH_INDEX,
56
+ 36=> :ER_NO_SUCH_SPACE,
57
+ 37=> :ER_NO_SUCH_FIELD,
58
+ 38=> :ER_SPACE_ARITY,
59
+ 39=> :ER_INDEX_ARITY,
60
+ 40=> :ER_WAL_IO,
61
+ 41=> :ER_MORE_THAN_ONE_TUPLE,
62
+ }.each do |n, s|
63
+ klass = Class.new(KnownDBError)
64
+ klass.return_code = n
65
+ Tarantool16::DBErrors[n] = klass
66
+ Tarantool16.const_set(s, klass)
67
+ end
68
+ def self.with_code_message(n, m="")
69
+ if klass = DBErrors[n]
70
+ klass.new(m)
71
+ else
72
+ e = UnknownDBError.new(m)
73
+ e.return_code = n
74
+ e
75
+ end
76
+ end
77
+
78
+ def inspect
79
+ "<#{self.class.name} return_code=#{return_code} message=#{message}>"
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,8 @@
1
+ relative_require 'consts'
2
+
3
+ module Tarantool16
4
+ class Query
5
+ def initialize(db)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,36 @@
1
+ module Tarantool16
2
+ class Option
3
+ attr :error, :data
4
+ def initialize(err, data)
5
+ @error = err
6
+ @data = data
7
+ end
8
+
9
+ def ok?
10
+ !@error
11
+ end
12
+
13
+ def raise_if_error!
14
+ raise @error if @error
15
+ end
16
+
17
+ def self.ok(data)
18
+ new(nil, data)
19
+ end
20
+
21
+ def self.error(err, message = nil)
22
+ if err.is_a? Class
23
+ err = err.new message
24
+ end
25
+ new(err, nil)
26
+ end
27
+
28
+ def inspect
29
+ if ok?
30
+ "<Option data=#{@data.inspect}>"
31
+ else
32
+ "<Option error=#{@error.inspect}>"
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,269 @@
1
+ require_relative 'response'
2
+ require_relative 'consts'
3
+ module Tarantool16
4
+ class SchemaSpace
5
+ attr :sid, :name, :indices, :fields
6
+ def initialize(sid, name, fields)
7
+ @sid = sid
8
+ @name = name
9
+ @has_tail = false
10
+ self.fields = fields
11
+ end
12
+
13
+ # imitate Option
14
+ def ok?; true; end
15
+ def data; self; end
16
+
17
+ def fields=(flds)
18
+ @field_names = {}
19
+ @fields = []
20
+ @has_tail = false
21
+ flds.each_with_index do |fld, i|
22
+ if @has_tail
23
+ raise "no fields allowed after tail: #{flds}"
24
+ end
25
+ case fld
26
+ when String, Symbol
27
+ name = fld.to_s
28
+ type = nil
29
+ when Array
30
+ name, type = fld
31
+ when Hash
32
+ name = fld['name'] || fld[:name]
33
+ type = fld['type'] || fld[:type]
34
+ tail = fld['tail'] || fld[:tail]
35
+ end
36
+ name_s = name.to_sym
37
+ field = Field.new(name_s, i, type)
38
+ @field_names[name] = field
39
+ @field_names[name_s] = field
40
+ @field_names[i] = field
41
+ @fields << field
42
+ @has_tail = true if tail
43
+ end
44
+ if @index_defs
45
+ self.indices= @index_defs
46
+ end
47
+ flds
48
+ end
49
+
50
+ def indices=(inds)
51
+ @index_defs = inds
52
+ @index_names = {}
53
+ @indices = []
54
+ @_fields_2_ino = {}
55
+ inds.each do |name, nom, type, parts|
56
+ if @fields && @fields.size > parts.max
57
+ part_names = parts.map{|p| @fields[p].name}
58
+ else
59
+ part_names = []
60
+ end
61
+ index = Index.new(name, nom, type, parts, part_names)
62
+ @index_names[name] = index
63
+ @index_names[name.to_sym] = index
64
+ @index_names[nom] = index
65
+ @indices[nom] = index
66
+ end
67
+ end
68
+
69
+ def indices?
70
+ @indices && !@indices.empty?
71
+ end
72
+
73
+ def get_ino(ino, key, iter, cb)
74
+ if ino.nil?
75
+ unless key.is_a?(Hash)
76
+ opt = Option.error(SchemaError, "Could not detect index without field names and iterator: #{key.inspect} in #{name_sid}")
77
+ return cb.call(opt)
78
+ end
79
+ unless iter.is_a?(Integer)
80
+ iter = ::Tarantool16.iter(iter)
81
+ end
82
+ # key should be Hash here
83
+ keys = key.keys
84
+ _ino = @_fields_2_ino[keys]
85
+ if _ino
86
+ ind = @indices[_ino]
87
+ return yield(_ino, ind.map_key(key))
88
+ elsif _ino == false
89
+ opt = Option.error(SchemaError, "Could not detect index for fields #{key.keys} in #{name_sid}")
90
+ return cb.call(opt)
91
+ end
92
+
93
+ fields = keys.map{|fld|
94
+ case fld
95
+ when Integer
96
+ fld
97
+ when Symbol, String
98
+ @field_names[fld].pos
99
+ else
100
+ return cb.call(Option.error(SchemaError, "Unknown field #{fld.inspect} in query key #{key.inspect}"))
101
+ end
102
+ }
103
+
104
+ index = nil
105
+ for ind in @indices
106
+ next unless ind
107
+ first_fields = ind.parts[0,fields.size]
108
+ if ind.can_iterator?(iter)
109
+ if fields == first_fields
110
+ index = ind
111
+ break
112
+ elsif (fields - first_fields).empty?
113
+ index = ind
114
+ end
115
+ end
116
+ end
117
+ if index
118
+ @_fields_2_ino[keys.freeze] = index.pos
119
+ yield index.pos, index.map_key(key)
120
+ else
121
+ @_fields_2_ino[keys.freeze] = false
122
+ cb.call(Option.error(SchemaError, "Could not detect index for fields #{key.keys} in #{name_sid}"))
123
+ end
124
+ elsif index = @index_names[ino]
125
+ yield index.pos, index.map_key(key)
126
+ else
127
+ cb.call(Option.error(SchemaError, "Could not find index #{ino} for spacefor fields #{key.keys}"))
128
+ end
129
+ end
130
+
131
+ def tuple2hash(ar)
132
+ raise "No fields defined for #{name_sid}" unless @fields && !@fields.empty?
133
+ res = {}
134
+ i = 0
135
+ flds = @fields
136
+ s = flds.size - (@has_tail ? 1 : 0)
137
+ while i < s
138
+ res[flds[i].name] = ar[i]
139
+ i += 1
140
+ end
141
+ if @has_tail
142
+ tail = flds[s]
143
+ unless tail.type.is_a?(Array)
144
+ res[tail.name] = ar[s..-1]
145
+ else
146
+ res[tail.name] = ar[s..-1].each_slice(tail.type.size).to_a
147
+ end
148
+ end
149
+ res
150
+ end
151
+
152
+ def name_sid
153
+ @_np ||= "space #{@name}:#{@sid}"
154
+ end
155
+
156
+ def map_tuple(tuple)
157
+ row = []
158
+ unless @has_tail
159
+ tuple.each_key do |k|
160
+ field = @field_names[k]
161
+ row[field.pos] = tuple[k]
162
+ end
163
+ else
164
+ tail = @fields.last
165
+ tuple.each do |k|
166
+ field = @field_names[k]
167
+ val = tuple[k]
168
+ if field.equal? tail
169
+ unless tail.type.is_a?(Array)
170
+ row[field.pos,0] = val
171
+ else
172
+ row[field.pos,0] = val.flatten(1)
173
+ end
174
+ else
175
+ row[field.pos] = tuple[k]
176
+ end
177
+ end
178
+ end
179
+ row
180
+ end
181
+
182
+ def map_ops(ops)
183
+ ops.map do |op|
184
+ case _1 = op[1]
185
+ when Integer
186
+ op
187
+ when Symbol, String
188
+ _op = op.dup
189
+ _op[1] = @field_names[_1].pos
190
+ _op
191
+ when Array
192
+ _1.dup.insert(1, @field_names[op[0]].pos)
193
+ end
194
+ end
195
+ end
196
+
197
+ def wrap_cb(cb)
198
+ CallbackWrapper.new(self, cb)
199
+ end
200
+
201
+ class Field
202
+ attr :name, :pos, :type
203
+ def initialize(name, pos, type)
204
+ @name = name
205
+ @pos = pos
206
+ @type = type
207
+ end
208
+
209
+ def to_s
210
+ "<Fields #{@name}@#{pos}>"
211
+ end
212
+ end
213
+
214
+ class Index
215
+ attr :name, :pos, :parts, :type, :part_names, :part_positions
216
+ ITERS = {
217
+ tree: (ITERATOR_EQ..ITERATOR_GT).freeze,
218
+ hash: [ITERATOR_ALL, ITERATOR_EQ, ITERATOR_GT].freeze,
219
+ bitset: [ITERATOR_ALL, ITERATOR_EQ, ITERATOR_BITS_ALL_SET,
220
+ ITERATOR_BITS_ANY_SET, ITERATOR_BITS_ALL_NOT_SET].freeze,
221
+ rtree: [ITERATOR_ALL, ITERATOR_EQ, ITERATOR_GT, ITERATOR_GE, ITERATOR_LT, ITERATOR_LE,
222
+ ITERATOR_RTREE_OVERLAPS, ITERATOR_RTREE_NEIGHBOR].freeze
223
+ }
224
+ def initialize(name, pos, type, parts, part_names)
225
+ @name = name
226
+ @pos = pos
227
+ @type = type.downcase.to_sym
228
+ @iters = ITERS[@type] or raise "Unknown index type #{type.inspect}"
229
+ @parts = parts
230
+ @part_names = part_names
231
+ @part_positions = {}
232
+ parts.each_with_index{|p, i| @part_positions[p] = i}
233
+ part_names.each_with_index{|p, i|
234
+ @part_positions[p.to_s] = i
235
+ @part_positions[p.to_sym] = i
236
+ }
237
+ end
238
+
239
+ def can_iterator?(iter)
240
+ @iters.include?(iter)
241
+ end
242
+
243
+ def map_key(key)
244
+ return key if key.is_a?(Array)
245
+ res = []
246
+ positions = @part_positions
247
+ key.each_key do |k|
248
+ res[positions[k]] = key[k]
249
+ end
250
+ res
251
+ end
252
+ end
253
+
254
+ class CallbackWrapper
255
+ def initialize(space, cb)
256
+ @space = space
257
+ @cb = cb
258
+ end
259
+
260
+ def call(r)
261
+ if r.ok?
262
+ sp = @space
263
+ r = Option.ok(r.data.map{|row| sp.tuple2hash(row)})
264
+ end
265
+ @cb.call r
266
+ end
267
+ end
268
+ end
269
+ end
@@ -0,0 +1,3 @@
1
+ module Tarantool16
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,17 @@
1
+ require "tarantool16/version"
2
+ require "tarantool16/db"
3
+
4
+ module Tarantool16
5
+ autoload :DumbDB, 'tarantool16/dumb_db'
6
+ def self.new(opts = {})
7
+ opts = opts.dup
8
+ hosts = opts[:host]
9
+ type = opts[:type] && opts[:type].to_s || 'dumb'
10
+ case type
11
+ when 'dumb'
12
+ DumbDB.new hosts, opts
13
+ else
14
+ raise "Unknown DB type"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'tarantool16/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "tarantool16"
8
+ spec.version = Tarantool16::VERSION
9
+ spec.authors = ["Sokolov Yura aka funny_falcon"]
10
+ spec.email = ["funny.falcon@gmail.com"]
11
+ spec.summary = %q{adapter for Tarantool 1.6}
12
+ spec.description = %q{adapter for Tarantool 1.6}
13
+ spec.homepage = "https://github.com/funny-falcon/tarantool16-ruby"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+
24
+ spec.add_dependency "msgpack"
25
+ end