tarantool16 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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